zoukankan      html  css  js  c++  java
  • SpringAOP深入学习

    ----------------------Spring AOP介绍------------------

    1.编程范式概念

    面向过程编程:C

    面向对象编程:c++,Java

    函数式编程

    事件驱动编程:GUI编程

    面向切面编程(AOP)

    2.AOP是什么

     (1)是一种编程范式,不是编程语言

    (2)解决特定问题,不能解决所有问题

    (3是OOP的补充,不是替代。

    3.AOP初衷:

    1.解决代码重复问题,增加代码的可读性与可维护性

    2.关注点分离,使程序员可以将更多的精力放在开发主要功能中。

    4.SpringAOP的两种实现方式

    1.基于xml配置的方式

    2.基于注解的方式(重点)

     

    5.AOP中名词:

    Aspect:切面(切入点+通知)

    join point:连接点,所有可以织入通知的方法

    point cut:切入点,需要|已经织入通知的方法

    advice:通知。需要增强的代码

    weaving:织入,动词,将通知应用到切点的过程

    target:目标对象

    proxy:代理对象

    注解AOP:

     表达式:

    •  选择器类型

    •  wildcards通配符:

    *  匹配任意数量的字符

    +  匹配定指定类及其子类

    ..  一般用于匹配任意数的子包或参数

    • operators:运算符

    && 与操作符

    ||    或操作符

    !     非操作符

    6.将通知织入切点的时机

    1.编译时期(Aspect)

    2.类加载时期(Aspect5+)

     3.运行时期(Spring AOP)

      从静态代理到动态代理(基于接口的代理与基于继承的代理)。

       

     ---------------------------Spring AOP实战--------------------

    1.基于xml配置方式的AOP实现

     1.编写通知类:(通过反射精确的定位到每个方法可以做一些详细的处理,两种方式定位到方法)

    package cn.qlq.XMLaspect;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.reflect.MethodSignature;
    
    //xml切面
    public class MyAdvice {
        // 前置通知
        public void before(JoinPoint joinPoint) throws Exception {
            System.out.println("---------------前置通知开始~~~~~~~~~~~");
            // 获取到类名
            String targetName = joinPoint.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到方法名
            String methodName = joinPoint.getSignature().getName();
            System.out.println("增强的方法是:" + methodName);
            // 获取到参数
            Object[] parameter = joinPoint.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取字节码对象
            Class<?> targetClass = Class.forName(targetName);
            // 获取所有的方法
            Method[] methods = targetClass.getMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == parameter.length) {
                        System.out.println("找到这个方法");
                        //处理一些业务逻辑
                        break;
                    }
                }
            }
            System.out.println("---------------前置通知结束~~~~~~~~~~~");
        }
    
        // 后置通知(异常发生后不会调用)
        public void afterRunning() {
            System.out.println("这是后置通知(异常发生后不会调用)");
        }
    
        // 环绕通知(推荐下面这种方式获取方法)
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("----------------环绕通知之前 的部分----------------");
            // 获取到类名
            String targetName = pjp.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到参数
            Object[] parameter = pjp.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取到方法签名,进而获得方法
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            System.out.println("增强的方法名字是:" + method.getName());
            //处理一些业务逻辑
            
            
            // 获取参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.println("参数类型是:" + parameterTypes.toString());
            
            //让方法执行(proceed是方法的返回结果,可以针对返回结果处理一下事情)
            System.out.println("--------------方法开始执行-----------------");
            Object proceed = pjp.proceed();
            
            //环绕通知之后的业务逻辑部分
            System.out.println("----------------环绕通知之后的部分----------------");
            return proceed;
        }
        // 异常通知
        public void afterException() {
            System.out.println("这是异常通知(发生异常后调用)~~~~~~~~~~~");
        }
    
        // 最终通知(发生异常也会在最终调用)
        public void after() {
            System.out.println("这是后置通知(发生异常也会在最终调用)");
        }
    }

    2.编写service接口和实现类(需要被增强的类)

    • UserService.java
    package cn.qlq.service;
    
    import java.sql.SQLException;
    
    public interface UserService {
    
        void save();
        void save(String userId)throws Exception;
        void update();
        void delete();
        void find();
    }
    • UserServiceImpl.java
    package cn.qlq.service;
    
    import org.springframework.stereotype.Service;
    
    @Service("userService")
    public class UserServiceImpl implements UserService {
    
        @Override
        public void save() {
            System.out.println("保存用户。。。无参数");
        }
    
        @Override
        public void save(String userId) throws Exception {
            int i = 1 / 0;
            System.out.println("保存用户.....(有参数)");
        }
    
        @Override
        public void update() {
            System.out.println("更新用户....");
        }
    
        @Override
        public void delete() {
            System.out.println("删除用户....");
        }
    
        @Override
        public void find() {
            System.out.println("查找用户");
        }
    }

    3.xml配置切入点+通知形成切面

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
        <!-- 1.首先导入aop约束,在beans元素添加命名空间 -->
        <context:component-scan base-package="cn.qlq.service"></context:component-scan>
        <!-- 3.配置通知对象 -->
        <bean name="advice" class="cn.qlq.XMLaspect.MyAdvice"></bean>
        <!-- 4.通知对象织入目标对象 .expression:切入点表达式。id:切入点名字
        public void cn.qlq.service.UserServiceImpl.save()    对指定类型指定返回值的空参方法进行增强
        void cn.qlq.service.UserServiceImpl.save()          public可以省去,默认public
        * cn.qlq.service.UserServiceImpl.save()        对返回类型不做要求,可以对任何返回类型织入
        * cn.qlq.service.UserServiceImpl.*()        对返回类型不做要求,对方法名字不做要求
        * cn.qlq.service.UserServiceImpl.*(..)        对返回类型不做要求,对方法名字不做要求,对参数也不做要求
        * cn.qlq.service.*ServiceImpl.*(..)        对service包下所有以ServiceImpl结尾的类中的任意参数的任意方法增强
        * cn.qlq.service..*ServiceImpl.*(..)        与上面不同的是对service包的子包也要进行增强(一般不用)    -->
    <aop:config>
        <!-- 配置切点 -->
       <aop:pointcut expression="execution(* cn.qlq.service.*ServiceImpl.*(..))" id="pc"/>
       <!-- 将通知织入切点形成切面 -->
       <aop:aspect ref="advice">
               <aop:before method="before" pointcut-ref="pc"/>
               <aop:after-returning method="afterRunning" pointcut-ref="pc"/>
               <aop:after method="after" pointcut-ref="pc"/>
               <aop:around method="around" pointcut-ref="pc"/>
               <aop:after-throwing method="afterException" pointcut-ref="pc"/>
       </aop:aspect>
    </aop:config>
    
    </beans>

    4.编写测试类:

    package cn.qlq.test;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import cn.qlq.service.UserService;
    
    //帮我们创建容器
    @RunWith(SpringJUnit4ClassRunner.class)
    //指定创建容器时使用的配置文件
    @ContextConfiguration("classpath:cn/qlq/XMLaspect/applicationContext.xml")
    public class SpringXMLAopTest {
    
        @Resource(name="userService")
        private UserService us;
        
        @Test
        public void fun1() throws Exception{
            us.save("111");
        }
        
    }

    结果:

    ---------------前置通知开始~~~~~~~~~~~
    代理的类是:cn.qlq.service.UserServiceImpl
    增强的方法是:save
    传入的参数是:[111]
    找到这个方法
    ---------------前置通知结束~~~~~~~~~~~
    ----------------环绕通知之前 的部分----------------
    代理的类是:cn.qlq.service.UserServiceImpl
    传入的参数是:[111]
    增强的方法名字是:save
    参数类型是:[Ljava.lang.Class;@5eebd82d
    --------------方法开始执行-----------------
    保存用户.....(有参数)
    这是后置通知(异常发生后不会调用)
    这是后置通知(发生异常也会在最终调用)
    ----------------环绕通知之后的部分----------------

    现在在程序中抛出一个异常,查看执行结果

        @Override
        public void save(String userId) throws Exception {
            int i = 1 / 0;
            System.out.println("保存用户.....(有参数)");
        }

    结果:

    ---------------前置通知开始~~~~~~~~~~~
    代理的类是:cn.qlq.service.UserServiceImpl
    增强的方法是:save
    传入的参数是:[111]
    找到这个方法
    ---------------前置通知结束~~~~~~~~~~~
    ----------------环绕通知之前 的部分----------------
    代理的类是:cn.qlq.service.UserServiceImpl
    传入的参数是:[111]
    增强的方法名字是:save
    参数类型是:[Ljava.lang.Class;@7022c24e
    --------------方法开始执行-----------------
    这是后置通知(发生异常也会在最终调用)
    这是异常通知(发生异常后调用)~~~~~~~~~~~

    通知执行顺序:

    (1)没有异常(不会走异常通知)

      前置通知->环绕通知之前部分->后置通知(异常不调用)->最终通知(异常也会调用)->环绕通知之后的部分

    (2)有异常(不会走环绕通知的后半部分和后置通知)

      前置通知->环绕通知之前部分->最终通知(异常也会调用)->异常通知

    2.基于注解方式的AOP实现(重点)

     1.applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p" xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
        <!-- 1.开启注解AOP -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
        <!-- 2.配置扫描的包 -->
        <context:component-scan base-package="cn.qlq"></context:component-scan>
    </beans>

    2.通知类:

    MyAdvice.java 
    package cn.qlq.annotationAOP;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    @Aspect // 表示该类是一个通知类
    @Component // 交给spring管理
    public class MyAdvice {
    
        // 定义一个空方法,借用其注解抽取切点表达式
        @Pointcut("execution(* cn.qlq.service.*ServiceImpl.*(..))")
        public void pc() {
        }
    
        // 前置通知
        @Before("MyAdvice.pc()")
        public void before(JoinPoint joinPoint) throws Exception {
            System.out.println("---------------前置通知开始(注解)~~~~~~~~~~~");
            // 获取到类名
            String targetName = joinPoint.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到方法名
            String methodName = joinPoint.getSignature().getName();
            System.out.println("增强的方法是:" + methodName);
            // 获取到参数
            Object[] parameter = joinPoint.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取字节码对象
            Class<?> targetClass = Class.forName(targetName);
            // 获取所有的方法
            Method[] methods = targetClass.getMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == parameter.length) {
                        System.out.println("找到这个方法");
                        break;
                    }
                }
            }
            System.out.println("---------------前置通知结束(注解)~~~~~~~~~~~");
        }
    
        // 后置通知(异常发生后不会调用)
        @AfterReturning("MyAdvice.pc()")
        public void afterRunning() {
            System.out.println("这是后置通知(异常发生后不会调用)。。。。(注解)");
        }
    
        // 环绕通知
        @Around("MyAdvice.pc()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("----------------环绕通知之前 的部分(注解)----------------");
            // 获取到类名
            String targetName = pjp.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到参数
            Object[] parameter = pjp.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取到方法签名,进而获得方法
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            System.out.println("增强的方法名字是:" + method.getName());
            // 获取参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.println("参数类型是:" + parameterTypes.toString());
            
            //让方法执行(proceed是拦截的方法的执行返回值,可以针对返回值做一些处理)
            System.out.println("--------------方法开始执行-----------------");
            Object proceed = pjp.proceed();
            
            //环绕通知之后的业务逻辑部分
            System.out.println("----------------环绕通知之后的部分(注解)----------------");
            return proceed;
        }
    
        // 异常通知
        @AfterThrowing("MyAdvice.pc()")
        public void afterException() {
            System.out.println("这是异常通知(发生异常后调用)~~~~~~~~~~~(注解)");
        }
    
        // 最终通知(发生异常也会在最终调用)
        @After("MyAdvice.pc()")
        public void after() {
            System.out.println("这是最终通知(发生异常也会在最终调用)........(注解)");
        }
    }
     

    3.UserService.java与UserServiceImpl.java同上

    4.测试类:

    package cn.qlq.test;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import cn.qlq.service.UserService;
    
    //帮我们创建容器
    @RunWith(SpringJUnit4ClassRunner.class)
    //指定创建容器时使用的配置文件
    @ContextConfiguration("classpath:cn/qlq/annotationAOP/applicationContext.xml")
    public class SpringAnnotationAopTest {
    
        @Resource(name="userService")
        private UserService us;
        
        @Test
        public void fun1(){
            us.save("111");
        }
        
    }

    结果:

    ----------------环绕通知之前 的部分(注解)----------------
    代理的类是:cn.qlq.service.UserServiceImpl
    传入的参数是:[111]
    增强的方法名字是:save
    参数类型是:[Ljava.lang.Class;@61d60df3
    --------------方法开始执行(注解)------------------
    ---------------前置通知开始(注解)~~~~~~~~~~~
    代理的类是:cn.qlq.service.UserServiceImpl
    增强的方法是:save
    传入的参数是:[111]
    找到这个方法
    ---------------前置通知结束(注解)~~~~~~~~~~~
    保存用户.....(有参数)
    ----------------环绕通知之后的部分(注解)-----------------
    这是最终通知(发生异常也会在最终调用)........(注解)
    这是后置通知(异常发生后不会调用)。。。。(注解)

    注意,为了抽取切点表达式,我们将上面的通知类改为如下:

    package cn.qlq.annotationAOP;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    @Aspect // 表示该类是一个通知类
    @Component // 交给spring管理
    public class MyAdvice {
    
        // 定义一个空方法,借用其注解抽取切点表达式
        @Pointcut("execution(* cn.qlq.service.*ServiceImpl.*(..))")
        public void pc() {
        }
    
        // 前置通知(下面两种方式均可以)
        // @Before("MyAdvice.pc()")
        @Before("pc()")
        public void before(JoinPoint joinPoint) throws Exception {
            System.out.println("---------------前置通知开始(注解)~~~~~~~~~~~");
            // 获取到类名
            String targetName = joinPoint.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到方法名
            String methodName = joinPoint.getSignature().getName();
            System.out.println("增强的方法是:" + methodName);
            // 获取到参数
            Object[] parameter = joinPoint.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取字节码对象
            Class<?> targetClass = Class.forName(targetName);
            // 获取所有的方法
            Method[] methods = targetClass.getMethods();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == parameter.length) {
                        System.out.println("找到这个方法");
                        break;
                    }
                }
            }
            System.out.println("---------------前置通知结束(注解)~~~~~~~~~~~");
        }
    
        // 后置通知(异常发生后不会调用)
        @AfterReturning("MyAdvice.pc()")
        public void afterRunning() {
            System.out.println("这是后置通知(异常发生后不会调用)。。。。(注解)");
        }
    
        // 环绕通知
        @Around("MyAdvice.pc()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
            System.out.println("----------------环绕通知之前 的部分(注解)----------------");
            // 获取到类名
            String targetName = pjp.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到参数
            Object[] parameter = pjp.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取到方法签名,进而获得方法
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            System.out.println("增强的方法名字是:" + method.getName());
            // 处理一些业务逻辑
    
            // 获取参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.println("参数类型是:" + parameterTypes.toString());
    
            // 让方法执行(proceed是方法的返回结果,可以针对返回结果做一些处理)
            System.out.println("--------------方法开始执行(注解)------------------");
            Object proceed = pjp.proceed();
    
            // 环绕通知之后的业务逻辑部分
            System.out.println("----------------环绕通知之后的部分(注解)-----------------");
            return proceed;
        }
    
        // 异常通知
        @AfterThrowing("MyAdvice.pc()")
        public void afterException() {
            System.out.println("这是异常通知(发生异常后调用)~~~~~~~~~~~(注解)");
        }
    
        // 最终通知(发生异常也会在最终调用)
        @After("MyAdvice.pc()")
        public void after() {
            System.out.println("这是最终通知(发生异常也会在最终调用)........(注解)");
        }
    }

    现在serviceImpl模拟抛出异常:

        @Override
        public void save(String userId) throws Exception {
            int i = 1 / 0;
            System.out.println("保存用户.....(有参数)");
        }

    结果:

    ----------------环绕通知之前 的部分(注解)----------------
    代理的类是:cn.qlq.service.UserServiceImpl
    传入的参数是:[111]
    增强的方法名字是:save
    参数类型是:[Ljava.lang.Class;@54b2a02f
    --------------方法开始执行(注解)------------------
    ---------------前置通知开始(注解)~~~~~~~~~~~
    代理的类是:cn.qlq.service.UserServiceImpl
    增强的方法是:save
    传入的参数是:[111]
    找到这个方法
    ---------------前置通知结束(注解)~~~~~~~~~~~
    这是最终通知(发生异常也会在最终调用)........(注解)
    这是异常通知(发生异常后调用)~~~~~~~~~~~(注解)

    总结:

    1.注解解释

      @Aspect:指明当前类是通知类

      @Before:前置通知

      @After:最终通知,发生异常也会调用,方法执行完之后执行

      @AfterReturning:后置通知(异常发生不会调用),方法成功执行之后。

      @Around:环绕通知

      @AfterThrowing:异常通知,抛出异常之后

      @Pointcut:抽取切点表达式,便于修改切点表达式

    2.通知执行顺序(顺序和xml方式有点不一样)

      没异常:(不会执行异常通知)

         环绕通知之前部分->前置通知->环绕通知之后部分->最终通知->后置通知  

      有异常(不会执行后置通知和环绕通知后半部分)

        环绕通知之前部分->前置通知->最终通知->异常通知 

     

    3.注解AOP几种切点表达式选择的使用:

    1.within("xxx")表达式

        // 匹配UserServiceImpl下面的所有方法
        @Pointcut("within(cn.qlq.service.UserServiceImpl)")
        public void pc() {
        }
        // 匹配cn.qlq包及其子包下面所有类的所有方法
        @Pointcut("within(cn.qlq..*)")
        public void pc() {
        }

     

    2.对象匹配:

    • 1.this

        // 匹配AOP对象的目标对象为指定类型的方法,即cn.qlq.service.UserService的AOP代理对象的方法
        @Pointcut("target(cn.qlq.service.UserService)")
        public void pc() {
        }
    • 2.target

        // 匹配实现UserService的目标对象,而不是代理对象,这里就是UserService的方法
        @Pointcut("target(cn.qlq.service.UserService)")
        public void pc() {
        }
    • 3.bean

        // 匹配注入到spring中所有以Service结尾的类的方法
        @Pointcut("bean(*Service)")
        public void pc() {
        }

    3.参数匹配

        // 匹配所有以方法名save开头且只有一个String类型参数的方法
        @Pointcut("execution(* *..save*(String))")
        public void pc() {
        }
        // 匹配所有只有一个String类型参数的方法
        @Pointcut("args(String)")
        public void pc() {
        }
        // 匹配所有以方法名save开头且第一个参数类型为String类型的方法
        @Pointcut("args(* *..save*(String,..))")
        public void pc() {
        }
        // 匹配第一个参数类型为String类型的方法
        @Pointcut("args(String,..)")
        public void pc() {
        }

    4.注解匹配:

    (1)自定义注解:

    package cn.qlq.annotationAOP;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnno {
        // 定义注解的属性,这不是方法,没有默认值就是必填属性
        String name();
    
        // 有默认值,就是可选属性
        int value() default 20;
    }

    (2)方法声明加注解

        @MyAnno(name="自定义注解")
        @Override
        public void save(String userId) throws Exception {
            System.out.println("保存用户.....(有参数)");
        }

    (3)利用注解表达式匹配上面表达式

    • 匹配方法的注解
        // 匹配方法标有cn.qlq.annotationAOP.MyAnno注解的方法
        @Pointcut("@annotation(cn.qlq.annotationAOP.MyAnno)")
        public void pc() {
        }
    • 匹配类的注解
        // 匹配标有cn.qlq.annotationAOP.MyAnno注解的类下面的方法,要求annotation的RetentionPolicy为CLASS
        @Pointcut("@whithin(cn.qlq.annotationAOP.MyAnno)")
        public void pc() {
        }
        // 匹配标有cn.qlq.annotationAOP.MyAnno注解的类下面的方法,要求annotation的RetentionPolicy为RUNTIME
        @Pointcut("@target(cn.qlq.annotationAOP.MyAnno)")
        public void pc() {
        }
    • 匹配参数的注解
        // 匹配传入的参数类型标有MyAnno注解的方法
        @Pointcut("@args(cn.qlq.annotationAOP.MyAnno)")
        public void pc() {
        }

    5.execution表达式

    格式:

       有问号的代表可以省略。

    演变过程:

        public void cn.qlq.service.UserServiceImpl.save()    对指定类型指定返回值的空参方法进行增强
        void cn.qlq.service.UserServiceImpl.save()          public可以省去,默认public
        * cn.qlq.service.UserServiceImpl.save()        对返回类型不做要求,可以对任何返回类型织入
        * cn.qlq.service.UserServiceImpl.*()        对返回类型不做要求,对方法名字不做要求
        * cn.qlq.service.UserServiceImpl.*(..)        对返回类型不做要求,对方法名字不做要求,对参数也不做要求
        * cn.qlq.service.*ServiceImpl.*(..)        对service包下所有以ServiceImpl结尾的类中的任意参数的任意方法增强
        * cn.qlq.service.*ServiceImpl.*(..) throws java.sql.SQLException;     对service包下所有以ServiceImpl结尾的类中的任意参数的任意方法且抛出的异常类型是SQLException的方法增强
        * cn.qlq.service..*ServiceImpl.*(..)        与上面不同的是对service包的子包也要进行增强(一般不用)

    4.五种通知详解:

    1.前置通知:@Before  

    查看源码:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Before {
        String value();
    
        String argNames() default "";
    }

      第一个value是切点表达式,第二个argName属性不太理解?前置通知可用于参数校验,比如通过下列方式获取参数:

        // 匹配传入的参数类型标有MyAnno注解的方法
        @Pointcut("@annotation(cn.qlq.annotationAOP.MyAnno)")
        public void pc() {
        }
    
        // 前置通知(下面两种方式均可以)
        // @Before("MyAdvice.pc()")
        @Before(value = "pc() && args(arg)")
        public void before(JoinPoint joinPoint,Object arg) throws Exception {
            System.out.println("---------------前置通知开始(注解)~~~~~~~~~~~");
            System.out.println("参数是:"+arg.toString());
            System.out.println("---------------前置通知结束(注解)~~~~~~~~~~~");
        }

    需要增强的方法:

        @MyAnno(name="自定义注解")
        @Override
        public boolean save(String userId) throws Exception {
            System.out.println("保存用户.....(有参数):"+userId);
            return true;
        }

    测试:

    package cn.qlq.test;
    
    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import cn.qlq.service.UserService;
    
    //帮我们创建容器
    @RunWith(SpringJUnit4ClassRunner.class)
    //指定创建容器时使用的配置文件
    @ContextConfiguration("classpath:cn/qlq/annotationAOP/applicationContext.xml")
    public class SpringAnnotationAopTest {
    
        @Resource(name="userService")
        private UserService us;
        
        @Test
        public void fun1() throws Exception{
            us.save("111");
        }
        
    }

    结果:

    ---------------前置通知开始(注解)~~~~~~~~~~~
    参数是:111
    ---------------前置通知结束(注解)~~~~~~~~~~~

    2.最终通知,方法执行完之后:@After

    查看源码:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface After {
        String value();
    
        String argNames() default "";
    }

    第一个value是切点表达式,第二个argName属性暂时不太理解?

    3.后置通知,方法成功执行之后:@AfterReturning

    查看源码:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface AfterReturning {
        String value() default "";
    
        String pointcut() default "";
    
        String returning() default "";
    
        String argNames() default "";
    }

    比上面多了一个returning,是方法的返回值,可以在返回通知中获取方法返回值,然后进行一些处理。例如:

        // 后置通知(异常发生后不会调用)
        @AfterReturning(value="MyAdvice.pc()",returning = "result")
        public void afterRunning(Object result) {
            System.out.println(result);//获取方法的返回值
            System.out.println("这是后置通知(异常发生后不会调用)。。。。(注解)");
        }

    4.环绕通知:@Around(一般有环绕通知就不需要其他四种通知)-----------重要

    查看源码:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Around {
        String value();
    
        String argNames() default "";
    }

    环绕通知需要有返回值,而且有异常通知一般不需要其它四种通知

    package cn.qlq.annotationAOP;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    @Aspect // 表示该类是一个通知类
    @Component // 交给spring管理
    public class MyAdvice {
    
        // 匹配传入的参数类型标有MyAnno注解的方法
        @Pointcut("@annotation(cn.qlq.annotationAOP.MyAnno)")
        public void pc() {
        }
    
        // 环绕通知
        @Around("MyAdvice.pc()")
        public Object around(ProceedingJoinPoint pjp){
            System.out.println("----------------环绕通知之前 的部分(相当于前置通知)----------------");
            // 获取到类名
            String targetName = pjp.getTarget().getClass().getName();
            System.out.println("代理的类是:" + targetName);
            // 获取到参数
            Object[] parameter = pjp.getArgs();
            System.out.println("传入的参数是:" + Arrays.toString(parameter));
            // 获取到方法签名,进而获得方法
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            System.out.println("增强的方法名字是:" + method.getName());
            // 处理一些业务逻辑
    
            // 获取参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            System.out.println("参数类型是:" + parameterTypes.toString());
    
            // 让方法执行
            System.out.println("--------------方法开始执行(注解)------------------");
            Object proceed=null;
            try {
                proceed = pjp.proceed();
                // 环绕通知之后的业务逻辑部分
                System.out.println("----------------环绕通知之后的部分(相当于后置通知AfterReturning)-----------------");
            } catch (Throwable e) {
                System.out.println("-------------环绕通知的异常部分(相当于异常通知AfterThrowing)--------------------------");
                e.printStackTrace();
            }finally {
                System.out.println("-------------环绕通知的最终部分部分(相当于最终通知After)--------------------------");
            }
    
    
            return proceed;
        }
    }

    结果:

    ----------------环绕通知之前 的部分(相当于前置通知)----------------
    代理的类是:cn.qlq.service.UserServiceImpl
    传入的参数是:[111]
    增强的方法名字是:save
    参数类型是:[Ljava.lang.Class;@1548a36c
    --------------方法开始执行(注解)------------------
    保存用户.....(有参数):111
    ----------------环绕通知之后的部分(相当于后置通知AfterReturning)-----------------
    -------------环绕通知的最终部分部分(相当于最终通知After)--------------------------

      

      如果我们在环绕通知验证完权限之后,如果权限不够我们可以不执行proceed = pjp.proceed();方法,也就是不执行对应的方法。可以灵活的控制方法的执行与否。

     

    5.异常通知: AfterThrowing

    查啊看源码:

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface AfterThrowing {
        String value() default "";
    
        String pointcut() default "";
    
        String throwing() default "";
    
        String argNames() default "";
    }

    5.SpringAOP代理的实现原理以及选择代理的方式参考: 

                              http://www.cnblogs.com/qlqwjy/p/7550609.html

    6.Spring多个AOP作用于同一目标对象采用的责任链模式

    0.简单的类图

    1.自己维护逻辑关系的责任链

    Handler.java 
    package cn.qlq.chai;
    
    
    /**
     * 
    * @author: qlq
    * @date :  2018年4月8日下午5:09:07
     */
    public abstract class Handler {
    
        private Handler sucessor;
    
        public Handler getSucessor() {
            return sucessor;
        }
    
        public void setSucessor(Handler sucessor) {
            this.sucessor = sucessor;
        }
    
        public void execute(){
            handleProcess();
            if(sucessor != null){
                sucessor.execute();
            }
        }
    
        protected abstract void handleProcess();
    }

    测试类:

    package cn.qlq.chai;
    
    
    /**
     * 
    * @author: qlq
    * @date :  2018年4月8日下午5:09:42
     */
    public class Client {
        static class HandlerA extends Handler{
            @Override
            protected void handleProcess() {
                System.out.println("handle by a");
            }
        }
        static class HandlerB extends Handler{
            @Override
            protected void handleProcess() {
                System.out.println("handle by b");
            }
        }
        static class HandlerC extends Handler{
            @Override
            protected void handleProcess() {
                System.out.println("handle by c");
            }
        }
    
        public static void main(String[] args){
            Handler handlerA = new HandlerA();
            Handler handlerB = new HandlerB();
            Handler handlerC = new HandlerC();
    
            handlerA.setSucessor(handlerB);
            handlerB.setSucessor(handlerC);
    
            handlerA.execute();
        }
    }

    结果:

    handle by a
    handle by b
    handle by c

    2.通过一个集合维护责任链:(类似于递归模式)

    ChainHandler.java

    package cn.qlq.chai;
    
    /**
     * 
     * @author: qlq
     */
    public abstract class ChainHandler {
    
        public void execute(Chain chain) {
            handleProcess();
            chain.proceed();
        }
    
        protected abstract void handleProcess();
    }

    Chain.java

    package cn.qlq.chai;
    
    import java.util.List;
    
    /**
     * 
     * @author: qlq
     */
    public class Chain {
    
        private List<ChainHandler> handlers;
    
        private int index = 0;
    
        public Chain(List<ChainHandler> handlers) {
            this.handlers = handlers;
        }
    
        public void proceed() {
            if (index >= handlers.size()) {
                return;
            }
            handlers.get(index++).execute(this);
        }
    }

    测试类:

    package cn.qlq.chai;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * 
     * @author: qlq
     */
    public class ChainClient {
        static class ChainHandlerA extends ChainHandler {
            @Override
            protected void handleProcess() {
                System.out.println("handle by chain a");
            }
        }
    
        static class ChainHandlerB extends ChainHandler {
            @Override
            protected void handleProcess() {
                System.out.println("handle by chain b");
            }
        }
    
        static class ChainHandlerC extends ChainHandler {
            @Override
            protected void handleProcess() {
                System.out.println("handle by chain c");
            }
        }
    
        public static void main(String[] args) {
            List<ChainHandler> handlers = Arrays.asList(new ChainHandlerA(), new ChainHandlerB(), new ChainHandlerC());
            Chain chain = new Chain(handlers);
            chain.proceed();
        }
    }

    结果:

    handle by a
    handle by b
    handle by c

    3.Spring的链式调用:(采用上面第二种方式)

        public Object proceed() throws Throwable {
            //    We start with an index of -1 and increment early.
            if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
                return invokeJoinpoint();
            }
    
            Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                // Evaluate dynamic method matcher here: static part will already have
                // been evaluated and found to match.
                InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
                if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                    return dm.interceptor.invoke(this);
                }
                else {
                    // Dynamic matching failed.
                    // Skip this interceptor and invoke the next in the chain.
                    return proceed();
                }
            }
            else {
                // It's an interceptor, so we just invoke it: The pointcut will have
                // been evaluated statically before this object was constructed.
                return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
            }
        }

    关于spring配置总结:

    <!-- 0.开启aop,对类代理使用cglib代理 -->
    <aop:config proxy-target-class="true"></aop:config>



    <!-- 1.开启注解AOP -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>


    <!-- 2.开启注解管理aop事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

      最后我靠着自己的理解写了一个注解AOP+注解的日志记录功能,参考我的另一篇博客:http://www.cnblogs.com/qlqwjy/p/8747476.html

  • 相关阅读:
    SQL SEREVR IO
    INTEL
    windows performance
    The DiskSpd Storage Performance Tool
    machine Learning
    NOSQL
    X64 Deep Dive
    Debugging and performance,ETW
    Disk Performance
    WCF transport-and-message-security
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/8729280.html
Copyright © 2011-2022 走看看