• Spring框架学习07——基于传统代理类的AOP实现


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

    1、通知类型

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

    (1)环绕通知

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

    (2)前置通知

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

    (3)后置返回通知

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

    (4)后置(最终)通知

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

    (5)异常通知

    异常通知(org.springframework.aop.ThrowsAdvice)是在方法抛出异常后实施增强,可应用于处理异常、记录日志等功能。

    (6)引入通知

    引入通知(org.springframework.aop.IntroductionInterceptor)是在目标类中添加一些新的方法和属性,可应用于修改目标类(增强类)。Spring AOP 只支持方法层面的增强,所以该类型的通知只作为了解即可。

    2、切面类型

    • Advisor:代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
    • PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类哪些方法
    • IntroductionAdvisor:代表引介切面,针对引介通知而使用切面

    3、ProxyFactoryBean

    ProxyFactoryBean是org.springframework.beans.factory.FactoryBean 接口的实现类,FactoryBean负责实例化一个Bean实例,ProxyFactoryBean负责为其他Bean实例创建代理实例。ProxyFactoryBean类的常用属性如下:

    • target:代理的目标对象
    • proxyInterfaces:代理需要实现的接口列表,如果是多个接口,可以使用以下格式赋值:<list> <value></value> </list>
    • InterceptorNames:需要织入目标的Advice
    • proxyTargetClass:是否对类代理而不是接口,默认为false,使用JDK动态代理;当为true时,使用CGLIB动态代理
    • singleton:返回的代理实例是否为单例,默认为true
    • optimize:当设置为true时强制使用CGLIB动态代理

    4、Advisor一般切面的实现

    在pom.xml中导入AOP联盟和Spring-aop的相关依赖

    <!-- AOP联盟依赖 -->
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>

    创建StuDao接口和StuDaoImpl实现类,示例代码如下:
    StuDao接口

    public interface StuDao {
        public void save();
        public void modify();
        public void delete();
    }

    StuDaoImpl实现类

    public class StuDaoImpl implements StuDao {
        @Override
        public void save() {
            System.out.println("保存");
        }
    
        @Override
        public void modify() {
            System.out.println("修改");
        }
    
        @Override
        public void delete() {
            System.out.println("删除");
        }
    }

    创建切面类MyBeforeAdvice,仅实现前置通知

    import org.springframework.aop.MethodBeforeAdvice;
    import java.lang.reflect.Method;
    
    public class MyBeforeAdvice implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println("前置增强========");
        }
    }

    在applicationContext.xml中配置切面并指定代理

    <!--配置目标类-->
    <bean id="stuDao" class="aop.StuDaoImpl"></bean>
    <!--配置前置通知类型-->
    <bean id="myBeforeAdvice" class="aop.MyBeforeAdvice"></bean>
    <!--Spring AOP 生成代理对象-->
    <bean id="stuDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--配置目标类-->
        <property name="target" ref="stuDao"></property>
        <!--代理实现的接口-->
        <property name="proxyInterfaces" value="aop.StuDao"></property>
        <!--采取拦截的名称-->
        <property name="interceptorNames" value="myBeforeAdvice"></property>
    </bean>

    创建测试类

    @Test
    public void demo(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        StuDao stuDao = (StuDao) app.getBean("stuDaoProxy");
        stuDao.save();
        stuDao.modify();
        stuDao.delete();
    }

    运行结果

    5、PointcutAdvisor切点切面的实现

    使用普通Advice作为切面,将对目标类所有方法进行拦截,不够灵活,在实际开发中常采用带切点的切面。
    常用PointcutAdvisor实现类:

    • DefaultPointcutAdvisor最常用的切面类型,它可以通过任意Pointcut和Advice组合定义切面;
    • JdkRegexpMethodPointcut构造正则表达式切点(推荐)

    创建StuDao接口和StuDaoImpl实现类,示例代码如下:
    StuDao接口

    public interface StuDao {
        public void save();
        public void modify();
        public void delete();
    }

    StuDaoImpl实现类

    public class StuDaoImpl implements StuDao {
        @Override
        public void save() {
            System.out.println("保存");
        }
    
        @Override
        public void modify() {
            System.out.println("修改");
        }
    
        @Override
        public void delete() {
            System.out.println("删除");
        }
    }

    下面创建一个实现环绕通知的切面类

    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class MyAspect implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation m) throws Throwable {
            //前置增强方法
            check();
            //执行目标方法
            Object obj = m.proceed();
            //后置增强方法
            log();
            return obj;
        }
    
        public void check(){
            System.out.println("模拟权限控制");
        }
        public void log(){
            System.out.println("模拟日志记录");
        }
    }

    在applicationContext.xml中配置切面并指定代理

    <!--配置目标类-->
     <bean id="stuDao" class="aop.StuDaoImpl"></bean>
     <!--配置通知-->
     <bean id="myAspect" class="aop.MyAspect"></bean>
     <!--配置带切入点的切面-->
     <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
         <!--pattern中配置正则表达式:.表示任意字符 *表示任意次数-->
         <property name="pattern" value=".*"></property>
         <property name="advice" ref="myAspect"></property>
     </bean>
     <!--Spring AOP 生成代理对象-->
     <bean id="stuDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
         <!--配置目标类-->
         <property name="target" ref="stuDao"></property>
         <!--代理实现的接口-->
         <property name="proxyInterfaces" value="aop.StuDao"></property>
         <!--采取拦截的名称-->
         <property name="interceptorNames" value="myAdvisor"></property>
     </bean>

    创建测试类

    @Test
    public void demo(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        StuDao stuDao = (StuDao) app.getBean("stuDaoProxy");
        stuDao.save();
        stuDao.modify();
        stuDao.delete();
    }

    运行结果

  • 相关阅读:
    Redis其他知识
    Mybatis的sql语句操作
    Redis
    mybatis插件原理
    mybatis工作原理
    Mybatis逆向工程
    mybatis缓存机制
    Mybatis查询
    zabbix api添加主机
    jenkins + bitbucket 实现 pr自动构建及build状态通知
  • 原文地址:https://www.cnblogs.com/jpwz/p/10567481.html
Copyright © 2020-2023  润新知