Spring框架IOC和AOP的实现原理

本文深入解析Spring框架中的IoC(控制反转)和AOP(面向切面编程)两大核心概念,涵盖IoC的基本原理、依赖注入方式及其优势,以及AOP的关键概念如Aspect、JoinPoint、PointCut等,帮助读者理解Spring是如何通过这些机制降低组件间的耦合度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、IoC(Inversion of Control)  

  (1). IoC(Inversion of Control)是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控。控制权由应用代码中转到了外部容器,控制权的转移是所谓反转。 对于Spring而言,就是由Spring来控制对象的生命周期和对象之间的关系;IoC还有另外一个名字——“依赖注入(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,即由容器动态地将某种依赖关系注入到组件之中。  

(2). 在Spring的工作方式中,所有的类都会在spring容器中登记,告诉spring这是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

(3). 在系统运行中,动态的向某个对象提供它所需要的其他对象。  

(4). 依赖注入的思想是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。 总而言之,在传统的对象创建方式中,通常由调用者来创建被调用者的实例,而在Spring中创建被调用者的工作由Spring来完成,然后注入调用者,即所谓的依赖注入or控制反转。 注入方式有两种:依赖注入和设置注入; IoC的优点:降低了组件之间的耦合,降低了业务对象之间替换的复杂性,使之能够灵活的管理对象。

二、AOP(Aspect Oriented Programming)

(1). AOP面向方面编程基于IoC,是对OOP的有益补充;

(2). AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了 多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的 逻辑或责任封装起来,比如日志记录,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

(3). AOP代表的是一个横向的关 系,将“对象”比作一个空心的圆柱体,其中封装的是对象的属性和行为;则面向方面编程的方法,就是将这个圆柱体以切面形式剖开,选择性的提供业务逻辑。而 剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹,但完成了效果。

(4). 实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

(5). Spring实现AOP:JDK动态代理和CGLIB代理 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理;其核心的两个类是InvocationHandler和Proxy。 CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强;需要引入包asm.jar和cglib.jar。     使用AspectJ注入式切面和@AspectJ注解驱动的切面实际上底层也是通过动态代理实现的。

(6). AOP使用场景:                     

Authentication 权限检查        

Caching 缓存        

Context passing 内容传递        

Error handling 错误处理        

Lazy loading 延迟加载        

Debugging  调试      

logging, tracing, profiling and monitoring 日志记录,跟踪,优化,校准        

Performance optimization 性能优化,效率检查        

Persistence  持久化        

Resource pooling 资源池        

Synchronization 同步        

Transactions 事务管理    

另外Filter的实现和struts2的拦截器的实现都是AOP思想的体现。  

三、详细介绍

       Spring提供了很多轻量级应用开发实践的工具集合,这些工具集以接口、抽象类、或工具类的形式存在于Spring中。通过使用这些工具集,可以实现应用程序与各种开源技术及框架间的友好整合。比如有关jdbc封装的数据访问工具Spring JDBC,有关编写单元测试的spring test包以及spring-mock,有关访问动态脚本语言的Spring Script,另外还有发送邮件的工具Spring Mail、日程及任务处理工具Spring scheduling等。 可以这么说,大多数企业级应用开发中经常涉及到的一些通用的问题,都可以通过Spring提供的一些实用工具包轻松解决。

       依赖注入的三种方式:(1)接口注入(2)Construct注入(3)Setter注入

       控制反转(IoC)与依赖注入(DI)是同一个概念,引入IOC的目的:(1)脱开、降低类之间的耦合;(2)倡导面向接口编程、实施依赖倒换原则; (3)提高系统可插入、可测试、可修改等特性。
       具体做法:

     (1)将bean之间的依赖关系尽可能地抓换为关联关系;
     (2)将对具体类的关联尽可能地转换为对Java interface的关联,而不是与具体的服务对象相关联;
     (3)Bean实例具体关联相关Java interface的哪个实现类的实例,在配置信息的元数据中描述;
     (4)由IoC组件(或称容器)根据配置信息,实例化具体bean类、将bean之间的依赖关系注入进来。

      org.springframework.beans及org.springframework.context包是Spring IoC容器的基础。BeanFactory提供的高级配置机制,使得管理任何性质的对象成为可能。ApplicationContext是BeanFactory的扩展,功能得到了进一步增强,比如更易与Spring AOP集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的context实现(如针对web应用的WebApplicationContext)。 简而言之,BeanFactory提供了配制框架及基本功能,而ApplicationContext则增加了更多支持企业核心内容的功能。ApplicationContext完全由BeanFactory扩展而来,因而BeanFactory所具备的能力和行为也适用于ApplicationContext。

     IoC容器负责容纳bean,并对bean进行管理。在Spring中,BeanFactory是IoC容器的核心接口。它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。Spring为我们提供了许多易用的BeanFactory实现,XmlBeanFactory就是最常用的一个。该实现将以XML方式描述组成应用的对象以及对象间的依赖关系。XmlBeanFactory类将持有此XML配置元数据,并用它来构建一个完全可配置的系统或应用。

    实现化容器:

  1. Resource resource = new FileSystemResource("beans.xml");  
  2. BeanFactory factory = new XmlBeanFactory(resource);  
  3. ... 或...  
  4. ClassPathResource resource = new ClassPathResource("beans.xml");  
  5. BeanFactory factory = new XmlBeanFactory(resource);  
  6. ... 或...  
  7. ApplicationContext context = new ClassPathXmlApplicationContext(  
  8.         new String[] {"applicationContext.xml", "applicationContext-part2.xml"});  
  9. // of course, an ApplicationContext is just a BeanFactory  
  10. BeanFactory factory = (BeanFactory) context;  

   将XML配置文件分拆成多个部分是非常有用的。为了加载多个XML文件生成一个ApplicationContext实例,可以将文件路径作为字符串数组传给ApplicationContext构造器。而bean factory将通过调用bean defintion reader从多个文件中读取bean定义。

   通常情况下,Spring团队倾向于上述做法,因为这样各个配置并不会查觉到它们与其他配置文件的组合。另外一种方法是使用一个或多个的<import/>元素来从另外一个或多个文件加载bean定义。所有的<import/>元素必须放在<bean/>元素之前以完成bean定义的导入。 让我们看个例子:

<beans><import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>
      <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
  </beans>

    在上面的例子中,我们从3个外部文件:services.xml、messageSource.xml及themeSource.xml来加载bean定义。这里采用的都是相对路径,因此,此例中的services.xml一定要与导入文件放在同一目录或类路径,而messageSource.xml和themeSource.xml的文件位置必须放在导入文件所在目录下的resources目录中。正如你所看到的那样,开头的斜杠‘/’实际上可忽略。因此不用斜杠‘/’可能会更好一点。

   根据Spring XML配置文件的Schema(或DTD),被导入文件必须是完全有效的XML bean定义文件,且根节点必须为<beans/> 元素。

   BeanFactory和FactoryBean的区别,简而言之,BeanFactory是加载的容器,加载一切的BEAN,而FactoryBean用于创建代理类

===============================================================上面已讲到

 BeanFactory它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

FactoryBean(通常情况下,bean无须自己实现工厂模式,Spring容器担任工厂角色;但少数情况下,容器中的bean本身就是工厂,其作用是产生其它bean实例),作用是产生其他bean实例。通常情况下,这种bean没有什么特别的要求,仅需要提供一个工厂方法,该方法用来返回其他bean实例。由工厂bean产生的其他bean实例,不再由Spring容器产生,因此与普通bean的配置不同,不再需要提供class元素。

ProxyFactoryBean用于创建代理(根据Advisor生成的Bean,也就是TargetBean的代理)

我们的Advisor,PointCut等等,其最终目的都是为了创建这个代理。

===============================================================下面将讲到

AOP全名Aspect-Oriented Programming,中文直译为面向切面(方面)编程,当前已经成为一种比较成熟的编程思想,可以用来很好的解决应用系统中分布于各个模块的交叉关注点问题。在轻量级的J2EE中应用开发中,使用AOP来灵活处理一些具有横切性质的系统级服务,如事务处理、安全检查、缓存、对象池管理等,已经成为一种非常适用的解决方案。 AOP中比较重要的概念有:Aspect、JoinPoint、PonitCut、Advice、Introduction、Weave、Target Object、Proxy Object等

引介(Introduction)是指给一个现有类添加方法或字段属性,引介还可以在不改变现有类代码的情况下,让现有的Java类实现新的接口,或者为其指定一个父类从而实现多重继承。相对于增强(Advice)可以动态改变程序的功能或流程来说,引介(Introduction)则用来改变一个类的静态结构。比如我们可以让一个现有为实现java.lang.Cloneable接口,从而可以通过clone()方法复制这个类的实例。

拦截器是用来实现对连接点进行拦截,从而在连接点前或后加入自定义的切面模块功能。在大多数JAVA的AOP框架实现中,都是使用拦截器来实现字段访问及方法调用的拦截(interception)。所用作用于同一个连接点的多个拦截器组成一个连接器链(interceptor chain),链接上的每个拦截器通常会调用下一个拦截器。Spring AOP及JBoos AOP实现都是采用拦截器来实现的。

面向对象编程(OOP)解决问题的重点在于对具体领域模型的抽象,而面向切面编程(AOP)解决问题的关键则在于对关注点的抽象。也就是说,系统中对于一些需要分散在多个不相关的模块中解决的共同问题,则交由AOP来解决;AOP能够使用一种更好的方式来解决OOP不能很好解决的横切关注点问题以及相关的设计难题来实现松散耦合。因此,面向方面编程 (AOP) 提供另外一种关于程序结构的思维完善了OOP,是OOP的一种扩展技术,弥补补了OOP的不足。

AOP概念详解:注意以下实例<aop:开头的AspectJ的概念,Spring没有分的这么细。

  — 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现, 然后可以通过@Aspect标注或在applictionContext.xml中进行配置: 

      <aop:aspect id="fourAdviceAspect" ref="fourAdviceBean" order="2"> 

  — 连接点(Joinpoint):程序执行过程中的行为,如方法的调用或特定的异常被抛出,在代码上有JoinPoint类和ProceedingJoinPoint类,如下所示,可以通过JoinPoint获取很多参数,JoinPoint一般用在Advice实现方法中作为参数传入,ProceedingJoinPoint用于实现围绕Advice的参数传入。  通过下面JoinPoint的接口可以看出通过JoinPoint可以得到代理对象和Target对象。

  1. package org.aspectj.lang;  
  2. import org.aspectj.lang.reflect.SourceLocation;  
  3. public interface JoinPoint {  
  4.     String toString();         //连接点所在位置的相关信息  
  5.     String toShortString();     //连接点所在位置的简短相关信息  
  6.     String toLongString();     //连接点所在位置的全部相关信息  
  7.     Object getThis();         //返回AOP代理对象  
  8.     Object getTarget();       //返回目标对象  
  9.     Object[] getArgs();       //返回被通知方法参数列表  
  10.     Signature getSignature();  //返回当前连接点签名  
  11.     SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置  
  12.     String getKind();        //连接点类型  
  13.     StaticPart getStaticPart(); //返回连接点静态部分  
  14. }  
  15.   
  16. public interface ProceedingJoinPoint extends JoinPoint {  
  17.     public Object proceed() throws Throwable;  
  18.     public Object proceed(Object[] args) throws Throwable;  
  19. }  

      — 切入点(Pointcut):指定一个Adivce将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。

          xml中配置:

<aop:pointcut id="myPointcut" expression="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" /> 

          或使用Annoation :@pointcut("execution * transfer(..)")并用一个返回值为void,方法体为空的方法来命名切入点如:  private void anyOldTransfer(){}

     之后就可以在Advice中引用,如: @AfterReturning(pointcut="anyOldTransfer()", returning="reVal")  

  1. package org.springframework.aop;  
  2. public interface Pointcut {  
  3.     ClassFilter getClassFilter();  
  4.     MethodMatcher getMethodMatcher();  
  5.     Pointcut TRUE = TruePointcut.INSTANCE;  
  6. }  
  7. package org.springframework.aop;  
  8. public interface ClassFilter {  
  9.      boolean matches(Class<?> clazz);//如果clazz与我们关注的现象相符时返回true,负责返回false  
  10.      ClassFilter TRUE = TrueClassFilter.INSTANCE;//静态参数 如果类型对于要扑捉的Pointcut来说无所谓,可将此参数传递给Pointcut  
  11. }  
  12. package org.springframework.aop;  
  13. public interface MethodMatcher {  
  14.    boolean matches(Method method, Class<?> targetClass);  
  15.   
  16.  /** 
  17.   * 是否对参数值敏感 
  18.   * 如果为false表明匹配时不需要判断参数值(参数值不敏感),称之为StaticMethodMatcher,这时只有 
  19.   * matches(Method method, Class<?> targetClass); 被执行,执行结果可以缓存已提高效率。 
  20.   * 如果为true表明匹配时需要判断参数值(参数值敏感),称之为DynamicMethodMatcher,这时先执行 
  21.   * matches(Method method, Class<?> targetClass);如果返回true,然后再执行 
  22.   * boolean matches(Method method, Class<?> targetClass, Object[] args);已做进一步判断 
  23.   *  
  24.   */  
  25.  boolean isRuntime();  
  26.  boolean matches(Method method, Class<?> targetClass, Object[] args);  
  27.  MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;  
  28. }  

关于PointCut中使用的execution的说明:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 

modifiers-pattern:方法的操作权限

ret-type-pattern:返回值

declaring-type-pattern:方法所在的包

name-pattern:方法名

parm-pattern:参数名

throws-pattern:异常

记忆法则就是Java定义一个方法时的样子:public boolean produceValue(int oo) throws Exception, 只要在方法名前加上包名就可以了。

其中,除ret-type-pattern和name-pattern之外,其他都是可选的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值为任意类型;方法名任意;参数不作限制的所有方法。

常见的PointCut结构图:
 

  — 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。Advice中必须用到PointCut

     在xml中配置,配置中的method为Aspect实现类中的方法名,使用pointcut自定义或pointcut-ref进行引用已有pointcut

       <aop:before pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="authority" /> 

       <aop:after pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="release" /> 

       <aop:after-returning pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="log" />

       <aop:around pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))" method="processTx" /> 

       <aop:after-throwing pointcut-ref="myPointcut" method="doRecovertyActions" throwing="ex" />     

     或使用Annoation: 

       @Before("execution(* com.wicresoft.app.service.impl.*.*(..))")

       @AfterReturning(returning="rvt", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))")

       @AfterThrowing(throwing="ex", pointcut="execution(* com.wicresoft.app.service.impl.*.*(..))")

       @After("execution(* com.wicresoft.app.service.impl.*.*(..))")

       @Around("execution(* com.wicresoft.app.service.impl.*.*(..))")  

注意

  • AfterReturning 增强处理处理只有在目标方法成功完成后才会被织入。
  • After 增强处理不管目标方法如何结束(保存成功完成和遇到异常中止两种情况),它都会被织入。

使用方法拦截器的around通知,需实现接口MethodInterceptor:

public interface MethodInterceptor extends Interceptor {

    Object invoke(MethodInvocation invocation) throws Throwable;

}

invoke()方法的MethodInvocation 参数暴露将被调用的方法、目标连接点、AOP代理和传递给被调用方法的参数。 invoke()方法应该返回调用的结果:连接点的返回值。

一个简单的MethodInterceptor实现看起来如下:

  1. public class DebugInterceptor implements MethodInterceptor {  
  2.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  3.         System.out.println("Before: invocation=[" + invocation + "]");  
  4.         Object rval = invocation.proceed();  
  5.         System.out.println("Invocation returned");  
  6.         return rval;  
  7.     }  
  8. }  

注意MethodInvocation的proceed()方法的调用。这个调用会应用到目标连接点的拦截器链中的每一个拦截器。大部分拦截器会调用这个方法,并返回它的返回值。但是, 一个MethodInterceptor,和任何around通知一样,可以返回不同的值或者抛出一个异常,而不调用proceed方法。但是,没有好的原因你要这么做。

Before通知:需实现MethodBeforeAdvice接口

public interface MethodBeforeAdvice extends BeforeAdvice {

    void before(Method m, Object[] args, Object target) throws Throwable;

}

Throw通知,需实现ThrowsAdvice接口

After Returning通知须直线AfterReturningAdvice接口

public interface AfterReturningAdvice extends Advice {

    void afterReturning(Object returnValue, Method m, Object[] args, Object target)

            throws Throwable;

}

       — 引入(Introduction):添加方法或字段到被通知的类,引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。使用introduction要有三个步骤(1)声明新接口(2)创建自己的IntrouductionInterceptor通过Implements IntroductionInterceptor或extends DelegatingIntroductionInterceptor 并同时implements(1)中声明的接口 (3)将新接口和自定义的IntroductionInterceptor配置到DefaultIntroductionAdvisor中,然后将前三者配置到ProxyFactoryBean中。

  1. public interface IOtherBean {  
  2.     public void doOther();  
  3. }  
  4.   
  5. public class SomeBeanIntroductionInterceptor implements IOtherBean, IntroductionInterceptor {  
  6.   
  7.     public void doOther() {  
  8.         System.out.println("doOther!");  
  9.     }  
  10.   
  11.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  12.           
  13.         //判断调用的方法是否为指定类中的方法  
  14.         if ( implementsInterface(invocation.getMethod().getDeclaringClass()) ) {  
  15.             return invocation.getMethod().invoke(this, invocation.getArguments());  
  16.         }  
  17.           
  18.         return invocation.proceed();  
  19.     }  
  20.       
  21.     /** 
  22.      * 判断clazz是否为给定接口IOtherBean的实现 
  23.      */  
  24.     public boolean implementsInterface(Class clazz) {  
  25.           
  26.         return clazz.isAssignableFrom(IOtherBean.class);  
  27.     }  
  28. }  

 

  1. <!-- 目标对象 -->  
  2. <bean id="someBeanTarget" class="aop.spring.introduction.SomeBeanImpl" />  
  3. <!-- 通知 -->  
  4. <bean id="someBeanAdvice" class="aop.spring.introduction.SomeBeanIntroductionInterceptor" />  
  5. <!-- 通知者,只能以构造器方法注入-->  
  6. <bean id="introductionAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor">  
  7.     <constructor-arg ref="someBeanAdvice" />  
  8.     <constructor-arg value="aop.spring.introduction.IOtherBean" />      
  9. </bean>  
  10.       
  11. <!-- 代理 (将我们的切面织入到目标对象)-->  
  12. <bean id="someBeanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
  13.     <!-- 若目标对象实现了代理接口,则可以提供代理接口的配置 -->  
  14.     <property name="proxyInterfaces"  value="aop.spring.introduction.ISomeBean" />  
  15.     <!-- 配置目标对象 -->  
  16.     <property name="target" ref="someBeanTarget" />  
  17.     <!-- 配置切面 -->  
  18.     <property name="interceptorNames">  
  19.         <list>  
  20.             <value>introductionAdvisor</value>  
  21.         </list>  
  22.     </property>  
  23. </bean>  

    — 拦截器(Advisor )常用的有PointCutAdvisor和IntroudtionAdvisor。前者Advisor有PointCut和Advice组成,满足Poincut(指定了哪些方法需要增强),则执行相应的Advice(定义了增强的功能),后者由Introduction构成。PointCutAdvisor主要是根据PointCut中制定的Target Objects的方法在调用(前,后,around,throws, after-return等)时引入新的Aspect中的methods, 而IntroductionAdvisor主要是引入新的接口到Targets对象中。

  1. public interface PointcutAdvisor {  
  2.     Pointcut getPointcut();  
  3.     Advice getAdvice();  
  4. }  

1、 PointcutAdvisor: Advice和Pointcut,默认实现为DefaultPointcutAdvisor, 还有NameMatchMethodPointcutAdvisor,RegexpMethodPointcutAdvisor等。 

 其中NameMacthMethodPointCutAdvisor、RegexpMethodPointCutAdvisor 可以对比常用的PointCut类有NameMatchedMethodPointCut和JdkRegexMethodPointCut。
 前者需要注入mappedName和advice属性,后者需要注入pattern和advice属性。其中mappedName和pattern是直接配置的值,而advice需要自己实现具体的advice,可见实现advisor的时候,不需要实现PointCut,一般PointCut只需要配置就好了,不需要具体实现类
 mappedName指明了要拦截的方法名,pattern按照正则表达式的方法指明了要拦截的方法名,advice定义了一个增强(需要自己实 现MethodBeforeAdvice、  MethodAfterAdvice、ThrowsAdvice、MethodInterceptor接口之 一)。然后在ProxyFactoryBean的拦截器(interceptorNames)中注入这个PointCutAdvisor即可,如上面这个ProxyFactoryBean是一个静态代理,只能代理一个类给加上AOP,那么这个静态代理需要注入有目标对象,目标对象的接口,和interceptorsNames

2、 IntroductionAdvisor :默认实现为DefaultIntroductionAdvisor,这个主要与Introduction有关,可以参考上面的例子

  — 目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。

  — AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。如ProxyFactory,ProxyFactoryBean, 下面会进行详细说明    

  — 编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。将Aspect加入到程序代码的过程,对于Spring AOP,由ProxyFactory或者ProxyFactoryBean负责织入动作。 

    通过ProxyFactory可以将对符合条件的类调用时添加上Aspect。

    或者 可使用XML声明式 ProxyFactoryBean:需要设定 target,interceptorNames(可以是Advice或者Advisor,注意顺序, 对接口代理需设置proxyInterfaces 

注意:一个ProxyFactoryBean只能指定一个代理目标,不是很方便,这就产生了自动代理。通过自动代理,可以实现自动为多个目标Bean实现AOP代理、避免客户端直接访问目标Bean(即getBean返回的都是Bean的代理对象)。spring的自动代理是通过BeanPostProcessor实现的,容器载入xml配置后会修改bean为代理Bean,而id不变。 

ApplicationContext可以直接检测到定义在容器中的BeanPostProcessor,BeanFactory需要手动添加。 
有2种常用的BeanPostProcessor: 
1.BeanNameAutoProxyCreator 故名思议,BeanName需要注入的两个属性有BeanNames和interceptorNames

  1. <bean id="loginBeforeAdvisor" .../>    
  2. <bean id="loginThrowsAdvisor" .../>    
  3. <bean  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">    
  4.         <!-- 注入目标Bean -->    
  5.         <property name="beanNames" value="*Service">    
  6.         </property>    
  7.         <property name="interceptorNames">    
  8.             <list>    
  9.                 <value>loginBeforeAdvisor</value>    
  10.                 <value>loginThrowsAdvisor</value>    
  11.             </list>    
  12.         </property>    
  13. </bean>    

2.DefaultAdvisorAutoProxyCreator: DefaultAdvisorAutoProxyCreator和BeanNameAutoProxyCreator不同的是,前者只和Advisor 匹配, 该类实现了BeanPostProcessor接口。当应用上下文读入所有的Bean的配置信息后,该类将扫描上下文,寻找所有的Advisor,他将这些Advisor应用到所有符合切入点的Bean中。所以下面的xml中没有绑定也无需绑定DefaultAdvisorAutoProxyCreator与Advisor的关系。

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">  
  3. <beans>  
  4.      <bean id="kwikEMartTarget" class="demo.ApuKwikEMart"></bean>  
  5.      <bean id="performanceThresholdInterceptor" class="demo.advice.PerformanceThresholdInterceptor">  
  6.           <constructor-arg>  
  7.                <value>5000</value>  
  8.           </constructor-arg>  
  9.      </bean>  
  10.      <!-- 使用RegexpMethodPointcutAdvisor来匹配切入点完成个一个Advisor; -->  
  11.      <bean id="regexpFilterPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
  12.           <property name="pattern">  
  13.                <!-- 匹配的名字为方法名-->  
  14.                <value>.*buy.*</value>  
  15.           </property>  
  16.           <property name="advice">  
  17.                <ref bean="performanceThresholdInterceptor"/>  
  18.           </property>  
  19.      </bean>  
  20.      <bean id="defaultAdvisorAutoProxyCreator"  
  21.      class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />  
  22. </beans>  

在使用Aonnotation的时候,需要进行在ApplicationContext.xml中进行配置:

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.        xmlns:aop="http://www.springframework.org/schema/aop"        
  5.        xsi:schemaLocation="http://www.springframework.org/schema/beans  
  6.            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  7.            http://www.springframework.org/schema/aop  
  8.        http://www.springframework.org/schema/beans/spring-aop-3.0.xsd">  
  9.         <!-- 启动对@AspectJ注解的支持 -->  
  10.         <aop:aspectj-autoproxy/>  
  11. </beans>  


 

综上,Spring下AOP的配置与实现,BeanNameAutoProxyCreator,DefaultAdvisorAutoProxyCreator已经部分简化了AOP配置,然而还是很繁琐: 首先要编写xxxAdvice类(需要自己实现MethodBeforeAdvice、MethodAfterAdvice、 ThrowsAdvice、MethodInterceptor接口之一),然后还要在xml中配置Advisor,还要在Advisor中注入 Advice,最后还要将Advisor加入ProxyFactoryBean、BeanNameAutoProxyCreator或者 DefaultAdvisorAutoProxyCreator中

实际上AOP不止Spring进行了实现,还有AspectJ,后者对AOP中的概念实现比较彻底,可以看上面,而Spring中对AOP的方方面面进行简化,拿上面定义的regexpFilterPointcutAdvisor是一种Advisor包含了PointCut和Advice,而此处的PointCut就是pattern属性的值了,没有特定的PointCut Bean定义,而advice定义了Bean。而其他概念Aspect, JoinPoint都融汇于Advice的实现中即Advisor(MethodBeforeAdvice等和MethodIntector接口的实现类)或IntroductionInterceptor了

转载于:https://my.oschina.net/u/2519523/blog/1858855

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值