8. Spring之AOP前奏

Spring之AOP前奏

1. 提出问题

情景:数学计算器
要求:

  1. 执行加减乘除运算
  2. 日志:在程序执行期间追踪正在发生的活动
  3. 验证:希望计算器只能处理正数的运算
常规实现

Calculator

 /**
 * @Date 2020/5/23 23:23
 * @Version 10.21
 * @Author DuanChaojie
 */
public interface Calculator {
    int add(int m,int n);
    
    int sub(int m,int n);
    
    // ...
}

CalculatorImpl

/**
 * @Date 2020/5/23 23:24
 * @Version 10.21
 * @Author DuanChaojie
 */
public class CalculatorImpl implements Calculator {

    public int add(int m, int n) {
        System.out.println("日志:执行了add方法参数1="+m+",参数2="+n);
        int result = m + n;
        System.out.println("日志:执行add方法结束,结果为"+result);
        return result;
    }
    
    public int sub(int m, int n) {
        System.out.println("日志:执行了sub方法参数1="+m+",参数2="+n);
        int result = m - n;
        System.out.println("日志:执行sub方法结束,结果为"+result);
        return result;
    }
    
    // ...
    
}
2. 问题分析
  1. 代码混乱:越来越多的非业务需求(日志和验证等)加入后,原有的业务方法急剧膨胀。每个方法在处理核心逻辑的同时还必须兼顾其他多个关注点。
  2. 代码分散: 以日志需求为例,只是为了满足这个单一需求,就不得不在多个模块(方法)里多次重复相同的日志代码。如果日志需求发生变化,必须修改所有模块。
3. 动态代理
原理:
  1. 代理设计模式的原理:使用一个代理将原本对象包装起来,然后用该代理对象”取代”原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ghqyv5nR-1590424526605)(assets/clip_image002-1590248368361.png)]
方式:
  1. 基于接口实现动态代理: JDK动态代理
  2. 基于继承实现动态代理: CglibJavassist动态代理
4. 动态代理改进后

注释,之前在CalculatorImpl中的日志信息。

4.1 创建代理对象
  1. newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
  2. 内部类中使用Calculator,需要是final定义。
/**
 * @Date 2020/5/24 9:50
 * @Version 10.21
 * @Author DuanChaojie
 *     // java.lang.reflect.Proxy
 *     // newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h)
 *     // 返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
 */
public class CalculatorProxy {

    /**
     * 帮calculator对象创建代理对象
     * @param calculator 被代理的对象
     * @return
     */
    public static Calculator getProxy(final Calculator calculator){

        InvocationHandler h = new InvocationHandler() {
            /**
             * 方法执行器,帮我们被代理对象执行目标方法
             * @param proxy 代理对象给jdk使用的,我们任何时候都不要使用这个对象
             * @param method 当前将要执行的目标对象
             * @param args 这个方法调用时外界传入的参数值
             * @return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //System.out.println("这是动态代理帮你执行的方法");
                // 利用反射执行目标方法
                // 内部类中使用,需要是final定义的
                Object result = null;
                try {
                    LogUtils.logStart(method,args);

                    // int a = 1/0; 异常的测试
                    
                    result = method.invoke(calculator, args);

                    LogUtils.logEnd(method,result);
                    
                } catch (Exception e) {
                    
                    LogUtils.logError(method,e);
                    
                    e.printStackTrace();
                }finally {
                    LogUtils.logFinally(method,result);
                }
                // 返回值必须返回出去外界才能拿到真正执行后的返回值
                return result;
            }
        };

        // 拿到接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        // 拿到类加载器
        ClassLoader loader = calculator.getClass().getClassLoader();

        // Proxy为目标对象创建代理对象
        Calculator cal  = (Calculator) Proxy.newProxyInstance(loader, interfaces, h);
        return cal;
    }
}
4.2 创建日志工具类
/**
 * @Date 2020/5/24 10:51
 * @Version 10.21
 * @Author DuanChaojie
 */
public class LogUtils {

    public static void logStart(Method method,Object args){
        System.out.println("["+method.getName()+"]:方法执行了,其参数列表为"+ Arrays.asList(args));

    }
    
    public static void logEnd(Method method,Object result){
        System.out.println("["+method.getName()+"]:方法正常执行完成了,其结果为"+ result);
    }
    
    public static void logError(Method method,Exception e){
        System.out.println("["+method.getName()+"]:方法执行出现了异常,原因为:"+ e.getCause());
    }
    
    public static void logFinally(Method method,Object result){
        System.out.println("["+method.getName()+"]:方法最终执行完成了,其结果为"+ result);
    }

}
4.3 测试动态代理
/**
 * @Date 2020/5/24 10:39
 * @Version 10.21
 * @Author DuanChaojie
 */
public class TestCalculator {

    @Test
    public void testCalculator() {
        Calculator calculator = new CalculatorImpl();
        // 没使用动态代理之前
        int addRes = calculator.add(2, 3);

        // 使用动态代理,如果拿到这个对象的代理对象,代理对象执行加减乘除
        Calculator proxy = CalculatorProxy.getProxy(calculator);
        
        // proxy.getClass() =  class com.sun.proxy.$Proxy4
        // 代理对象和被代理对象唯一能产生的关系就是实现了同一个接口
        System.out.println("proxy.getClass() = " + proxy.getClass());
        int add = proxy.add(5,6);
        System.out.println("测试结果 result = " + add);
    }
}
5. 使用动态代理实现存在的问题
  1. 写起来很复杂,实现复杂
  2. jdk默认的动态代理,如果目标对象没有实现任何接口,是无法为他创建代理对象的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值