Spring4.0学习笔记007——AOP基础:动态代理、概念解析

问题的引出

  1. 首先新建名为 SpringAOP 的java工程,为其创建名为 com.yfyzwr.spring.aop 的package,在该包中创建名为 ArithmeticCalculator 的接口。

    public interface ArithmeticCalculator {
        int add(int i, int j);
        int sub(int i, int j);
        int mul(int i, int j);
        int dev(int i, int j);
    }
  2. 创建名为 ArithmeticCalculatorImpl 的java类,实现上述的 ArithmeticCalculator 接口。

    public class ArithmeticCalculatorImpl implements ArithmeticCalculator{
        @Override
        public int add(int i, int j){
            int result = i + j;
            return result;
        }
    
        @Override
        public int sub(int i, int j){
            int result = i - j;
            return result;
        }
    
        @Override
        public int mul(int i, int j){
            int result = i * j;
            return result;
        }
    
        @Override
        public int dev(int i, int j){
            int result = i / j;
            return result;
        }
    }
  3. 现在遇到新的需求:

    • 需求1-日志:在程序执行期间追踪正在发生的活动
    • 需求2-验证:希望计算器只能处理正数的运算
  4. 对于上述的需要,我们首先想到的就是“在每个方法的开始和结尾添加日志和验证的语句”,如下所示。

    public int add(int i, int j){
        System.out.println("add method begin ...");
        int result = i + j;
        System.out.println("add method end ...");
        return result;
    }
  5. 分析这种实现,不难发现这种处理方式有些不足的地方需要指出:

    • 每个方法都这样实现,会增加代码的复杂程度,增加维护成本。
    • 一旦需要改变日志的打印内容,那么每个函数都要改动,这很可能就是巨大的工作量。
  6. 所以需要能够统一的管理这些日志打印、验证等工作,这就需要借助“动态代理”的模式。

动态代理的实现

  1. 新建名为 ArithmeticCalculatorLoggingProxy 的java类。

    public class ArithmeticCalculatorLoggingProxy {
    
        //被代理的对象
        private ArithmeticCalculator target;
    
        public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
            this.target = target;
        }
    
        public ArithmeticCalculator getLoggingProxy(){
    
            ArithmeticCalculator proxy = null;
    
            //被代理对象由哪一个类加载器来负责加载
            ClassLoader loader = target.getClass().getClassLoader();
    
            //被代理对象的类型,即该类型有哪些方法
            //Class [] interfaces = new Class[]{ArithmeticCalculator.class};
            Class [] interfaces = target.getClass().getInterfaces();
    
            //当调用代理对象的方法时,需要执行的代码
            InvocationHandler h = new InvocationHandler() {
    
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("proxy invoke ...");
                    return 0;
                }
            };
    
            proxy = (ArithmeticCalculator)Proxy.newProxyInstance(loader, interfaces, h);
    
            return proxy;
        }
    }


    特别注意:Proxy.newProxyInstance()的第二个参数是Class类型的数组,而该数组要求的是“接口类型的Class元素。

  2. 新建带有main方法的类。

    public static void main(String[] args) {
        ArithmeticCalculator target = new ArithmeticCalculatorImpl();
        ArithmeticCalculator proxy = new ArithmeticCalculatorLoggingProxy(target).getLoggingProxy();
    
        int result = proxy.add(1, 2);
        System.out.println("add result :" + result);
    }
  3. 运行程序,查看输出结果。

    proxy invoke …
    add result :0

    本来调用add(1, 2)方法应该输出的结果为3,但是现在的输出结果却是Proxy类的实现内容,所有代理操作已经成功。

  4. 再修改proxy类的实现,现在才来实现日志打印的功能。

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法调用之前的日志
        System.out.println("proxy invoke method " + method.getName() + "begin with" + Arrays.asList(args));
        //执行方法
        Object result = method.invoke(target, args);
        //方法调用之后的日志
        System.out.println("proxy invoke method " + method.getName() + "end with" + result);
    
        return result;
    }
  5. 运行程序,查看输出结果

    proxy invoke method addbegin with[1, 2]
    proxy invoke method addend with3
    add result :3


特别注意:在开发的时候,可以按上述的方式直接使用动态代理来实现需要的功能,但是却并不建议这样直接使用动态代理。其实完全可以使用更简单的方式来实现,那就是Spring提供的AOP技术。

基础概念的解析

AOP(Aspect-Oriented Programming 面向切面编程):是一种新的方法论,它的主要编程对象是切面(aspect),而切面模块化横切关注点。

从上述的演进过程,可以得知AOP的好处大致为:

  • 每个事物逻辑(如日志、验证等)位于一个位置,代码不分散,便于维护和升级。
  • 业务模块更简洁,只需关注于核心业务代码。

先来看看上述程序示例的AOP分析图,帮助我们理解相关的概念。
Spring4.0学习笔记007——AOP基础:动态代理、概念解析_第1张图片

  • 切面(Aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象。
  • 通知(Advice):切面必须要完成的工作。
  • 目标(Target):被通知的对象。
  • 连接点(Joinpoint):程序执行到的某个特定位置(如类某个方法调用前、调用后、方法抛出异常后等)。每个类都拥有多个连接点(如 ArithmethicCalculator 的所有方法实际上都是连接点),即连接点是程序类中客观存在的事务。

你可能感兴趣的:(spring)