zoukankan      html  css  js  c++  java
  • 过滤器/拦截器/AOP----spring aop

    录:

    1、spring AOP介绍
    2、spring aop 术语
    3、spring手动代理--jdk动态代理
    4、spring手动代理--cglib字节码增强
    5、spring编写代理--半自动ProxyFactoryBean
    6、spring aop编程:全自动
    7、AspectJ介绍及切入点表达式
    8、AspectJ 通知类型
    9、AspectJ 基于xml
    10、AspectJ 基于注解
    11、基于注解进行 AOP 开发的demo
    12、自定义注解 + AOP 编程

    1、spring AOP介绍    <--返回目录
      AOP:Aspect Oriented Programming的缩写,面向切面编程
      使用AOP的好处:利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性;AOP采取【横向抽取机制】,取代了传统纵向继承体系重复性代码
      AOP经典应用:事务管理、性能监视、安全检查、缓存、日志等。
      Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。
      AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入
          
      aop底层将采用代理机制进行实现
        - 接口 + 实现类 :spring采用 jdk 的动态代理Proxy。
        - 实现类:spring 采用 cglib字节码增强。

    2、spring aop 术语    <--返回目录
      1) target:目标类, 需要被代理的类。例如:UserService
      2) Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
      3) PointCut 切入点:已经被增强的连接点。例如:addUser()
      4) advice 通知/增强,增强代码。例如:after、before
      5) Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
      6) proxy 代理类
      7) Aspect(切面): 是切入点pointcut和通知advice的结合
            一个线是一个特殊的面。一个切入点和一个通知,组成成一个特殊的面。

    3、spring手动代理--jdk动态代理    <--返回目录

      手动代理:自己打代码完成一个切面编程
      jdk动态代理    
        - JDK动态代理,是对"装饰者"设计模式的简化。使用前提:必须有接口
        1) 目标类:接口 + 实现类
        2) 切面类:用于存通知,切面类取名MyAspect
        3) 工厂类:编写工厂生成代理

      目标类:接口+实现类(代码省略)

    public interface UserService {
        public void addUser();  // 连接点
        public void updateUser(); // 连接点
        public void deleteUser(); // 连接点
    }

      切面类(里面的方法都是通知)

    public class MyAspect {    
        public void before(){ // 通知
            System.out.println("鸡首");
        }        
        public void after(){ // 通知
            System.out.println("牛后");
        }
    }

      生成代理的工厂

    public class MyBeanFactory {    
        public static UserService createService(){
            //1 目标类
            final UserService userService = new UserServiceImpl();
            //2 切面类
            final MyAspect myAspect = new MyAspect();
            /* 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面
             *     Proxy.newProxyInstance
             *         参数1:loader ,类加载器,动态代理类 运行时创建,任何类都需要类加载器将其加载到内存。
             *             一般情况:当前类.class.getClassLoader();
             *                     目标类实例.getClass().get...
             *         参数2:Class[] interfaces 代理类需要实现的所有接口
             *             方式1:目标类实例.getClass().getInterfaces()  ;注意:只能获得自己接口,不能获得父元素接口
             *             方式2:new Class[]{UserService.class}   
             *             例如:jdbc 驱动  --> DriverManager  获得接口 Connection
             *         参数3:InvocationHandler  处理类,接口,必须进行实现类,一般采用匿名内部
             *             提供 invoke 方法,代理类的每一个方法执行时,都将调用一次invoke
             *                 参数31:Object proxy :代理对象
             *                 参数32:Method method : 代理对象当前执行的方法的描述对象(反射)
             *                     执行方法名:method.getName()
             *                     执行方法:method.invoke(对象,实际参数)
             *                 参数33:Object[] args :方法实际参数
             * 
             */
            UserService proxyService = (UserService)Proxy.newProxyInstance(
                                    MyBeanFactory.class.getClassLoader(), 
                                    userService.getClass().getInterfaces(), 
                                    new InvocationHandler() {
                                        
                                        @Override
                                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                            
                                            //前执行
                                            myAspect.before();
                                            
                                            //执行目标类的方法
                                            Object obj = method.invoke(userService, args);
                                            
                                            //后执行
                                            myAspect.after();
                                            
                                            return obj;
                                        }
                                    });
            
            return proxyService;
        }
    }

      测试:

    UserService userService = MyBeanFactory.createService();
    userService.addUser();

    4、spring手动代理--cglib字节码增强    <--返回目录

      cglib字节码增强:没有接口,只有实现类。采用字节码增强框架cglib,在运行时创建目标类的子类,从而对目标类进行增强。
      导入jar包:
        核心:hibernate-distribution-3.6.10.Finallibytecodecglibcglib-2.2.jar
        依赖:struts-2.3.15.3appsstruts2-blankWEB-INFlibasm-3.3.jar
        spring-core..jar 已经整合以上两个内容

      目标类:

    public class UserServiceImpl {
        public void addUser() {
            System.out.println("添加方法");
        }
        public void updateUser() {
            System.out.println("修改方法");
        }
        public void deleteUser() {
            System.out.println("删除方法");
        }
    }

      切面类:

    public class MyAspect {
        public void before(){
            System.out.println("鸡首");
        }        
        public void after(){
            System.out.println("牛后");
        }
    }

      代理工厂

    public class MyBeanFactory {
        public static UserServiceImpl createService() {
            // 1 目标类
            final UserServiceImpl userService = new UserServiceImpl();
            // 2 切面类
            final MyAspect myAspect = new MyAspect();
    
            // 3 代理类,采用cglib,底层创建目标类的子类
            // 3.1 核心类
            Enhancer enhancer = new Enhancer();
            // 3.2 确定父类
            enhancer.setSuperclass(userService.getClass());
            // 3.3 设置回调函数,MethodInterceptor接口等效与jdk Invocationhandler接口
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object proxy, Method method, Object[] args,
                        MethodProxy methodProxy) throws Throwable {
    
                    // 前执行
                    myAspect.before();
    
                    // 执行目标类的方法
                    Object obj = method.invoke(userService, args);
                    // methodProxy.invokeSuper(proxy,args);//执行代理类的父类的方法,也即是执行目标类,因为目标类就是代理类的父类
                    
                    // 后执行
                    myAspect.after();
                    return obj;
                }
            });
            // 3.4 创建代理
            UserServiceImpl proxyService = (UserServiceImpl) enhancer.create();
            return proxyService;
        }
    }

    5、spring编写代理--半自动    <--返回目录

      所谓半自动:即使用 spring 提供的代理工厂,我们提供目标类(代理对象)和 通知(增强)。

      既然是我们提供通知,那么 spring 如何知道哪个通知是前置,哪个是后置呢?所以,aop联盟提供了一个关于通知类型的规范(即一组接口)。

      aop联盟通知类型:Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
        - 前置通知 org.springframework.aop.MethodBeforeAdvice,在目标方法执行前实施增强
        - 后置通知 org.springframework.aop.AfterReturningAdvice,在目标方法执行后实施增强
        - 环绕通知 org.aopalliance.intercept.MethodInterceptor,在目标方法执行前后实施增强
        - 异常抛出通知 org.springframework.aop.ThrowsAdvice,在方法抛出异常后实施增强,方法包括目标方法代码和通知方法代码
        - 引介通知 org.springframework.aop.IntroductionInterceptor,在目标类中添加一些新的方法和属性

      环绕通知,必须手动执行目标方法       

    try{
       //前置通知
       //执行目标方法
       //后置通知
    } catch(){
       //抛出异常通知
    }

      需要导入的jar:
            - 核心4+1:beans, context, core, express + commons-logging
            - aop编程:aop联盟(规范) com.springsource.org.aopalliance-1.0.0.jar,spring-aop(实现) spring-aop-3.2.0.RELEASE.jar

      目标类:接口UserService和实现类UserServiceImpl

      切面类

    /* 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
       采用"环绕通知"MethodInterceptor
     */
    public class MyAspect implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation mi)throws Throwable {
            System.out.println("前");
            
            //手动执行目标方法
            Object obj = mi.proceed();
            
            System.out.println("后");
            return obj;
        }
    }
    View Code

      applicationContext.xml配置文件

    <!--创建目标类-->
    <bean id="userServiceId" class="cn.oy.service.UserServiceImpl"/>
    <!--创建切面类-->
    <bean id="myAspectId" class="cn.oy.aspect.MyAspect"/>
    <!--创建代理类
        使用工厂FactoryBean,底层调用getObject()方法返回特殊bean
        ProxyFactoryBean:用于创建代理工厂bean,生成特殊代理对象
        interfaces:指定接口们,通过<array>可以设置多个值,只有一个值时,<property name="interfaces" value=""/>
        target:指定目标类
        interceptorNames:切面类的名称。类型是字符串数组,如果只有一个值,<property name="interceptorNames" value="myAspectId"/>
        
        底层机制:如果目标类有接口,采用jdk动态代理
                  如果没有接口,采用cglib
                  如果声明optimize = true ,无论是否有接口,都使用cglib
    -->
    <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces">
            <array>
                <value>cn.oy.dao.UserService</value>
            </array>
        </property>
        <property name="target" ref="userServiceId"></property>
        <property name="interceptorNames" value="myAspectId"></property>
    </bean>
    View Code

      测试

    String xmlPath = "applicationContext.xml";
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
    UserService userService = (UserService) applicationContext.getBean("proxyServiceId");//获取代理类
    userService.addUser();
    View Code

    6、spring aop编程:全自动    <--返回目录

      全自动:从spring容器获得目标类,如果配置了aop,spring将自动生成代理。从而我们就拿到了目标类的代理对象。

      需要导入的jar:
            - 核心4+1:beans, context, core, express + commons-logging
            - aop编程:aop联盟(规范) com.springsource.org.aopalliance-1.0.0.jar,spring-aop(实现) spring-aop-3.2.0.RELEASE.jar

      - 要确定目标类,要使用aspectj切入点表达式 com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

      目标类:接口UserService和实现类UserServiceImpl

      切面类

    /**
     * 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
     * * 采用"环绕通知" MethodInterceptor
     */
    public class MyAspect implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            
            System.out.println("前3");
            
            //手动执行目标方法
            Object obj = mi.proceed();
            
            System.out.println("后3");
            return obj;
        }
    }
    View Code

      spring 配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           【xmlns:aop="http://www.springframework.org/schema/aop"】
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               【http://www.springframework.org/schema/aop 
                               http://www.springframework.org/schema/aop/spring-aop.xsd】">
        <!-- 1 创建目标类 -->
        <bean id="userServiceId" class="com.oy.aop.UserServiceImpl"></bean>
        <!-- 2 创建切面类(通知) -->
        <bean id="myAspectId" class="com.oy.aop.MyAspect"></bean>
        <!-- 3 aop编程 
            3.1 导入命名空间
            3.2 使用 <aop:config>进行配置
                    proxy-target-class="true" 声明时使用cglib代理
                <aop:pointcut> 切入点 ,从目标对象获得具体方法
                <aop:advisor> 特殊的切面,只有一个通知 和 一个切入点
                    advice-ref 通知引用
                    pointcut-ref 切入点引用
            3.3 切入点表达式
                execution(* com.oy.aop.*.*(..))
                选择方法  返回值任意   包          类名任意   方法名任意   参数任意
            
        -->
        <aop:config proxy-target-class="true">
            <aop:pointcut expression="execution(* com.oy.aop.*.*(..))" id="myPointCut"/>
            <aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/>
        </aop:config>
    </beans>
    View Code

      测试

    String xmlPath = "applicationContext.xml";
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
    
    //获得目标类(由于配置了aop,spring将自动生成代理)
    UserService userService = (UserService) applicationContext.getBean("userServiceId");
    userService.addUser();
    View Code

    7、AspectJ介绍及切入点表达式        <--返回目录
        * AspectJ是一个基于Java语言的AOP框架
        * 切入点表达式(掌握)  
            - 1.execution()用于描述方法【掌握】
                语法:execution(修饰符  返回值  包.类.方法名(参数) throws异常)
                修饰符,一般省略
                    public        公共方法
                    *            任意
                返回值,不能省略
                    void            返回没有值
                    String        返回值字符串
                    *             任意
                包,[省略]
                    com.oy.crm            固定包
                    com.oy.crm.*.service    crm包下面子包任意 (例如:com.oy.crm.staff.service)
                    com.oy.crm..            crm包下面的所有子包(含自己)
                    com.oy.crm.*.service..    crm包下面任意子包,固定目录service,service目录任意包
                类,[省略]
                    UserServiceImpl            指定类
                    *Impl                    以Impl结尾
                    User*                    以User开头
                    *                        任意
                方法名,不能省略
                    addUser                    固定方法
                    add*                        以add开头
                    *Do                        以Do结尾
                    *                        任意
                (参数)
                    ()                        无参
                    (int)                        一个整型
                    (int ,int)                    两个
                    (..)                        参数任意
                throws ,可省略,一般不写。

                综合1
                    execution(* com.oy.crm.*.service..*.*(..))
                综合2
                    <aop:pointcut expression="execution(* com.oy.*WithCommit.*(..)) ||
                                          execution(* com.oy.*Service.*(..))" id="myPointCut"/>
            
            - 2.within:匹配包或子包中的方法(了解)
                within(com.oy.aop..*)
            - 3.this:匹配实现接口的代理对象中的方法(了解)
                this(com.oy.aop.user.UserDAO)
            - 4.target:匹配实现接口的目标对象中的方法(了解)
                target(com.oy.aop.user.UserDAO)
            - 5.args:匹配参数格式符合标准的方法(了解)
                args(int,int)
            - 6.bean(id)  对指定的bean所有的方法(了解)
                bean('userServiceId')

    8、AspectJ 通知类型     <--返回目录         
      aop联盟定义通知类型,具有特性接口,必须实现,从而确定方法名称。
      aspectj 通知类型,只定义类型名称,以及方法格式。

    before:前置通知(应用:各种校验),在方法执行前执行,如果通知抛出异常,阻止方法运行
    afterReturning:后置通知(应用:常规数据处理),方法正常返回后执行,如果方法中抛出异常,通知无法执行;必须在方法执行后才执行,所以可以获得方法的返回值。
    around:环绕通知(应用:十分强大,可以做任何事情【掌握】),方法执行前后分别执行,可以阻止方法的执行;必须手动执行目标方法
    afterThrowing:抛出异常通知(应用:包装异常信息),方法抛出异常后执行,如果方法没有抛出异常,无法执行
    after:最终通知(应用:清理现场),方法执行完毕后执行,无论方法中是否出现异常

      环绕通知

    try{
        //前置:before
        //手动执行目标方法
        //后置:afterReturning
    } catch(){
        //抛出异常 afterThrowing
    } finally{
        //最终 after
    }

    9、AspectJ 基于xml    <--返回目录

      使用AspectJ实现切面编程,需要导入的:

    - 核心4+1:beans, context, core, express + commons-logging
    - aop联盟(规范) com.springsource.org.aopalliance-1.0.0.jar
    - spring-aop(实现) spring-aop-3.2.0.RELEASE.jar
    - aspect 规范   com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
    - spring aspect 实现  spring-aspects-3.2.0.RELEASE.jar

      步骤:
      1) 目标类:接口 + 实现
      2) 切面类:编写多个通知,采用aspectj 通知名称任意(方法名任意)
      3) aop编程,将通知应用到目标类

      目标类:接口和实现类

    public interface UserService {
        public void addUser();
        public void updateUser();
        public void deleteUser();
    }
    public class UserService {
        public void addUser(){
            System.out.println("addUser");
        }
        public void updateUser(){
            System.out.println("updateUser");
        }
        public void deleteUser(){
            System.out.println("deleteUser");
        }
    }
    View Code

      切面类

    public class MyAspect {
        public void myBefore(JoinPoint joinPoint){
            System.out.println("前置通知 : " + joinPoint.getSignature().getName());
        }
        
        public void myAfterReturning(JoinPoint joinPoint,Object ret){
            System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
        }
        
        public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
            System.out.println("前");
            //手动执行目标方法
            Object obj = joinPoint.proceed();
            
            System.out.println("后");
            return obj;
        }
        
        public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
            System.out.println("抛出异常通知 : " + e.getMessage());
        }
        
        public void myAfter(JoinPoint joinPoint){
            System.out.println("最终通知");
        }
    }

      spring 配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
                               http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/aop 
                               http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!-- 1 创建目标类 -->
        <bean id="userServiceId" class="com.oy.d_aspect.a_xml.UserServiceImpl"></bean>
        <!-- 2 创建切面类(通知) -->
        <bean id="myAspectId" class="com.oy.d_aspect.a_xml.MyAspect"></bean>
        
        <!-- 3 aop编程 
            <aop:aspect> 将切面类 声明“切面”,从而获得通知(方法)
                ref 切面类引用
            <aop:pointcut> 声明一个切入点,所有的通知都可以使用。
                expression 切入点表达式
                id 名称,用于其它通知引用
        -->
        <aop:config>
            <aop:aspect ref="myAspectId">
                <aop:pointcut expression="execution(* com.oy.d_aspect.a_xml.UserServiceImpl.*(..))" id="myPointCut"/>
                
                <!-- 3.1 前置通知 
                    <aop:before method="" pointcut="" pointcut-ref=""/>
                        method : 通知,及方法名
                        pointcut :切入点表达式,此表达式只能当前通知使用。
                        pointcut-ref : 切入点引用,可以与其他通知共享切入点。
                    通知方法格式:public void myBefore(JoinPoint joinPoint){
                        参数1:org.aspectj.lang.JoinPoint  用于描述连接点(目标方法),获得目标方法名等
                    例如:
                <aop:before method="myBefore" pointcut-ref="myPointCut"/>
                -->
                
                <!-- 3.2后置通知  ,目标方法后执行,获得返回值
                    <aop:after-returning method="" pointcut-ref="" returning=""/>
                        returning 通知方法第二个参数的名称
                    通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){
                        参数1:连接点描述
                        参数2:类型Object,参数名 returning="ret" 配置的
                    例如:
                <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
                -->
                
                <!-- 3.3 环绕通知 
                    <aop:around method="" pointcut-ref=""/>
                    通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
                        返回值类型:Object
                        方法名:任意
                        参数:org.aspectj.lang.ProceedingJoinPoint
                        抛出异常
                    执行目标方法:Object obj = joinPoint.proceed();
                    例如:
                <aop:around method="myAround" pointcut-ref="myPointCut"/>
                -->
                <!-- 3.4 抛出异常
                    <aop:after-throwing method="" pointcut-ref="" throwing=""/>
                        throwing :通知方法的第二个参数名称
                    通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
                        参数1:连接点描述对象
                        参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置
                    例如:
                <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
                -->
                <!-- 3.5 最终通知 -->            
                <aop:after method="myAfter" pointcut-ref="myPointCut"/>
                    
            </aop:aspect>
        </aop:config>
    </beans>

    10、AspectJ 基于注解    <--返回目录

      spring 配置文件中配置:注解扫描,声明启动aspectJ 自动代理(JavaConfig 声明启动aspectJ 自动代理: @EnableAspectJAutoProty)

    <!-- 扫描注解类-->
    <context:component-scan base-package=""/>
    <!-- 声明aop注解生效-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

      切面类使用注解

    @Component
    @Aspect
    public class MyAspect {
        // 切入点当前有效
        @Before("execution(* cn.oy.service.UserServiceImpl.*(..))")
        public void myBefore(JoinPoint joinPoint) {
            System.out.println("前置通知 : " + joinPoint.getSignature().getName());
        }
    
        // 声明公共切入点
        @Pointcut("execution(* cn.oy.service.UserServiceImpl.*(..))")
        private void myPointCut() {
        }
        
        @AfterReturning(value="myPointCut()" ,returning="returnValue")
        public void myAfterReturning(JoinPoint joinPoint, Object returnValue) {
            System.out.println("后置通知 : " + joinPoint.getSignature().getName()
                    + " , -->" + returnValue);
        }
    
        @Around(value = "myPointCut()")
        public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("前");
            // 手动执行目标方法
            Object obj = joinPoint.proceed();
    
            System.out.println("后");
            return obj;
        }
    
        @AfterThrowing(value="execution(* cn.oy.service.UserServiceImpl.*(..))" ,throwing="e")
        public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
            System.out.println("抛出异常通知 : " + e.getMessage());
        }
    
        public void myAfter(JoinPoint joinPoint) {
            System.out.println("最终通知");
        }
    }

      替换 <aop:before method="myBefore" pointcut="execution(* com.oy.anno.UserServiceImpl.*(..))"/>

    //切入点当前有效
    @Before("execution(* com.oy.anno.UserServiceImpl.*(..))")
    public void myBefore(JoinPoint joinPoint){
        System.out.println("前置通知 : " + joinPoint.getSignature().getName());
    }

      替换 <aop:before method="myBefore" pointcut-ref="" />

    // 声明公共切入点
    @Pointcut("execution(* cn.oy.service.UserServiceImpl.*(..))")
    private void myPointCut() {
    }
    
    @AfterReturning(value="myPointCut()" ,returning="returnValue")
    public void myAfterReturning(JoinPoint joinPoint, Object returnValue) {
        System.out.println("后置通知 : " + joinPoint.getSignature().getName()
                + " , -->" + returnValue);
    }

    11、基于注解进行 AOP 开发的demo    <--返回目录

       依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    
    <dependency>  
              <groupId>org.springframework.boot</groupId>  
              <artifactId>spring-boot-starter-aop</artifactId>  
    </dependency>
    View Code

      application.properties

    server.port=8089
    server.servlet.context-path=/BootDemo
    View Code

      切面类

    package com.oy.aspect;
    
    import java.util.Arrays;
    
    import javax.servlet.http.HttpServletRequest;
    
    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.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    /**
     * 自定义切面类
     * 
     * @author oy
     * @version 1.0
     * @date 2020年4月13日
     * @time 下午5:55:06
     */
    @Component
    @Aspect
    public class LoggerAspect {
        // 定义公共切入点
        @Pointcut("execution(public * com.oy.controller.IndexController.*(..))")
        public void webLog() {
        }
    
        @Before("webLog()")
        public void deBefore(JoinPoint joinPoint) throws Throwable {
            // 接收到请求,记录请求内容
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            // 记录下请求内容
            System.out.println("URL : " + request.getRequestURL().toString());
            System.out.println("HTTP_METHOD : " + request.getMethod());
            System.out.println("IP : " + request.getRemoteAddr());
            System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
                    + joinPoint.getSignature().getName());
            System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs()));
    
        }
    
        /*
        @AfterReturning(returning = "ret", pointcut = "webLog()")
        public void doAfterReturning(Object ret) throws Throwable {
            // 处理完请求,返回内容
            System.out.println("方法的返回值 : " + ret);
        }
    
        // 后置异常通知
        @AfterThrowing("webLog()")
        public void throwss(JoinPoint jp) {
            System.out.println("方法异常时执行.....");
        }
    
        // 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
        @After("webLog()")
        public void after(JoinPoint jp) {
            System.out.println("方法最后执行.....");
        }
    
        // 环绕通知,环绕增强,相当于MethodInterceptor
        @Around("webLog()")
        public Object arround(ProceedingJoinPoint pjp) {
            System.out.println("方法环绕start.....");
            try {
                Object o = pjp.proceed();
                System.out.println("方法环绕proceed,结果是 :" + o);
                return o;
            } catch (Throwable e) {
                e.printStackTrace();
                return null;
            }
        }
        */
    }
    View Code

      测试 controller

    package com.oy.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/index")
    public class IndexController {
    
        @RequestMapping("/test1")
        public String test1(String name, int age) {
            return "test1";
        }
    
        @RequestMapping("/test2")
        public String test2() {
            return "test1";
        }
        
    }
    View Code

      启动 springboot 项目,访问 http://localhost:8089/BootDemo/index/test1?name=xxx&age=10,控制台打印结果

    URL : http://localhost:8089/BootDemo/index/test1
    HTTP_METHOD : GET
    IP : 0:0:0:0:0:0:0:1
    CLASS_METHOD : com.oy.controller.IndexController.test1
    ARGS : [xxx, 10]

    12、自定义注解 + AOP 编程    <--返回目录

      如果是要对类的几个方法进行增强(即切入点是类的某些方法,不是所有),使用注解就比较方便。

       自定义注解

    package com.oy.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.METHOD })
    public @interface OutputLog {
        boolean value() default true;
    }
    View Code

      切面类

    package com.oy.aspect;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    /**
     * 自定义切面类
     * 
     * @author oy
     * @version 1.0
     * @date 2020年4月13日
     * @time 下午5:55:06
     */
    @Component
    @Aspect
    public class LoggerAspect {
        // 定义切入点
        @Pointcut("@annotation(com.oy.annotation.OutputLog)")
        public void webLog() {
        }
    
        // 环绕通知, 环绕增强, 相当于MethodInterceptor
        @Around("webLog()")
        public Object arround(ProceedingJoinPoint pjp) throws Throwable {
            long startTime = System.currentTimeMillis();
            Object result = pjp.proceed();
            long time = System.currentTimeMillis() - startTime;
    
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
    
            // 获取handler的参数(不包括 request 和 response)
            List<Object> logArgs = Arrays.stream(pjp.getArgs())
                    .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
                    .collect(Collectors.toList());
    
            System.out.println("请求uri: " + request.getRequestURI());
            System.out.println("请求参数: " + logArgs);
            System.out.println("handler执行时长:" + time + " ms");
    
            return result;
        }
    }

      测试 controller

    package com.oy.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.oy.annotation.OutputLog;
    
    @RestController
    @RequestMapping("/index")
    public class IndexController {
    
        @OutputLog
        @RequestMapping("/test1")
        public String test1(String name, int age) {
            for (int i = 0; i < 100000; i++) {
                System.out.println(i);
            }
            return "test1";
        }
    
        @RequestMapping("/test2")
        public String test2() {
            return "test1";
        }
    
    }
    View Code

      启动 springboot 项目,访问 http://localhost:8089/BootDemo/index/test1?name=xxx&age=10,控制台打印结果

    99998
    99999
    请求uri: /BootDemo/index/test1
    请求参数: [xxx, 10]
    handler执行时长:565 ms

    参考:

      1)pring boot中使用aop详解

  • 相关阅读:
    有关线程与进程的参考资料
    [Notes] 各种数据源配置
    [Notes] 显卡更新后docker nvidia-runtime不可用
    [Tips] numpy diff
    [Tips] vs code ssh remote情况下如何选者python
    RSA算法之学习
    湖南大学推荐书《社会学大纲》阅读有感 其一
    解决某些应用程序阻止了IDM集成到浏览器中的问题
    Oracle实现判断功能三种方式总结
    JS实现数字每三位加逗号
  • 原文地址:https://www.cnblogs.com/xy-ouyang/p/12692613.html
Copyright © 2011-2022 走看看