spring AOP 编程演示

 

   Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:

 

1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了

 

2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB

 

 

AOP编程概念 

   Aop  aspect object programming  面向切面编程

         功能: 让关注点代码与业务代码分离!

  关注点,

         重复代码就叫做关注点;

  切面,

          关注点形成的类,就叫切面(类)!

          面向切面编程,就是指 对很多功能都有的重复的代码抽取,再在运行的时候网业务方法上动态植入“切面类代码”。

  切入点,

         执行目标对象方法,动态植入切面代码。

         可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。

  

  需求:实现数字的四则运算,并进行日志打印。

  实现将打印日志这些重复代码与核心代码进行分离。

  

 

代码演示

  方式一:基于注解的实现

  一、开启注解扫描与AOP注解 (spring 配置文件)

   

  二、定义核心代码的接口并进行实现

  

  

  三、定义切面类

package com.jcy.aop;

import java.util.Arrays;
import java.util.List;

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.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.mysql.fabric.xmlrpc.base.Array;
@Order(2)   //设置优先级(值越小越高的优先级)
@Aspect    //指定为切面类
@Component
public class LoggingAspect {
    
    /**
     * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
     * 使用 @Pointcut 来声明切入点表达式. 
     * 后面的其他通知直接使用方法名来引用当前的切入点表达式.
     * 在其他类中使用 包名.类名.方法  com.jcy.aop.LoggingAspect.describePointcut() 
     */
    @Pointcut("execution(public int com.jcy.aop.ArithmeticCalculateImpl.*(..))")
    public void describePointcut(){}

    /**
     * 方法使用前的前置通知
     * 
     * @param joinPoint
     */
    @Before("describePointcut()")
    public void beforeLogging(JoinPoint joinPoint) {

        String methodName = joinPoint.getSignature().getName();

        List<Object> l = Arrays.asList(joinPoint.getArgs());

        System.out.println(methodName + " 方法的开始,参数有" + l);

    }

    /**
     * 不看方法执行结果,方法执行后也会执行
     * 后置通知
     * @param joinPoint
     */
    @After("execution(public int com.jcy.aop.ArithmeticCalculateImpl.*(int, int))")
    public void afterLogging(JoinPoint joinPoint) {

        String methodName = joinPoint.getSignature().getName();

        System.out.println(methodName + " 方法的结束。");

    }

    /**
     * 方法执行成功后,执行
     * 结果返回通知
     * @param joinPoint
     * @param result
     */
    @AfterReturning(value = "execution(public int com.jcy.aop.ArithmeticCalculateImpl.*(int, int))", returning = "result")
    public void afterReturningLogging(JoinPoint joinPoint, Object result) {

        String methodName = joinPoint.getSignature().getName();

        System.out.println(methodName + " 方法的结果:" + result);

    }
    /**
     * 方法执行异常时,执行 异常通知
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "execution(public int com.jcy.aop.ArithmeticCalculateImpl.*(int, int))", throwing = "e")
    public void afterThrowingExceptionLogging(JoinPoint joinPoint, Exception e) {

        String methodName = joinPoint.getSignature().getName();

        System.out.println(methodName + " 执行失败,方法的异常结果:" + e);

    }
//    /**
//     * 环绕通知
//     * 类似代理
//     * 包含以上的所有通知
//     * @return 返回的结果就是当前目标方法的返回结果
//     */
//    @Around("execution(public int com.jcy.aop.ArithmeticCalculateImpl.*(int, int))")
//    public Object aroundLogging(ProceedingJoinPoint proceedingJoinPoint) {
//
//        String methodName = proceedingJoinPoint.getSignature().getName();
//        
//        Object result = null;
//        //前置通知
//        System.out.println(methodName + " 方法的开始,参数有" +Arrays.asList( proceedingJoinPoint.getArgs()));
//        try {
//            
//            //执行当前目标方法
//            result = proceedingJoinPoint.proceed();
//            
//            //结果返回通知
//            System.out.println(methodName + " 方法的结果:" + result);
//            
//        } catch (Throwable e) {
//            // 异常通知
//            System.out.println(methodName + " 执行失败,方法的异常结果:" + e);
//        }
//        
//        //后置通知
//        System.out.println(methodName + " 方法的结束。");
//        
//        
//        return result;
//
//    }
}

 

  ProceedingJoinPoint 对象

    ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
   java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
   java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。   

  四、运行测试

  

  测试结果:

  

  五、验证 @Order(2) 注解的优先级:

  

     再定义一个切面类(对传入参数进行验证是否合法)如下:

   

  再次就行运行测试:

  

  方式二:基于 xml 配置实现 AOP 

  spring xml 配置如下

<?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/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.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 配置 bean -->
    <bean id="arithmeticCalculate" class="com.jcy.aop.xml.ArithmeticCalculateImpl">
    </bean>
    <!-- 配置切面的 bean 类 -->
    <bean id="loggingAspect" class="com.jcy.aop.xml.LoggingAspect"></bean>

    <bean id="validateAspect" class="com.jcy.aop.xml.ValidateAspect"></bean>

    <!-- 配置 AOP -->
    <aop:config>
        <!-- 配置切点表达式  execution(* com.jcy.aop.xml.ArithmeticCalculate.*(int,int)) -->
        <aop:pointcut
            expression="execution(* com.jcy.aop.xml.ArithmeticCalculate.*(*,*))"
            id="pointcut" />
            
        <!-- 配置切面及通知 -->
        <aop:aspect ref="loggingAspect" order="2">
            <aop:before method="beforeLogging" pointcut-ref="pointcut" />
            <aop:after-returning method="afterReturningLogging"
                pointcut-ref="pointcut" returning="result" />
            <aop:after method="afterLogging" pointcut-ref="pointcut" />
            <aop:after-throwing method="afterThrowingExceptionLogging"
                pointcut-ref="pointcut" throwing="e" />
        </aop:aspect>

        <aop:aspect ref="validateAspect" order="1">
            <aop:before method="beforeValidateAspect" pointcut-ref="pointcut" />
        </aop:aspect>

    </aop:config>


</beans>

   定义切点表达式的方式:

     <!-- 定义一个切入点表达式: 拦截哪些方法 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.*.*(..))" id="pt"/>-->
        
        <!-- 【拦截所有public方法】 -->
        <!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
        
        <!-- 【拦截所有save开头的方法 】 -->
        <!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>-->
        
        <!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 -->
        <!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>-->
        
        <!-- 【拦截指定类的所有方法】 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>-->
        
        <!-- 【拦截指定包,以及其自包下所有类的所有方法】 -->
        <!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
        
        <!-- 【多个表达式】 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <!-- 下面2个且关系的,没有意义 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) &amp;&amp; execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        
        <!-- 【取非值】 -->
        <!--
     <aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/> <aop:pointcut expression=" not execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>      -->

  自己用JDK动态代理的方式实现上述的切面编程。代码如下:

package com.jcy.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class ArithmeticCalculateLoggingProxy {
    
    //要代理的对象
    private ArithmeticCalculate target;
    
    public ArithmeticCalculateLoggingProxy(ArithmeticCalculate target) {
        super();
        this.target = target;
    }

    //返回代理对象
    public ArithmeticCalculate getLoggingProxy(){
        ArithmeticCalculate proxy = null;
        
        ClassLoader loader = target.getClass().getClassLoader();
        Class [] interfaces = new Class[]{ArithmeticCalculate.class};
        InvocationHandler h = new InvocationHandler() {
            /**
             * proxy: 代理对象。 一般不使用该对象
             * method: 正在被调用的方法
             * args: 调用方法传入的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                String methodName = method.getName();
                //打印日志
                System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));
                
                //调用目标方法
                Object result = null;
                
                try {
                    //前置通知
                    result = method.invoke(target, args);
                    //返回通知, 可以访问到方法的返回值
                } catch (NullPointerException e) {
                    e.printStackTrace();
                    //异常通知, 可以访问到方法出现的异常
                }
                
                //后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值
                
                //打印日志
                System.out.println("[after] The method ends with " + result);
                
                return result;
            }
        };
        
        /**
         * loader: 代理对象使用的类加载器。 
         * interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法. 
         * h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
         */
        proxy = (ArithmeticCalculate) Proxy.newProxyInstance(loader, interfaces, h);
        
        return proxy;
    }
}

 

posted @ 2018-03-17 17:24  TigerExpensive  阅读(305)  评论(0编辑  收藏  举报