• 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>

      

  • 相关阅读:
    C++三大特性之多态
    内向者沟通圣经:4P法(Preparation,Presence,Push,Practice)
    RTP/RTCP、TCP、UDP、RTMP、RTSP
    网络七层协议
    预防U盘被病毒侵害的方法
    Win8安装程序出现2502、2503错误解决方法
    小L的区间求和
    【剑指offer-12】矩阵中的路径
    【剑指offer】数值的整数次方
    【剑指offer】二进制中1的个数
  • 原文地址:https://www.cnblogs.com/zhangbLearn/p/10034502.html
Copyright © 2020-2023  润新知