`
yjkun
  • 浏览: 13511 次
  • 性别: Icon_minigender_1
  • 来自: 河北
文章分类
社区版块
存档分类
最新评论

Spring AOP的应用 收藏

 
阅读更多

在实际的应用程序开发中,经常需要在一个服务流程中插入一些与业务逻辑无关的系统服务逻辑(最常见的就是记录日志,权限检查等),如果把所有这些与业务逻辑无关的服务与业务逻辑编织在一起,就会使业务逻辑对象的负担加重,因为它不但要具有业务逻辑的功能,还带有例如记录日志等其他功能,这样就容易产生对象的职责混淆。
为了避免对象职责的混淆,我们在设计中就需要将与业务逻辑无关的服务逻辑从业务逻辑中剥离出来,独立设计为一个模块或对象,而在希望需要使用这些对象的时候插入进来,不希望使用的时候去掉即可,这种设计模式就称为AOP。
Spring AOP是实现AOP的一种技术。Spring AOP最常用的就是采用XML配置文件的方式。Spring AOP只支持在目标对象的某个方法的前后调用服务代码。
下面是使用Spring AOP的几个简单的例子:
1.在目标对象的方法执行之前调用(Before Adivce)
首先需要定义目标对象必须实现的接口:
package com.test.spring.aop;

public interface IHello {

public void sayHello(String name);
}
接着定义一个IHello的实现类:
package com.test.spring.aop;

public class HelloSpeaker implements IHello {

public void sayHello(String name) {
System.out.println("Hello," + name);
}
}
现在我们想要在调用对象HelloSpeaker的sayHello()方法前打印一下方法开始执行的日志,一般情况下我们会直接在sayHello()的方法中直接加上日志的代码,这样就把日志和业务逻辑的代码融在了一起,对于要记录大量日志的程序来说,无疑会加重目标对象的负担。
我们把打印日志的代码独立出来成为一个专门用于日志记录的对象,这里需要实现Spring的MethodBeforeAdvice接口:
package com.test.spring.aop;

import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.aop.MethodBeforeAdvice;

public class LogBeforeAdvice implements MethodBeforeAdvice {

private Logger logger = Logger.getLogger(this.getClass().getName());

public void before(Method method, Object[] args, Object target) throws Throwable {
logger.log(Level.INFO, "method start..." + method);
System.out.println("LogBeforeAdvice: method start... " + method);
}
}
在before()方法的实现中,加入了一些记录日志的代码,LogBeforeAdive被设计成一个独立的服务,可以提供给需要这个服务的对象,在下面的配置中,我们把这个服务提供给了前面的HelloSpeaker对象:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="logBeforeAdvice" class="com.test.spring.aop.LogBeforeAdvice"></bean>

<bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>

<bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.test.spring.aop.IHello"></property>
<property name="target" ref="helloSpeaker"></property>
<property name="interceptorNames">
<list>
<value>logBeforeAdvice</value>
</list>
</property>
</bean>
</beans>
在上面的配置文件中,属性target指定了要被服务的目标对象为helloSpeaker,属性interceptorNames指定了使用的服务对象是logBeforeAdvice。这样就建立了目标对象与服务对象之间的联系,下面是测试代码:
package com.test.spring.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeforeAdviceTest {

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"com/test/spring/aop/beforeAfterAdvice.xml");

IHello helloProxy = (IHello) context.getBean("helloProxy");
helloProxy.sayHello("world");
}
}
可以看到,测试代码中,取的对象是helloProxy代理对象,而不是helloSpeaker目标对象,取到的代理对象必须转换为IHello接口,这样当调用代理对象的sayHello()方法时,就可以在调用目标对象的sayHello()方法体中的代码前打印日志,实际上流程是先进入LogBeforeAdvice对象执行before()方法,执行完毕后再进入helloSpeaker的sayHello()方法,这一切的流程控制都是有Spring完成的,而我们仅仅需要做的工作就是定义目标对象和服务对象,并编写XML配置文件建立服务对象与目标对象的联系。动手试一下吧,看看执行结果再说。
2.在目标对象的方法执行之后调用(After Advice)
这里就不在定义目标对象的接口及实现类了,依然使用前面定义好的目标对象helloSpeaker。
要在目标对象的方法执行之后进行一些操作,这里依然是打印日志,Spring AOP要求我们实现AfterReturningAdvice接口:
package com.test.spring.aop;

import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.aop.AfterReturningAdvice;

public class LogAfterAdvice implements AfterReturningAdvice {

private Logger logger = Logger.getLogger(this.getClass().getName());

public void afterReturning(Object object, Method method, Object[] args, Object target)
throws Throwable {
logger.log(Level.INFO, "method end... " + method);
System.out.println("method end... " + method);
}
}
可以看到,这个服务对象的目的是在执行目标对象的方法之后执行afterReturning()方法打印方法执行完毕的日志。
下一步就是配置XML文件了,把这个服务提供给前面helloSpeaker目标对象:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="logAfterAdvice" class="com.test.spring.aop.LogAfterAdvice"></bean>

<bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>

<bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.test.spring.aop.IHello"></property>
<property name="target" ref="helloSpeaker"></property>
<property name="interceptorNames">
<list>
<value>logAfterAdvice</value>
</list>
</property>
</bean>
</beans>
可以看到,与前面的配置Before Advice非常相似。
测试代码与Before Advice的完全一样,这里不再重复列出。动手试一下,看看是否在执行目标对象的sayHello()方法之后调用了打印结束日志。
当然,还可以把Before Advice和After Advice一起提供给目标对象,这样,就可以在执行目标对象的方法的前、后都打印出日志了,配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="logBeforeAdvice" class="com.test.spring.aop.LogBeforeAdvice"></bean>

<bean id="logAfterAdvice" class="com.test.spring.aop.LogAfterAdvice"></bean>

<bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>

<bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.test.spring.aop.IHello"></property>
<property name="target" ref="helloSpeaker"></property>
<property name="interceptorNames">
<list>
<value>logBeforeAdvice</value>
<value>logAfterAdvice</value>
</list>
</property>
</bean>
</beans>
可以看到,在interceptorNames属性中,把logBeforeAdvice和logAfterAdvice两个服务对象都提供给了目标对象,实际上我们可以配置任意多个。
测试代码依然与前面的一样,不再列出,执行一下,看看结果是否是在执行sayHello()方法之前和之后分别打印了日志。
3.在目标对象的方法执行前后调用(Around Advice)
在前面的两种模式中,有一个缺陷,就是无论如何,目标对象的方法总是会被执行,我们不能控制目标对象的方法是否执行,且执行的时机。
有时候我们需要自行决定是否要执行目标对象的方法,有时需要在不同的时机才执行目标对象的方法,比如权限检查,当权限检查通过时,才继续执行目标对象的方法,如果权限检查不通过,则进入别的流程中。前面的两种模式是做不到这一点的。
在Around Advice这种模式中,我们就可以自行决定是否执行目标对象的方法及其执行的时机了。
这种模式需要实现MethodInterceptor接口:
package com.test.spring.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class LogInterceptor implements MethodInterceptor {

public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Log start...");
Object result = null;
result = methodInvocation.proceed();
System.out.println("Log end...");
return result;
}
}
这个服务实现的方法是invoke()方法,在这个方法中,调用了proceed()方法,当调用了这个方法时,就表示需要执行目标对象的方法,下面是XML配置文件的内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="logInterceptor" class="com.test.spring.aop.LogInterceptor"></bean>

<bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>

<bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.test.spring.aop.IHello"></property>
<property name="target" ref="helloSpeaker"></property>
<property name="interceptorNames">
<list>
<value>logInterceptor</value>
</list>
</property>
</bean>
</beans>
如上例中,我们在调用proceed()方法的前后打印了日志,则表示在执行helloSpeaker目标对象的sayHello()方法的前后都打印日志。
可以看到,关键就在于proceed()方法的调用,因此,在这种模式中,我们可以在调用proceed()方法的前、后的任何地方插入任何处理逻辑,当然,我们也可以不调用proceed()方法,这样,目标对象的方法就不能执行了。
测试代码与前面的完全一样,这里不再列出。
对于前面的三种方式,我们可以看出,这几种方式都需要在使用时建立代理对象,然后获取代理对象并把类型转换为目标对象的接口,这需要我们手动创建代理对象,即每一个目标对象都要建立一个相应的代理对象,对于有很多目标对象的应用程序,配置XML文件无疑会是一件痛苦的事。
Spring AOP框架中提供了一种自动创建代理的工具。
4.自动代理(BeanNameAutoProxyCreator)
要使用Spring AOP的自动代理,需要使用org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator类来完成。
这里,目标对象依然使用前面的helloSpeaker,服务对象依然使用前面的LogInterceptor,代码不再列出,下面是配置自动代理的XML文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="logInterceptor" class="com.test.spring.aop.LogInterceptor"></bean>

<bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>helloSpeaker</value>
</list>
</property>
<property name="interceptorNames" value="logInterceptor"></property>
</bean>
</beans>
在上面的配置中,指定了org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator类的两个属性,beanNames和interceptorNames,beanNames指定了要进行自动代理的目标对象,interceptorNames与前面的几种模式中的这个属性含义相同,指定了提供服务的对象。
测试代码为:
package com.test.spring.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeforeAdviceTest {

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"com/test/spring/aop/beforeAfterAdvice.xml");
IHello helloSpeaker = (IHello) context.getBean("helloSpeaker");
helloSpeaker.sayHello("longyg");
}
}
看,这次我们不再是获取的代理对象,而是直接获得helloSpeaker目标对象了。由于我们在XML配置文件中指定了为helloSpeaker目标对象进行自动代理,因此当我们获取目标对象时,Spring会自动为其创建代理对象,并根据配置的服务对象提供相应的服务(logInterceptor)。
基于这种方式,我们可以把一个服务对象用于多个目标对象,下面我另外定义了一个helloSpeaker2目标对象,它也实现了IHello接口(当然我们也可以实现任务 其他接口):
package com.test.spring.aop;

public class HelloSpeaker2 implements IHello {

public void sayHello(String name) {
System.out.println("Welcome to world," + name);
}
}
下面在XML文件中增加对helloSpeaker2目标对象提供服务:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="logInterceptor" class="com.test.spring.aop.LogInterceptor"></bean>

<bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>

<bean id="helloSpeaker2" class="com.test.spring.aop.HelloSpeaker2"></bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>helloSpeaker</value>
<value>helloSpeaker2</value>
</list>
</property>
<property name="interceptorNames" value="logInterceptor"></property>
</bean>
</beans>
在beanNames属性中增加了helloSpeaker2目标对象,因此logInterceptor也对它提供服务。我们还可以增加任务多的目标对象。
同样,我们也可以为目标对象提供多个服务对象,下面我又定义了一个agumentInterceptor服务对象:
package com.test.spring.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class AgumentInterceptor implements MethodInterceptor {

public Object invoke(MethodInvocation methodInvocation) throws Throwable {
String name = null;
Object[] args = methodInvocation.getArguments();
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof String) {
name = (String) args[i];
}
}
System.out.println("the argument is : " + name);
Object result = methodInvocation.proceed();
System.out.println("the argument is proceed");
return result;
}
}
下面在XML文件中增加agumentInterceptor服务对象:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="logInterceptor" class="com.test.spring.aop.LogInterceptor"></bean>

<bean id="agumentInterceptor" class="com.test.spring.aop.AgumentInterceptor"></bean>

<bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>

<bean id="helloSpeaker2" class="com.test.spring.aop.HelloSpeaker2"></bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>helloSpeaker</value>
<value>helloSpeaker2</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>logInterceptor</value>
<value>agumentInterceptor</value>
</list>
</property>
</bean>
</beans>
在interceptorNames属性中增加了agumentInterceptor服务对象,这样,agumentInterceptor服务就可以在目标对象上被应用了。
5.结束语
以上就是Spring AOP提供的几种比较简单且常用的模式,当然,Spring还提供了很多其他的方式,这里不再一一列出。一般情况下,自动代理的模式是我们经常使用的,它的配置很灵活,我认为初学只要能掌握并使用这种模式就可以了。

本文来自CSDN博客,转载请标明出处:

http://blog.csdn.net/ganglong99/archive/2009/02/27/3942726.aspx

分享到:
评论

相关推荐

    晾晾多年珍藏spring开发指南.pdf

    Spring是一个开源的Java平台,它简化了企业级应用的开发,通过提供依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)等核心特性,实现了代码的解耦和模块化。 2. **依赖...

    Spring技术内幕:深入解析Spring架构与设计原理

     spring如何在web环境中集成ioc容器并为web应用开发提供利器?  我们耳熟能详的mvc模式在spring中是如何实现的?  spring mvc如何灵活地集成各种丰富的视图展现方案?  spring实现远端调用的方案有很多种,你...

    Spring框架参考文档(中文版PDF)

    Spring框架是中国广大Java开发者广泛应用的开源框架,它以其强大的功能和灵活的设计,成为了构建企业级应用的首选。这个“Spring框架参考文档...这份文档覆盖了Spring的方方面面,值得每一个Java开发者收藏和研读。

    Spring+SpringMVC+Mybatis框架整合例子(SSM) 下载

    Spring还支持AOP,允许我们编写关注点分离的代码,如日志、事务管理等。 SpringMVC是Spring框架的一部分,专门用于构建Web应用的MVC框架。它负责处理HTTP请求,将请求分发到对应的控制器,控制器处理业务逻辑后,...

    基于Spring Boot框架的帖子管理系统.zip

    本项目是一个基于Spring Boot框架的帖子管理系统,旨在提供一个高效、易用的后端服务,支持用户发布、管理、收藏和点赞帖子。项目整合了常用框架和主流业务的示例代码,只需1分钟即可完成内容网站的后端搭建。 ...

    Spring5 源码分析2020.zip

    Spring作为Java企业级应用中最广泛应用的框架之一,其强大之处在于它的依赖注入(Dependency Injection, DI)和面向切面编程(Aspect-Oriented Programming, AOP)等核心特性。通过深入学习Spring的源码,我们可以...

    Spring核心知识点完整梳理(史上最全,值得收藏)

    Spring框架是Java开发中最常用的轻量级框架之一,它以其依赖注入(Dependency Injection,简称DI)和面向切面编程(Aspect-Oriented Programming,简称AOP)为核心特性,极大地简化了企业级应用的开发工作。...

    Struts,Hibernate,Spring经典面试题收藏

    总的来说,Struts、Hibernate和Spring的结合使用,能够帮助开发者构建出高度模块化、易于维护的J2EE应用程序,提高开发效率,降低维护成本。这三大框架的经典面试题涵盖了它们的核心概念、工作原理、优化技巧以及...

    spring-tool-suite-4-4.0.0.RELEASE-e4.9.0-win32.win32.x86_64.zip收藏版

    "Spring Tool Suite (STS) 4 4.0.0.RELEASE-e4.9.0-win32.win32.x86_64.zip" 是一个专为Windows x86_64架构设计的集成开发环境(IDE)的压缩包,特别收藏版,集成了Eclipse 4.9版本。这款工具主要面向Java开发者,...

    spring4.2.5最新文档-英文

    Spring框架是Java开发中不可或缺的一部分,它以其模块化、易用性和灵活性著称。Spring 4.2.5是该框架的一个稳定版本,为开发者提供了丰富的...这份文档是学习和参考Spring框架的宝贵资源,值得每一个Java开发者收藏。

    Struts Spring ajax收藏的几本书

    2. **Spring框架**: Spring是另一个非常流行的Java企业级应用框架,它不仅提供了IOC(Inversion of Control)和AOP(Aspect Oriented Programming)两大核心特性,还支持数据库操作、事务管理、MVC、WebSocket等多种...

    spring2.5.4+hibernate3.2.6+struts2+jbpm3.2.2收藏

    Spring 是一个全面的Java应用框架,提供依赖注入(DI)和面向切面编程(AOP)等功能。在配置中,`context-param`用于指定Spring配置文件的位置,例如`applicationContext-resources.xml`、`applicationContext-dao....

    (源码)基于SpringBoot和MyBatisPlus的帖子管理系统.zip

    系统整合了多种主流框架和技术,包括Spring MVC、MyBatis Plus、Redis、Elasticsearch等,支持用户登录、帖子发布、点赞、收藏、搜索等功能。 ## 项目的主要特性和功能 ### 主流框架 特性 Spring Boot 2.7.x快速...

    精品专题(2021-2022年收藏)java框架Spring2复习题.docx

    * AOP 的应用场景:安全性、日志记录、性能监控、缓存管理等。 * Spring 框架的依赖注入方式的优点:减少耦合性、提高可维护性和可扩展性。 * 事务隔离级别的选择:根据实际情况选择合适的隔离级别,以保证数据的...

    spring中文API 3.x 2.x版本

    Spring框架是Java开发中广泛应用的一个开源框架,以其强大的依赖注入(Dependency Injection,简称DI)和面向切面编程(Aspect-Oriented Programming,简称AOP)功能而著名。它简化了企业级应用的开发,提供了诸如...

    springShiro.rar

    而Spring是一个全面的企业应用开发框架,它在Java领域中扮演着核心角色,提供了依赖注入、AOP(面向切面编程)、MVC(模型-视图-控制器)等特性。将Spring与Shiro结合使用,可以构建出高效且安全的Web应用。 描述 ...

    EasyUI+MVC4.0+EF5.0+Spring.NET框架源码(精心收藏)

    EasyUI+MVC4.0+EF5.0+Spring.NET框架源码是一个全面的开发套件,结合了多种技术,旨在提供高效、稳定的Web应用解决方案。这些技术各自扮演着关键角色,共同构建了一个强大的开发环境。让我们逐一深入探讨这些技术。 ...

    使用 Spring Boot+SSM 实现的社区论坛项目源码.zip

    Spring是Java企业级应用开发的核心框架,提供了一种依赖注入(DI)和面向切面编程(AOP)的解决方案。在SSM架构中,Spring主要负责管理各个组件的生命周期,处理业务逻辑和服务之间的依赖关系。 3. **Spring MVC**...

Global site tag (gtag.js) - Google Analytics