• spring AOP (使用AspectJ的注解方式 的aop实现) (6)


    前面的动态代理中,可以有前置通知,后置通知,返回通知,异常通知

    在spring2.0以后,可以使用基于 AspectJ 注解或基于 XML 配置的 AOP

    • AspectJ:Java 社区里最完整最流行的 AOP 框架.

    一、在 Spring 中启用 AspectJ 注解支持

    • 1要在 Spring 应用中使用 AspectJ 注解, 必须在 classpath 下包含 AspectJ 类库: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar
      maven 引入
    <dependency>
        <groupId>org.springframework</groupId>  
        <artifactId>spring-aop</artifactId>  
        <version>4.1.9.RELEASE</version>  
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>  
        <artifactId>aspectjrt</artifactId>  
        <version>1.6.8</version>  
    </dependency>  
    <dependency>  
        <groupId>org.aspectj</groupId>  
        <artifactId>aspectjweaver</artifactId>  
        <version>1.6.8</version>  
        <scope>runtime</scope>  
    </dependency>    
    
    • 2将 aop Schema 添加到 根元素中.
    • 3要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中定义一个空的 XML 元素
    <!-- 配置自动为匹配 aspectJ 注解的 Java 类生成代理对象 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy>  
    
    • 4当 Spring IOC 容器侦测到 Bean 配置文件中的 aop:aspectj-autoproxy 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理.

    二、AspectJ 支持 5 种类型的通知注解:

    • @Before: 前置通知, 在方法执行之前执行
    • @After: 后置通知, 在方法执行之后执行
    • @AfterRunning: 返回通知, 在方法返回结果之后执行
    • @AfterThrowing: 异常通知, 在方法抛出异常之后
    • @Around: 环绕通知, 围绕着方法执行

    2.1、使用之前的 计算器接口和实现类 ArithmeticCalculator.java , ArithmeticCalculatorImpl.java

    @Component("arithmeticCalculator")
    public class ArithmeticCalculatorImpl implements ArithmeticCalculator{}  
    

    2.2、在xml中增加扫描注解和aspectj的支持

    <?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:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
            
        <!-- 配置自动扫描的包 -->
        <context:component-scan base-package="com.hp.spring.aop.annotation"></context:component-scan>
        
        <!-- 配置自动为匹配 aspectJ 注解的 Java 类生成代理对象 -->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>  
    

    2.3、编写切面类,定义各种通知

    @Aspect //注解定义切面
    @Component
    public class LoggingAspect {
        //前置通知
        @Before("execution(public int com.hp.spring.aop.annatation.ArithmeticCalculator.*(int, int))")
        public void beforeMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            Object [] args = joinPoint.getArgs();
            
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
        }
        //前置通知
        @After("execution(* com.hp.spring.aop.annatation.*.*(..))")
        public void afterMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends");
        }
        
    }  
    

    由于@before 和@after的表达式都一样,所以spring支持对表达式进行抽取成一个方法,抽取后的代码如下:

    package com.hp.spring.aop.annotation;
    import java.util.Arrays;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    /**
     * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
     */
    @Order(2) 
    @Aspect
    @Component
    public class LoggingAspect {
        
        /**
         * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
         * 使用 @Pointcut 来声明切入点表达式. 
         * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
         */
        @Pointcut("execution(public int com.hp.spring.aop.annotation.ArithmeticCalculator.*(..))")
        public void declareJointPointExpression(){}
        
        /**
         * 在 com.hp.spring.aop.annotation.ArithmeticCalculator 接口的每一个实现类的每一个方法开始之前执行一段代码
         */
        @Before("declareJointPointExpression()")
        public void beforeMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            Object [] args = joinPoint.getArgs();
            
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
        }
        
        /**
         * 在方法执行之后执行的代码. 无论该方法是否出现异常
         */
        @After("declareJointPointExpression()")
        public void afterMethod(JoinPoint joinPoint){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends");
        }
        
        /**
         * 在方法法正常结束受执行的代码
         * 返回通知是可以访问到方法的返回值的!
         */
        @AfterReturning(value="declareJointPointExpression()",
                returning="result")
        public void afterReturning(JoinPoint joinPoint, Object result){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends with " + result);
        }
        
        /**
         * 在目标方法出现异常时会执行的代码.
         * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
         */
        @AfterThrowing(value="declareJointPointExpression()",
                throwing="e")
        public void afterThrowing(JoinPoint joinPoint, Exception e){
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " occurs excetion:" + e);
        }
        
        /**
         * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. 
         * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
         * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
         */
        /*
        @Around("execution(public int com.hp.spring.aop.annotation.ArithmeticCalculator.*(..))")
        public Object aroundMethod(ProceedingJoinPoint pjd){
            
            Object result = null;
            String methodName = pjd.getSignature().getName();
            
            try {
                //前置通知
                System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
                //执行目标方法
                result = pjd.proceed();
                //返回通知
                System.out.println("The method " + methodName + " ends with " + result);
            } catch (Throwable e) {
                //异常通知
                System.out.println("The method " + methodName + " occurs exception:" + e);
                throw new RuntimeException(e);
            }
            //后置通知
            System.out.println("The method " + methodName + " ends");
            
            return result;
        }
        */
    }
    
    • 切面二
    package com.hp.spring.aop.annotation;
    import java.util.Arrays;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    @Order(1)
    @Aspect
    @Component
    public class VlidationAspect {
        @Before("com.hp.spring.aop.annotation.LoggingAspect.declareJointPointExpression()")
        public void validateArgs(JoinPoint joinPoint){
            System.out.println("-->validate:" + Arrays.asList(joinPoint.getArgs()));
        }
        
    }
    
    
    • 测试类
    package com.hp.spring.aop.annotation;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    public class Main {
        
        public static void main(String[] args) {
            
            ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-aspectj.xml");
            ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
            
            System.out.println(arithmeticCalculator.getClass().getName());
            
            int result = arithmeticCalculator.add(1, 2);
            System.out.println("result:" + result);
            
            result = arithmeticCalculator.div(1000, 10);
            System.out.println("result:" + result);
        }
        
    }
    

    打印出:
    com.sun.proxy.$Proxy12
    -->validate:[1, 2]
    The method add begins with [1, 2]
    The method add ends
    The method add ends with 3
    result:3
    -->validate:[1000, 10]
    The method div begins with [1000, 10]
    The method div ends
    The method div ends with 100
    result:100

    打印的日志中,可以看出,跟动态代理一样,可以定义各类通知。

    整个系列项目代码: http://git.oschina.net/nmc5/spring

  • 相关阅读:
    如何在一个页面后面随机跳转到多个链接地址Math.floor()和Math.random()
    thinkphp中volist标签
    PHP中删除数组空值的方法
    PHP实现四种基本排序算法
    如何解决自动加载与模板中(如Smarty)的自动加载冲突的问题
    GD库常用函数
    内网最小化安装CentOS后,想安装ISO文件中的包怎么办呢?
    Elasticsearch插件安装
    python类的反射使用方法
    python类的继承
  • 原文地址:https://www.cnblogs.com/linhp/p/5881798.html
Copyright © 2020-2023  润新知