`
huxiaojun_198213
  • 浏览: 101198 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

转载:Spring AOP应用

阅读更多
[color=indigo]在实际的应用程序开发中,经常需要在一个服务流程中插入一些与业务逻辑无关的系统服务逻辑(最常见的就是记录日志,权限检查等),如果把所有这些与业务逻辑无关的服务与业务逻辑编织在一起,就会使业务逻辑对象的负担加重,因为它不但要具有业务逻辑的功能,还带有例如记录日志等其他功能,这样就容易产生对象的职责混淆。
为了避免对象职责的混淆,我们在设计中就需要将与业务逻辑无关的服务逻辑从业务逻辑中剥离出来,独立设计为一个模块或对象,而在希望需要使用这些对象的时候插入进来,不希望使用的时候去掉即可,这种设计模式就称为AOP。
Spring AOP是实现AOP的一种技术。Spring AOP最常用的就是采用XML配置文件的方式。Spring AOP只支持在目标对象的某个方法的前后调用服务代码。

在目标对象的方法执行之前调用(Before Adivce)

首先需要定义目标对象必须实现的接口:
IHello.java 

package com.test.spring.aop;  

public interface IHello {  

  public void sayHello(String name);  

}


接着定义一个IHello的实现类:

HelloSpeaker.java 

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接口:
LogBeforeAdvice.java 

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配置文件建立服务对象与目标对象的联系。动手试一下吧,看看执行结果再说。

在目标对象的方法执行之后调用(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()方法之前和之后分别打印了日志。



在目标对象的方法执行前后调用(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框架中提供了一种自动创建代理的工具。



自动代理(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还提供了很多其他的方式,这里不再一一列出。一般情况下,自动代理的模式是我们经常使用的,它的配置很灵活,我认为初学只要能掌握并使用这种模式就可以了。

本文来自:http://blog.csdn.net/ganglong99/article/details/3942726[/color]
分享到:
评论

相关推荐

    Ehcache集成Spring的使用(转载)

    这篇博客将深入探讨如何将 Ehcache 集成到 Spring 应用中,以及如何使用 Spring AOP 实现计算结果的缓存。 首先,集成 Ehcache 到 Spring 需要以下步骤: 1. **引入依赖**: 在 Maven 或 Gradle 的配置文件中添加 ...

    (转载)自己动手写一个spring

    Spring是Java开发中最流行的框架之一,它以依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)为核心,极大地简化了企业级应用的开发。 首先,我们需要理解什么是依赖注入。...

    转载:cglib动态代理介绍(一)

    这个库在很多场合被广泛使用,特别是在那些需要在运行时动态创建对象或增强已有对象功能的场景下,比如Spring AOP(面向切面编程)就是其中的一个典型应用。下面我们将深入探讨CGlib动态代理的相关知识点。 ### ...

    不需要应用服务器的J2EE【转载】

    它使用Spring的AOP(面向切面编程)和IoC(Inversion of Control,控制反转)特性,将业务逻辑和事务管理从基础设施代码中解耦出来。 5. **Spring框架的角色**:Spring框架在此起到了关键作用,它提供了POJO(Plain...

    jsr168 portlet(struts2+spring2.5+hibernate3.3)(转载)

    【Spring框架】Spring2.5作为依赖注入(DI)和面向切面编程(AOP)的容器,管理着应用程序中的对象及其相互依赖关系。它简化了配置,支持多种数据访问技术,包括JDBC、Hibernate等,并提供了事务管理、安全管理等...

    Spring-Security-3应用的11个步骤.docx

    Spring Security 是一个强大的和高度可定制的身份验证和访问控制框架,用于保护基于 Java 的应用程序,尤其是J2EE企业级应用。它起源于2003年的Acegi Security,现在是Spring生态系统的组成部分,最新的版本是3.x。...

    springmybatis

    mybatis实战教程mybatis in action之五与spring3集成附源码 mybatis实战教程mybatis in action之六与Spring MVC 的集成 mybatis实战教程mybatis in action之七实现mybatis分页源码下载 mybatis实战教程mybatis in ...

    本项目是基于SpringBoot的线上宠物物资购买系统,为个人毕业设计,未经允许禁止转载.zip

    1. **Spring Framework**: 提供了依赖注入、AOP(面向切面编程)、事务管理等核心功能。 2. **Spring Boot Auto Configuration**: 根据类路径中的jar和存在的bean自动配置应用。 3. **Spring MVC**: 实现了Model-...

    SSM整合,参考尚硅谷视频,转载笔记,非原创,侵权删

    Spring是Java企业级应用的核心框架,它提供了一个全面的编程和配置模型,用于简化企业级应用的开发。Spring的核心特性包括依赖注入(DI)和面向切面编程(AOP)。DI允许开发者在运行时动态地管理对象及其依赖关系,...

    arthas命令总结(转载)

    Arthas支持Spring Boot、Spring Cloud等框架,使得开发者能够方便地对运行中的Java应用程序进行调试、监控和分析。 一、Arthas安装与启动 首先,你需要在官网或者通过GitHub下载Arthas的最新版本,然后解压到本地...

    华为面试试题,很经典,转载

    【Spring】Spring框架是企业级应用开发的基石,面试可能围绕IoC(Inversion of Control)和AOP(Aspect-Oriented Programming)展开,包括Bean管理、依赖注入、事务处理、Spring MVC、Spring Boot、Spring Data JPA...

    j2ee在线购物网实例源码

    3. **Spring框架**:Spring提供了依赖注入(DI)和面向切面编程(AOP),简化了业务组件的管理。 4. **Hibernate**:作为ORM(对象关系映射)工具,用于数据库操作,将Java对象与SQL数据库表进行映射。 5. **EJB...

    Java 最常见 200+ 面试题全解析:面试必备.pdf

    10. Spring/SpringMVC:介绍Spring框架的核心特性,包括IoC容器、AOP、事务管理、Spring MVC框架等。 11. SpringBoot/SpringCloud:SpringBoot简化了Spring应用的配置和部署,而SpringCloud则是云环境下微服务架构...

    Java面试题

    11. **Spring框架**:理解依赖注入(DI)和面向切面编程(AOP)的概念,熟悉Spring Boot和Spring Cloud的相关知识。 12. **其他**:可能还会涉及一些其他技术,如微服务、分布式系统、大数据处理、并发编程模型等,...

    Java面试资料大集合

    - **Spring框架**:依赖注入(DI)、AOP、事务管理等核心概念。 - **MyBatis**:持久层框架,动态SQL和映射文件的使用。 - **Maven或Gradle**:构建工具的使用和配置。 9. **数据库** - **SQL语句**:增删改查...

Global site tag (gtag.js) - Google Analytics