`
aijuans
  • 浏览: 1558135 次
社区版块
存档分类
最新评论

Spring中的AOP

阅读更多

 

Spring中的AOP

Written by Tony Jiang @ 2012-1-18

(转)何为AOP

AOP,面向切面编程。

在不改动代码的前提下,灵活的在现有代码的执行顺序前后,添加进新规机能。

来一个简单的Sample:

目标类:

  1. package com.hyron.tony;  
  2.   
  3. public class CustomerService {  
  4.     private String name;  
  5.     private String url;  
  6.   
  7.     public void setName(String name) {  
  8.         this.name = name;  
  9.     }  
  10.   
  11.     public void setUrl(String url) {  
  12.         this.url = url;  
  13.     }  
  14.   
  15.     public void printName() {  
  16.         System.out.println("Customer name : " + this.name);  
  17.     }  
  18.   
  19.     public void printURL() {  
  20.         System.out.println("Customer website : " + this.url);  
  21.     }  
  22.   
  23.     public void printThrowException() {  
  24.         throw new IllegalArgumentException();  
  25.     }  
  26.   
  27. }  


advice:只以Around advice为例

  1. import java.util.Arrays;  
  2.   
  3. import org.aopalliance.intercept.MethodInterceptor;  
  4. import org.aopalliance.intercept.MethodInvocation;  
  5.   
  6. public class HijackAroundMethod implements MethodInterceptor {  
  7.     @Override  
  8.     public Object invoke(MethodInvocation methodInvocation) throws Throwable {  
  9.   
  10.         System.out.println("Method name : "  
  11.                 + methodInvocation.getMethod().getName());  
  12.         System.out.println("Method arguments : "  
  13.                 + Arrays.toString(methodInvocation.getArguments()));  
  14.   
  15.         // same with MethodBeforeAdvice  
  16.         System.out.println("HijackAroundMethod : Before method hijacked!");  
  17.   
  18.         try {  
  19.             // proceed to original method call  
  20.             Object result = methodInvocation.proceed();  
  21.   
  22.             // same with AfterReturningAdvice  
  23.             System.out.println("HijackAroundMethod : Before after hijacked!");  
  24.   
  25.             return result;  
  26.   
  27.         } catch (IllegalArgumentException e) {  
  28.             // same with ThrowsAdvice  
  29.             System.out  
  30.                     .println("HijackAroundMethod : Throw exception hijacked!");  
  31.             throw e;  
  32.         }  
  33.     }  
  34. }  

编织切入关系的配置文件:

  1. <bean id="customerService" class="com.mkyong.customer.services.CustomerService">  
  2.         <property name="name" value="Yong Mook Kim" />  
  3.         <property name="url" value="http://www.mkyong.com" />  
  4.     </bean>  
  5.   
  6.     <bean id="hijackAroundMethodBean" class="com.mkyong.aop.HijackAroundMethod" />  
  7.   
  8.     <bean id="customerServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
  9.   
  10.         <property name="target" ref="customerService" />  
  11.   
  12.         <property name="interceptorNames">  
  13.             <list>  
  14.                 <value>hijackAroundMethodBean</value>  
  15.             </list>  
  16.         </property>  
  17.     </bean>  

 

Sample的启动:

  1. import org.springframework.context.ApplicationContext;  
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  3.   
  4. import com.mkyong.customer.services.CustomerService;  
  5.   
  6. public class App {  
  7.     public static void main(String[] args) {  
  8.         ApplicationContext appContext = new ClassPathXmlApplicationContext(  
  9.                 new String[] { "Spring-Customer.xml" });  
  10.   
  11.         CustomerService cust = (CustomerService) appContext  
  12.                 .getBean("customerServiceProxy");  
  13.   
  14.         System.out.println("*************************");  
  15.         cust.printName();  
  16.         System.out.println("*************************");  
  17.         cust.printURL();  
  18.         System.out.println("*************************");  
  19.         try {  
  20.             cust.printThrowException();  
  21.         } catch (Exception e) {  
  22.   
  23.         }  
  24.   
  25.     }  
  26. }  

 

以上代码,用customerServiceProxy代理CustomerService的执行

在customerServiceProxy的配置中,定义了用hijackAroundMethodBean作为方法拦截器,在hijackAroundMethodBean中利用invoke方法,拦截住所有的方法调用,塞入自己的逻辑业务。

AOP的两种实现

                上面看到的是Spring的Sample。

                其实,Spring的AOP也是调用了其他开源技术实现。

                比较常用的是JDK自己的Proxy,和开源的CGLIB

                两者的区别,Proxy需要Advice必须从接口继承过来。如果切入的目标物是实体类,则无法使用。

                CGLIB则可以用于直接覆盖实体类的方法。

                Spring对以上两种都有支持。

Spring的底层实现

                Spring在配置文件中,通过ProxyFactoryBean编织和实现了切面的构成。

                我们在执行以下这行话的时候

CustomerService cust = (CustomerService) appContext

                                                                .getBean("customerServiceProxy");

 

                其实是将动态对象的生成委托给了ProxyFactoryBean

当配置文件中 <bean>的class属性配置的实现类是FactoryBean时,通过getBean方法返回的不是FactoryBean本身,而是 FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方 法。

 

执行顺序如下:

1.  ProxyFactoryBean中的getObject

  1. /** 
  2.      * Return a proxy. Invoked when clients obtain beans from this factory bean. 
  3.      * Create an instance of the AOP proxy to be returned by this factory. 
  4.      * The instance will be cached for a singleton, and create on each call to 
  5.      * <code>getObject()</code> for a proxy. 
  6.      * @return a fresh AOP proxy reflecting the current state of this factory 
  7.      */  
  8.     public Object getObject() throws BeansException {  
  9.         initializeAdvisorChain();  
  10.         if (isSingleton()) {  
  11.             return getSingletonInstance();  
  12.         }  
  13.         else {  
  14.             if (this.targetName == null) {  
  15.                 logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +  
  16.                         "Enable prototype proxies by setting the 'targetName' property.");  
  17.             }  
  18.             return newPrototypeInstance();  
  19.         }  
  20.     }  

 

2.  ProxyFactoryBean中的initializeAdvisorChain

从配置文件中的advice list中取得interceptorNames,并将其加入advisorChain

  1. for (String name : this.interceptorNames) {  
  2.                 if (logger.isTraceEnabled()) {  
  3.                     logger.trace("Configuring advisor or advice '" + name + "'");  
  4.                 }  
  5.   
  6.                 if (name.endsWith(GLOBAL_SUFFIX)) {  
  7.                     if (!(this.beanFactory instanceof ListableBeanFactory)) {  
  8.                         throw new AopConfigException(  
  9.                                 "Can only use global advisors or interceptors with a ListableBeanFactory");  
  10.                     }  
  11.                     addGlobalAdvisor((ListableBeanFactory) this.beanFactory,  
  12.                             name.substring(0, name.length() - GLOBAL_SUFFIX.length()));  
  13.                 }  

 

3.  被切面改造过的instance的生成

我们以单例举例:

  1. /** 
  2.      * Return the singleton instance of this class's proxy object, 
  3.      * lazily creating it if it hasn't been created already. 
  4.      * @return the shared singleton proxy 
  5.      */  
  6.     private synchronized Object getSingletonInstance() {  
  7.         if (this.singletonInstance == null) {  
  8.             this.targetSource = freshTargetSource();  
  9.             if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {  
  10.                 // Rely on AOP infrastructure to tell us what interfaces to proxy.  
  11.                 Class targetClass = getTargetClass();  
  12.                 if (targetClass == null) {  
  13.                     throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");  
  14.                 }  
  15.                 setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));  
  16.             }  
  17.             // Initialize the shared singleton instance.  
  18.             super.setFrozen(this.freezeProxy);  
  19.             this.singletonInstance = getProxy(createAopProxy());  
  20.         }  
  21.         return this.singletonInstance;  
  22.     }  

 

AopProxy是最终生成instance的地方,但是它是接口,和框架分离开来了

接口的生成在父类的ProxyCreatorSupport中

  1. /** 
  2.      * Subclasses should call this to get a new AOP proxy. They should <b>not</b> 
  3.      * create an AOP proxy with <code>this</code> as an argument. 
  4.      */  
  5.     protected final synchronized AopProxy createAopProxy() {  
  6.         if (!this.active) {  
  7.             activate();  
  8.         }  
  9.         return getAopProxyFactory().createAopProxy(this);  
  10.     }  
  11. createAopProxy的实际运行代码:  
  12. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  
  13.         if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {  
  14.             Class targetClass = config.getTargetClass();  
  15.             if (targetClass == null) {  
  16.                 throw new AopConfigException("TargetSource cannot determine target class: " +  
  17.                         "Either an interface or a target is required for proxy creation.");  
  18.             }  
  19.             if (targetClass.isInterface()) {  
  20.                 return new JdkDynamicAopProxy(config);  
  21.             }  
  22.             if (!cglibAvailable) {  
  23.                 throw new AopConfigException(  
  24.                         "Cannot proxy target class because CGLIB2 is not available. " +  
  25.                         "Add CGLIB to the class path or specify proxy interfaces.");  
  26.             }  
  27.             return CglibProxyFactory.createCglibProxy(config);  
  28.         }  
  29.         else {  
  30.             return new JdkDynamicAopProxy(config);  
  31.         }  
  32.     }  

 

大家注意一下,这里根据targetClass的类型判断,采用JDK Proxy还是CGLIB模式生成动态对象

 

4.  JDK Proxy的生成

如果大家使用过原生的JDK PROXY,下面的代码是在熟悉不过了

JdkDynamicAopProxy中实例化instance的

  1. public Object getProxy(ClassLoader classLoader) {  
  2.         if (logger.isDebugEnabled()) {  
  3.             logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());  
  4.         }  
  5.         Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);  
  6.         findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  
  7.         return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  
  8.     }  

 

JdkDynamicAopProxy中的invoke方法

  1. // Get the interception chain for this method.  
  2.             List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);  
  3. // Check whether we have any advice. If we don't, we can fallback on direct  
  4.             // reflective invocation of the target, and avoid creating a MethodInvocation.  
  5.             if (chain.isEmpty()) {  
  6.                 // We can skip creating a MethodInvocation: just invoke the target directly  
  7.                 // Note that the final invoker must be an InvokerInterceptor so we know it does  
  8.                 // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.  
  9.                 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);  
  10.             }  
  11.             else {  
  12.                 // We need to create a method invocation...  
  13.                 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
  14.                 // Proceed to the joinpoint through the interceptor chain.  
  15.                 retVal = invocation.proceed();  
  16.             }  

 

Proceed的真实代码,把对象本身的invoke和拦截器的invoke交织在一起

  1. public Object proceed() throws Throwable {  
  2.         //  We start with an index of -1 and increment early.  
  3.         if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {  
  4.             return invokeJoinpoint();  
  5.         }  
  6.   
  7.         Object interceptorOrInterceptionAdvice =  
  8.             this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);  
  9.         if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {  
  10.             // Evaluate dynamic method matcher here: static part will already have  
  11.             // been evaluated and found to match.  
  12.             InterceptorAndDynamicMethodMatcher dm =  
  13.                 (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;  
  14.             if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {  
  15.                 return dm.interceptor.invoke(this);  
  16.             }  
  17.             else {  
  18.                 // Dynamic matching failed.  
  19.                 // Skip this interceptor and invoke the next in the chain.  
  20.                 return proceed();  
  21.             }  
  22.         }  
  23.         else {  
  24.             // It's an interceptor, so we just invoke it: The pointcut will have  
  25.             // been evaluated statically before this object was constructed.  
  26.             return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);  
  27.         }  
  28.     }  

 

拦截器是如何编织的

我们在JdkDynamicAopProxy中的invoke方法中看到如下拦截器链条的生成

  1. // Get the interception chain for this method.  
  2.             List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);  

 

它对应的代码是DefaultAdvisorChainFactory中的getInterceptorsAndDynamicInterceptionAdvice方法

  1. AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();  
  2.         for (Advisor advisor : config.getAdvisors()) {  
  3.             if (advisor instanceof PointcutAdvisor) {  
  4.                 // Add it conditionally.  
  5.                 PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;  
  6.                 if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {  
  7.                     MethodInterceptor[] interceptors = registry.getInterceptors(advisor);  

 

最终advisor的登记由DefaultAdvisorAdapterRegistry完成

我们可以看到DefaultAdvisorAdapterRegistry中首先登记了所有的AdviceAdapter

  1. /** 
  2.      * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters. 
  3.      */  
  4.     public DefaultAdvisorAdapterRegistry() {  
  5.         registerAdvisorAdapter(new MethodBeforeAdviceAdapter());  
  6.         registerAdvisorAdapter(new AfterReturningAdviceAdapter());  
  7.         registerAdvisorAdapter(new ThrowsAdviceAdapter());  
  8.     }  

 

在如下代码中按照AdviceAdapter的类型塞入责任练中

  1. public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {  
  2.         List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);  
  3.         Advice advice = advisor.getAdvice();  
  4.         if (advice instanceof MethodInterceptor) {  
  5.             interceptors.add((MethodInterceptor) advice);  
  6.         }  
  7.         for (AdvisorAdapter adapter : this.adapters) {  
  8.             if (adapter.supportsAdvice(advice)) {  
  9.                 interceptors.add(adapter.getInterceptor(advisor));  
  10.             }  
  11.         }  
  12.         if (interceptors.isEmpty()) {  
  13.             throw new UnknownAdviceTypeException(advisor.getAdvice());  
  14.         }  
  15.         return interceptors.toArray(new MethodInterceptor[interceptors.size()]);  
  16.     }  

 

这种做法类似于:

1.  首先为切入Adivsor建立责任链模式

2.       其次将每个Adivsor委托给DefaultAdvisorAdapterRegistry登录

3.       在DefaultAdvisorAdapterRegistry中封装Adivsor到各个专门的Advisor适配器中

比如,AfterReturningAdviceAdapter的代码如下:

  1. /** 
  2.  * Adapter to enable {@link org.springframework.aop.AfterReturningAdvice} 
  3.  * to be used in the Spring AOP framework. 
  4.  * 
  5.  * @author Rod Johnson 
  6.  * @author Juergen Hoeller 
  7.  */  
  8. class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {  
  9.   
  10.     public boolean supportsAdvice(Advice advice) {  
  11.         return (advice instanceof AfterReturningAdvice);  
  12.     }  
  13.   
  14.     public MethodInterceptor getInterceptor(Advisor advisor) {  
  15.         AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();  
  16.         return new AfterReturningAdviceInterceptor(advice);  
  17.     }  
  18.   
  19. }  

 

4.       在适配器中,将Advisor的方法和目标类方法交织在一起

  1. **  
  2.  * Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}.  
  3.  * Used internally by the AOP framework; application developers should not need  
  4.  * to use this class directly.  
  5.  *  
  6.  * @author Rod Johnson  
  7.  */  
  8. public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {  
  9.   
  10.     private final AfterReturningAdvice advice;  
  11.   
  12.   
  13.     /** 
  14.      * Create a new AfterReturningAdviceInterceptor for the given advice. 
  15.      * @param advice the AfterReturningAdvice to wrap 
  16.      */  
  17.     public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {  
  18.         Assert.notNull(advice, "Advice must not be null");  
  19.         this.advice = advice;  
  20.     }  
  21.   
  22.     public Object invoke(MethodInvocation mi) throws Throwable {  
  23.         Object retVal = mi.proceed();  
  24.         this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());  
  25.         return retVal;  
  26.     }  
  27.   
  28. }  

 

 

分享到:
评论

相关推荐

    spring-aop.jar各个版本

    spring-aop-1.1.1.jar spring-aop-1.2.6.jar spring-aop-1.2.9.jar spring-aop-2.0.2.jar spring-aop-2.0.6.jar spring-aop-2.0.7.jar spring-aop-2.0.8.jar spring-aop-2.0.jar spring-aop-2.5.1.jar spring-aop-...

    Spring中Aop的使用

    在Spring框架中,AOP(面向切面编程)是一种强大的设计模式,它允许开发者将关注点从业务逻辑中分离出来,比如日志记录、事务管理、权限检查等。AOP的核心概念包括切面(Aspect)、连接点(Join Point)、通知...

    开发工具 spring-aop-4.3.6.RELEASE

    开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE开发工具 spring-aop-4.3.6.RELEASE...

    Spring中aop编程所需要的jar包

    在Spring框架中,AOP(面向切面编程)是一种强大的工具,它允许程序员定义横切关注点,如日志、事务管理、权限控制等,并将它们模块化为可重用的切面。为了在Spring中进行AOP编程,我们需要一些特定的JAR包。以下是...

    spring-aop-5.2.0.RELEASE-API文档-中文版.zip

    赠送jar包:spring-aop-5.2.0.RELEASE.jar; 赠送原API文档:spring-aop-5.2.0.RELEASE-javadoc.jar; 赠送源代码:spring-aop-5.2.0.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.2.0.RELEASE.pom;...

    研究下Spring中AOP的实现?

    在Spring框架中,AOP(面向切面编程)是一种强大的设计模式,它允许开发者将关注点分离,将横切关注点(如日志、事务管理、安全检查等)与核心业务逻辑解耦。本篇文章将深入探讨Spring AOP的实现原理,并通过一个名...

    Spring中AOP所需jar包

    在AOP中,bean是被织入切面的对象,因此这个库是必不可少的。 3. **spring-context.jar**:提供了上下文相关的功能,如依赖注入(DI),事件传播,资源加载等。AOP的实现离不开Spring上下文的支持,因为它负责管理...

    spring-aop-jar

    在Spring AOP中,切面可以通过注解或XML配置来定义。 - 连接点(Join Point):连接点是程序执行过程中的一个特定点,例如方法的调用或字段的访问。 - 切入点(Pointcut):切入点是连接点的集合,定义了切面将在...

    spring-aop-3.2.0.RELEASE.jar

    spring-aop-3.2.0.RELEASE.jar,一个Spring中AOP的jar包

    springboot+aspect实现springaop拦截指定方法.zip

    在Spring AOP中,切点通常用正则表达式或预定义的注解来指定,例如`@Within("com.example.service.*")`表示拦截com.example.service包下的所有类的所有方法。 4. **通知(Advice)**:通知是在切点匹配的方法执行前...

    Spring中AOP实现EHCache的整合(一)

    在本文中,我们将深入探讨如何在Spring框架中集成并使用AOP(面向切面编程)来实现对EHCache的高效管理。Spring是一个广泛使用的Java应用框架,它提供了强大的依赖注入和面向切面编程功能。而EHCache是一款流行、高...

    spring-aop-5.0.10.RELEASE-API文档-中文版.zip

    赠送jar包:spring-aop-5.0.10.RELEASE.jar; 赠送原API文档:spring-aop-5.0.10.RELEASE-javadoc.jar; 赠送源代码:spring-aop-5.0.10.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.0.10.RELEASE....

    spring-aop-5.0.8.RELEASE-API文档-中英对照版.zip

    赠送jar包:spring-aop-5.0.8.RELEASE.jar; 赠送原API文档:spring-aop-5.0.8.RELEASE-javadoc.jar; 赠送源代码:spring-aop-5.0.8.RELEASE-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.0.8.RELEASE.pom;...

    spring-boot aop

    在Spring框架中,AOP主要用于日志记录、事务管理、性能统计等场景。本示例是关于如何在Spring Boot项目中实现AOP功能的一个简单演示。 首先,我们需要了解AOP的基本概念。AOP的核心是切面(Aspect),它封装了跨越...

    【Spring】Spring中AOP的简介和基本使用,SpringBoot使用AOP.txt

    在Spring AOP中,连接点总是表示方法执行。 5. **引入(Introduction)**:允许在现有的类中添加新的接口及其实现,从而让其他对象能够像处理普通对象一样处理引入的对象。 6. **织入(Weaving)**:把切面加入到程序...

    spring-aop-5.3.12-API文档-中英对照版.zip

    赠送jar包:spring-aop-5.3.12.jar; 赠送原API文档:spring-aop-5.3.12-javadoc.jar; 赠送源代码:spring-aop-5.3.12-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.3.12.pom; 包含翻译后的API文档:spring...

    Spring使用AOP的三个jar包

    这三个jar包在Spring AOP中的角色如下: 1. `aspectjrt.jar`:提供AspectJ的运行时支持,用于实现基于AspectJ的切面。 2. `aspectjweaver.jar`:作为Spring AOP的核心,负责运行时织入,动态修改字节码以实现切面...

    Spring中Aop的使用包括xml和注解

    在Spring框架中,AOP(面向切面编程)是一种强大的工具,它允许程序员将横切关注点(如日志、事务管理、权限检查等)与业务逻辑分离,从而实现代码的模块化和可维护性。这里我们将深入探讨两种在Spring中实现AOP的...

    spring-aop-5.3.10-API文档-中文版.zip

    赠送jar包:spring-aop-5.3.10.jar; 赠送原API文档:spring-aop-5.3.10-javadoc.jar; 赠送源代码:spring-aop-5.3.10-sources.jar; 赠送Maven依赖信息文件:spring-aop-5.3.10.pom; 包含翻译后的API文档:spring...

Global site tag (gtag.js) - Google Analytics