SpringAOP

AOP思想:

  AOP是对OOP的延伸,采取了横向抽取机制取代纵向继承提醒重复代码。将影响了多个类的公共行为封装到一个可重用模块,减少系统中重复代码,降低模块耦合。


 AOP常见场景:

  性能监控、事务管理、安全监测、缓存优化、记录日志等。


 相关概念:

  Aspect(切面):是切入点和通知(引入)的结合。

  Joinpoint(连接点):指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。

  Pointcut(切入点):一个通知将被引发的一系列连接点的机会。AOP框架必须允许开发者指定切入点。Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上。

  Introduction(引入):添加方法或字段到被通知的类。 Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口。

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

  Weaving(织入):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

  Proxy(代理):AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

  Advice(通知/增强):通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)。


 分析AOP:

  AOP采用动态代理模式,Spring在代理类中包裹切面,运行期间把切面织入到Bean中,Spring用代理类封装了目标类,同时拦截了被通知方法的调用,处理完通知后,再把调用转发给真正的目标Bean,也正因为是动态代理,所以Spring的AOP只支持到方法连接点而无法提供字段和构造器接入点(AspectJ和JBoss可以),所以Spring无法创建细粒度的通知。

  在这个动态代理的过程中,AOP设计可分为两大块:

    1、为目标对象建立代理对象(如何生成代理对象)。 

    2、启动代理对象的拦截器来完成各种横切面的织入(如何织入横切面同时如何拦截对目标对象方法的调用)。

  Spring提供两种方式生成代理对象。JDKProxy和CGLIB。两种代理方式的选择是由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认策略是如果目标类是接口,则采用JDK动态代理技术(JDK只能对接口进行代理),否则使用CGLIB来生成代理。

  JDK动态代理利用反射原理,给对象动态的生产代理对象,在执行的方法前后来执行相关内容。缺点在于:1、只能代理实现了接口的目标对象。2、基于反射,效率低

  CGLIB是针对类来实现代理的,原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,采用继承的方式。基于字节码实现效率高于反射,但它不能代理final方法,生成的是目标类的子类。


 何为代理对象?  

  Spring AOP的核心技术是动态代理,对于增强的对象方法,在用户调用这个对象方法(request)的时候其实是调用Spring AOP提前为其生成好的代理对象(Proxy)的相应方法,这个代理对象的方法实现就包含了preOperation—request—postOperation,通过对对象方法的这种拦截,增强了目标对象的方法操作,这种方式就是代理。

如何生成代理对象?

  

  具体AOP代理对象的生成,根据不同的需要分别由:AspectJProxyFactory、ProxyFactoryBean、ProxyFactory来完成。它们通过继承ProxyConfig、AdviceSupport、ProxyCreatorSupport等基类,实现其功能。

  Spring的AspectJProxyFactory、ProxyFactoryBean和ProxyFactory封装了代理对象AopProxy的生成过程,代理对象的生成实现过程由JDK的Proxy和CGLIB第三方来实现。  

如何拦截对目标对象方法的调用?

  对于JDK的代理对象,拦截使用的是InvocationHandler的invoke回调入口,对于CGLIB的代理对象,拦截是有设置好的回调callback方法(intercept方法)来完成。

  不管是什么方式生成的代理对象,对拦截器的调用都是通过proceed方法实现的。在该方法中完成对目标对象的增强功能。


 Aop的实现方式:

  1、基于代理的AOP

/**
 * 定义一个编码接口
 * @author hp16
 * @date 2018/12/316:25
 */
public interface CondingService {
    void coding();
}

/**
 * 实现编码接口
 * @author hp16
 * @date 2018/12/316:27
 */
public class CodingServiceImpl implements CondingService {
    @Override
    public void coding() {
        System.out.println("快点写代码");
    }
}

/**
 * 编码增强类,实现一个前置和后置的通知
 * @author hp16
 * @date 2018/12/316:28
 */
public class CodingHelper implements MethodBeforeAdvice, AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("编码后,要QC代码");
    }

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("编码前,要打开IDEA");
    }
}

  配置基于代理的AOP

    <bean id ="codingHelper" class="com.imooc.concurrency.aop.CodingHelper"/>
       <bean id="codingImpl" class="com.imooc.concurrency.CodingServiceImpl"/>
       <!-- 定义切点   pattern正则匹配所有的coding方法-->
       <bean id ="codingPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
             <property name="pattern" value=".*coding"></property>
       </bean>
     
       <!-- 切面 增强+切点结合 形成完整切面-->
       <bean id="codingHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
            <property name="advice" ref="codingHelper"/>
            <property name="pointcut" ref="codingPointcut"/>
       </bean>      

       <!-- 定义代理对象 通过ProxyFactoryBean生成最终代理对象-->
       <bean id="codingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
               <property name="target" ref="codingImpl"/>
               <property name="interceptorNames" value="codingHelperAdvisor"/>
       </bean>

   2、纯java对象切面

  通过spring内部机制自动扫描,而不需要使用代理

 <!-- 创建一个增强 advice -->
       <bean id ="codingHelper" class="com.imooc.concurrency.aop.CodingHelper"/>
       <!-- 目标类 -->
      <bean id="codingImpl" class="com.imooc.concurrency.CodingServiceImpl"/>

       <!--  配置切点和通知-->
       <bean id ="codingAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
            <property name="advice" ref="codingHelper"></property>
            <property name="pattern" value=".*coding"/>
       </bean>

       <!-- 自动代理配置 -->
       <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

  3、@Aspect注解

/**
 * 注解方式添加增强
 * @author hp16
 * @date 2018/12/316:37
 */
@Aspect
@Component
public class CodingHelperDemo {
    
    @Pointcut("execution(* *.coding(..))")
    public void codingPoint(){}

    @Before("codingPoint()")
    public void beforeCoding(){
        System.out.println("编码前,要打开IDEA");
    }

    @AfterReturning("codingPoint()")
    public void afterCoding(){
        System.out.println("编码后,要QC代码");
    }
}

 配置文件:

        <!--扫描包 -->
       <context:component-scan base-package="com.tgb" annotation-config="true"/> 
       <!-- ASPECTJ注解 -->
       <aop:aspectj-autoproxy  proxy-target-class="true" />  
      
       <!-- 目标类 -->
       <bean id="coding" class="com.imooc.concurrency.aop.CodingServiceImpl"/>

   4、注入形式的Aspect切面

/**
 * 注入形式的Aspcet切面
 * @author hp16
 * @date 2018/12/316:37
 */
@Aspect
@Component
public class CodingHelperDemo2 {

    public void beforeCoding(){
        System.out.println("编码前,要打开IDEA");
    }

    public void afterCoding(){
        System.out.println("编码后,要QC代码");
    }
}

 配置文件:

       <!-- 目标类 -->
       <bean id="coding" class="com.imooc.concurrency.aop.CodingServiceImpl"/>
       <bean id ="codingHelper" class="com.imooc.concurrency.aop.CodingHelperDemo2"/>
      
       <aop:config>
           <aop:aspect ref="codingHelper">
                <aop:before method="beforeCoding" pointcut="execution(* *.coding(..))"/>
                <aop:after method="afterCoding" pointcut="execution(* *.coding(..))"/>
           </aop:aspect>
       </aop:config>

  

原文地址:https://www.cnblogs.com/zhangbLearn/p/10034502.html