• 2、Spring之AOP


    AOP术语

    • 通知:定义了切面是什么以及何时使用。除了要描述页面要完成的工作,通知还解决了何时执行这个工作的问题。
    • 连接点:连接点是一个物理的存在。这个点可以是调用方法时、抛出异常时、甚至是修改一个字段时。
    • 切点:如果通知定义了切面是”什么“和”何时“,那么切点定义了”何处“。切点的定义会匹配通知所要织入的一个或者多个连接点。通常使用明确的类名和方法名来指定这些切点,或者使用正则表达式定义匹配的类和方法名模式来指定这些切点。
    • 切面:切面是切点和通知的组合。

    配置AOP

    <!-- 配置切面的Bean -->
        <bean id="loggingAspect" class="com.test.spring.aop.xml.LoggingAspect"></bean>
        <bean id="validationAspect" class="com.test.spring.aop.xml.ValidationAspect"></bean>
    
        <bean id ="constant" class="com.test.spring.aop.xml.ConstantImpl"></bean>
    
        <!-- 配置AOP -->
        <aop:config>
            <!-- 配置切点表达式 -->
            <aop:pointcut expression="execution(*  com.test.spring.aop.xml.ArithmeticCalculator.*(..))" id="pointcut"/>
            <!-- 配置切面和通知 -->
            <aop:aspect ref="loggingAspect" order="2">
                <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
                <aop:after-returning method="afterReturning" returning="result" pointcut-ref="pointcut"/>
                <aop:after method="afterMethod" pointcut-ref="pointcut"/>
                <aop:after-throwing method="afterThrowing" throwing="ex" pointcut-ref="pointcut"/>
                <!--<aop:around method="aroundMethod" pointcut-ref="pointcut"/>  -->
            </aop:aspect>
            <aop:aspect ref="validationAspect" order="1">
                <aop:before method="validateArgs" pointcut-ref="pointcut"/>
            </aop:aspect>
        </aop:config>

    LoggingAspect

    public class LoggingAspect {
    
        public void beforeMethod(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            //为通知传递参数,可以在JoinPoint 中拦截调用的目标方法,包括调用的目标方法的名字,出入目标方法的参数。
            Object[] args = joinPoint.getArgs();
            System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
        }
    
        public void afterMethod(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends");
        }
    
        public void afterReturning(JoinPoint joinPoint, Object result) {
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " ends with " + result);
        }
    
        public void afterThrowing(JoinPoint joinPoint, Exception ex) {
            String methodName = joinPoint.getSignature().getName();
            System.out.println("The method " + methodName + " occurs excetion:" + ex);
        }

    环绕通知

    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;
        }

    通过切面引入新功能

    <aop:aspect ref="loggingAspect" order="2">
        <aop:declare-parents types-matching="com.test.spring.aop.xml.ArithmeticCalculator+" 
            implement-interface="com.test.spring.aop.xml.Constant" delegate-ref="constant"/>
    </aop:aspect>

    说明:通过切面引入新的功能就是为代理实现接口的一个过程。ArithmeticCalculator 是一个代理,通过<aop:declare-parents/> 为此代理实现Constant接口,并指定默认实现delegate-ref="constant" 从而达到扩展基础对象ArithmeticCalculator 的效果。在掉用目标Bean的时候,如果不是目标Bean的方法,则从代理的实现的接口constant 寻找方法的代用,如果是目标Bean的方法,则调用目标Bean的方法。

    注解使用

    启用注解: <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    /**
     * 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
     */
    @Order(1)
    @Aspect
    @Component
    public class LoggingAspect {
    
        /**
         * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
         * 使用 @Pointcut 来声明切入点表达式. 
         * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
         */
        @Pointcut("execution(public int com.spring.aop.ArithmeticCalculator.*(..))")
        public void declareJointPointExpression(){}
    
        /**
         * 在 com.spring.aop.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.spring.aop.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;
        }
    }
  • 相关阅读:
    Java 练习(经典例题: 生产者/消费者问题)
    Java 基础(线程的通信)
    Java 练习(线程的同步)
    Java 基础( ReentrantLock )
    Java 基础(线程的死锁问题)
    Java基础(单实例设计模式懒汉式解决线程安全)
    Java 基础(同步方法解决线程安全问题)
    Java 基础(Thread类的有关方法,线程的调度)
    Java 基础(线程的生命周期, 同步代码块解决线程安全)
    Java 基础(以实现 Runnable 接口的方式创建多线程)
  • 原文地址:https://www.cnblogs.com/huacesun/p/6622490.html
Copyright © 2020-2023  润新知