【Spring学习随笔】4. Spring AOP

4. Spring AOP

4.1 Spring AOP的基本概念

4.1.1 AOP的概念

AOP ( Aspect-Oriented Programming )面向切面编程,它与OOP (Object-Oriented Programming , 面向对象编程) 相辅相成,提供了与OOP 不同的抽象软件结构的视角。在OOP 中,以类作为程序的基本单元,而AOP 中的基本单元是Aspect (切面)。Struts2的拦截器设计就是基于AOP 的思想,是个比较经典的应用。

4.1.2 AOP的术语

  • 切面
    切面(Aspect)是指封装横切到系统功能(例如事务处理)的类。
  • 连接点
    连接点(Joinpoint)是指程序运行中的一些时间点,例如方法的调用或异常的抛出。
  • 切人点
    切入点(Pointcut)是指需要处理连接点。在Spring AOP 中,所有的方法执行都是连接点,而切入点是一个描述信息,它修饰的是连接点,通过切入点确定哪些连接点需要被处理。

image-20191123225950652

  • 通知
    通知(Advice)是由切面添加到特定的连接点(满足切入点规则)的一段代码,即在定义好的切入点处所要执行的程序代码,可以将其理解为切面开启后切面的方法,因此通知是切面的具体实现
  • 引人
    引入(Introduction)允许在现有的实现类中添加自定义的方法和属性
  • 目标对象
    目标对象(Target Object)是指所有被通知的对象。如果AOP 框架使用运行时代理的方式(动态的AOP )来实现切面,那么通知对象总是一个代理对象。
  • 代理
    代理(Proxy)是通知应用到目标对象之后被动态创建的对象
  • 织人
    织入(Weaving)是将切面代码插入到目标对象上,从而生成代理对象的过程。根据不同的实现技术, AOP 织入有3 种方式:编译期织入, 需要有特殊的Java 编译器;类装载期织入, 需要有特殊的类装载器;动态代理织入,在运行期为目标类添加通知生成子类的方式。SpringAOP 框架默认采用动态代理织入,而AspectJ(基于Java 语言的AOP 框架)采用编译期织入和类装载期织入

4.2 动态代理

​ 在Java中有多种动态代理技术,例如JDK、CGLIB、Javassist、ASM,其中最常用的动态代理技术是JDK和CGLIB。目前,在Spring AOP中常用JDK和CGLIB两种动态代理技术

4.2.1 JDK动态代理

​ JDK 动态代理是java.lang . reflect. *包提供的方式,它必须借助一个接口才能产生代理对象。因此,对于使用业务接口的类, Spring 默认使用JDK 动态代理实现AOP

实例演示:

  • 创建应用,创建接口及实现类,在src的目录下创建一个dynamic.jdk包,在该包中创建接口TestDao和接口实现类TestDaoImpl。该实现类作为目标类,在代理类中对其方法进行增强处理。

    TestDao的代码如下:

    package dynamic.jdk;
    
    public interface TestDao {
        public void save();
        public void modify();
        public void delete();
    }
    

    TestDaoImpl的代码如下:

    package dynamic.jdk;
    
    public class TestDaoImpl implements TestDao {
        @Override
        public void save() {
            System.out.println("保存");
        }
    
        @Override
        public void modify() {
            System.out.println("修改");
        }
    
        @Override
        public void delete() {
            System.out.println("删除");
        }
    }
    
  • 创建切面类

    在src目录下创建一个aspect包,在该包下创建切面类MyAspect,注意在该类中可以定义多个通知(增强处理的功能方法)。

    MyAspect的代码如下:

    package aspect;
    
    /**
     * 切面类,可以定义多个通知,即增强处理的方法
     */
    public class MyAspect {
        public void check() {
            System.out.println("模拟权限控制");
        }
    
        public void except() {
            System.out.println("模拟一场处理");
        }
    
        public void log() {
            System.out.println("模拟日志记录");
        }
    
        public void monitor() {
            System.out.println("性能检测");
        }
    }
    
  • 创建代理类

    在dynamic.jdk包中创建代理类JDKDynamicProxy。在JDK动态代理中代理类必须实现java.lang.reflect.InvocationHandler接口,并编写代理方法,在代理方法中需要通过Proxy实现动态代理。

    JDKDynamicProxy的代码如下:

    package dynamic.jdk;
    
    import aspect.MyAspect;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class JDKDynamicProxy implements InvocationHandler {
    //    声明目标类接口对象(真实对象)
        private TestDao testDao;
    //    创建代理的方法,建立代理对象和真实对象的代理关系,并返回代理对象
        public Object createProxy(TestDao testDao) {
            this.testDao = testDao;
    //        1. 类加载器
            ClassLoader cld = JDKDynamicProxy.class.getClassLoader();
    //        2. 被代理对象实现的所有接口
            Class[] clazz = testDao.getClass().getInterfaces();
    //        3. 使用代理类进行增强,返回代理后的对象
            return Proxy.newProxyInstance(cld, clazz, this);
        }
    
        /**
         * 代理的逻辑方法,所有动态代理类的方法调用都交给该方法处理
         * proxy是被代理对象
         * method是将要被执行的方法
         * args是执行方法时需要的参数
         * return指返回代理结果
         *
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //        创建一个切面
            MyAspect myAspect = new MyAspect();
    //        前增强
            myAspect.check();
            myAspect.except();
    //        在目标类上调用方法标并传入参数,相当于调用testDao中的方法
            Object obj = method.invoke(testDao, args);
    //        后增强
            myAspect.log();
            myAspect.monitor();
            return obj;
        }
    }
    
  • 创建测试类

    在dynamic.jdk包中创建测试类JDKDynamicTest。在主方法中创建代理对象和目标对象,然后从代理对象中获取对目标对象增强后的对象,最后调用该对象的添加、修改和删除方法。

    JDKDynamicTest的代码如下:

    package dynamic.jdk;
    
    public class JDKDynamicTest {
        public static void main(String[] args) {
    //        创建代理对象
            JDKDynamicProxy jdkProxy = new JDKDynamicProxy();
    //        创建目标对象
            TestDao testDao = new TestDaoImpl();
    //        从代理对象中获取增强后的目标对象,该对象是一个被代理的对象,它会进入代理的逻辑方法invoke中
            TestDao testDaoAdvice = (TestDao) jdkProxy.createProxy(testDao);
    //        执行方法
            testDaoAdvice.save();
            System.out.println("===============");
            testDaoAdvice.modify();
            System.out.println("===============");
            testDaoAdvice.delete();
        }
    }
    
  • 运行效果:

    image-20191125173616991

4.2.2 CGLIB动态代理

JDK 动态代理必须提供接口才能使用,对于没有提供接口的类,只能采用CGLIB 动态代理

​ CGLIB (Code Generation Library ) 是一个高性能开源的代码生成包,采用非常底层字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring Core 包中己经集成了CGLIB 所需要的JAR 包,不需要另外导入JAR 包。

实例演示:

  • 创建目标类

    在src目录下创建一个dynamic.cglib包,在该包中创建目标类TestDao,注意该类不需要实现任何接口。

    TestDao的代码如下:

    
    

4.3 基于代理类的AOP实现

在Spring中默认使用JDK动态代理实现AOP编程。使用org.springframework.aop.framwork.ProxyFactoryBean创建代理是Spring AOP实现的最基本方式。

4.3.1 通知类型

根据Spring中通知在目标类方法中的连接点位置,通知可以分为6种类型。

4.3.1.1 环绕通知

​ 环绕通知(org.aopallicance.intercept.MethodIntercerptor)是在目标方法执行前和执行后实施增强,可应用于日志记录、事务处理等功能

4.3.1.2 前置通知

​ 前置通知(org.springframework.aop.MethodBeforeAdvice)实在目标方法执行前实施增强,可应用于权限管理等功能

4.3.1.3 后置返回通知

​ 后置返回通知(org.springframework.aop.AfterReturningAdvice)是在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能

4.1.3.5 后置(最终)通知

​ 后置通知(org.springframework.aop.AfterAdvice)是在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该类通知,该类通知可应用于释放资源

4.1.3.6 引入通知

​ 引入通知(org.springframework.aop.IntroductionInterceptor)是在目标类中添加一些新的方法和属性,可应用于修改目标类(增强类)

4.3.2 ProxyFactoryBean

​ ProxyFactoryBean是org.springframework.beans.factory.FactoryBean接口的实现类,FactoryBean负责实例化一个Bean实例,ProxyFactoryBean负责为其他Bean实例创建代理实例。

*** ProxyFactoryBean类的常用属性**:

4.4 基于XML配置开发AspectJ

​ AspectJ是一个基于Java语言的AOP框架。使用AspectJ实现Spring AOP的方式有两种,一是基于XML配置开发AspectJ,二是基于注解开发AspectJ。

​ 基于XML配置开发AspectJ是指通过XML配置文件定义切面、切入点及通知,所有这些定义都必须在<aop:config>元素内。<aop:config>元素及其子元素如下表所示:

image-20191126124023343

各类型通知与目标方法的执行过程,具体过程如图所示:

image-20191126124203736

4.5 基于注解开发AspectJ

​ 基于注解开发AspectJ要比基于XML配置开发AspectJ便捷许多,所以在实际开发中推荐使用注解方法。

AspectJ注解,如下表所示:

image-20191126124359161

都说酒是陈的香。
原文地址:https://www.cnblogs.com/yihangjou/p/12005497.html