zoukankan      html  css  js  c++  java
  • 浅谈 Spring的AOP的实现 -- 动态代理

    说起Spring的AOP(Aspect-Oriented Programming)面向切面编程大家都很熟悉(Spring不是这次博文的重点),但是我先提出几个问题,看看同学们是否了解,如果了解的话可以不用继续往下读:

      1. Spring的AOP的实现方式有哪些?

      2. 为什么使用动态代理?

      3. 它们是怎么实现的?

      4. 它们的区别是什么?

      下面进入正题,Spring采用动态代理的方式实现AOP,具体采用了JDK的动态代理和CGLib的动态代理。使用动态代理的目的是在现有类的基础上增加一些功能。简单地将就是有一个Proxy类,实现了原始类的方法,并且在原始类的基础上增加了新的功能。那么这么做可以实现很多功能:

      1. 在方法前后进行日志处理。

      2. 进行额外的校验,比如参数的验证功能等。

      3. 实现一些懒加载,也就是实例化的时候如果不去调用真正的方法的时候,这个类的属性就不会存在(Hibernate有这样类似的功能)。

      下面咱们用简单的代码实现它是如何进行代理的,首先采用的是JDK的动态代理实现:

      定义一个接口:

    按 Ctrl+C 复制代码
    按 Ctrl+C 复制代码

      定义一个实现类:

    复制代码
    package com.hqs.proxy;
    
    /**
     * Mac 的实现
     * @author hqs
     *
     */
    public class Mac implements OpSystem {
    
        public void work() {
            System.out.println("Mac is running");    
        }
    
    
    }
    复制代码

      关键位置来了,我们通过实现JDK自带的反射机制的包的InvocationHandler来进行反射处理,实现它之后需要实现里边的invoke方法,这个invoke方法里边的参数分别为:代理类实例,用于调用method的;method参数是实际执行的方法;args所传输的参数数组。

    复制代码
    package com.hqs.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class OpHandler implements InvocationHandler {
        
        private final OpSystem ops; 
        
        public OpHandler(OpSystem ops) {
            this.ops = ops;
        }
        
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before system running");
            method.invoke(ops, args);
            System.out.println("After system running");
            return null;
        }
        
        public static void main(String[] args) {
            Mac mac = new Mac();
            OpHandler oph = new OpHandler(mac);
            OpSystem os = (OpSystem)Proxy.newProxyInstance(oph.getClass().getClassLoader(),
                    mac.getClass().getInterfaces(), oph);
            
            os.work();
            System.out.println(os.getClass());
        }
        
    
    }
    
    
    输出:
    Before system running
    Mac is running
    After system running
    class com.sun.proxy.$Proxy0
    复制代码

       然后看到里边的main方法中,代理类实例化对象的方法Proxy.newProxyInstance,这个是JDK的反射方法去实例化代理类,其中有三个分别是,去实例化代理类的class loader;所代理的类的所有接口Class数组;hander处理类,用于做拦截使用的类。最后我输出了一下os.getClass(),大家可以看到的是代理类的实例,而不是真正代理类的实例,这么做的好处就是很方便的复用这个代理类,比如你可以重复调用它而不用去重新实例化新类,再一点就是你可以针对不同的方法进行拦截,比如你可以method.getName()去判断调用的方法名字是什么从而更细粒度的拦截方法。咱们继续看用CGLib的实现:  

    复制代码
    package com.hqs.proxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    /**
     * CGLib Interceptor用于方法拦截
     * @author hqs
     *
     */
    public class CGLibInterceptor implements MethodInterceptor {
        
        private final Mac mac;
        
        public CGLibInterceptor(Mac mac) {
            this.mac = mac;
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, 
                MethodProxy methodProxy) throws Throwable {
            System.out.println("Before system running");
            method.invoke(mac, args);
            System.out.println("After system running");
            return null;
        }
        
        public static void main(String[] args) {
            Mac mac = new Mac(); //实例而非接口
            MethodInterceptor handler = new CGLibInterceptor(mac);
            Mac m = (Mac)Enhancer.create(mac.getClass(), handler);
            m.work();
            System.out.println(m.getClass());
            
        }
    
    }
    
    
    输出:
    Before system running
    Mac is running
    After system running
    class com.hqs.proxy.Mac$$EnhancerByCGLIB$$1f2c9d4a
    复制代码

      首先需要引入cglib包,然后才能使用他的MethodInterptor,它也采用method.invoke实现对代理类的调用。它的代理类创建采用Enhancer的create方法,其中传入了需要创建的类的class,以及Callback对象,因为MethodInterceptor继承了Callback对象。用于指向方法前后进行调用的类。  

    public interface MethodInterceptor
    extends Callback

      这是这两个类的基本实现,那么它们的区别是什么呢?

    1. JDK的动态代理只能针对接口和其实现类,如果没有实现类只有接口也是可以代理的,这里就不在举例了。为什么JDK的动态代理只针对接口代理,因为这个是JDK的定义。
    2. 如果不针对接口实现动态代理那就用到了CGLib了,也就是可以针对具体的类进行代理,大家可以参考我的代码。

      这些是它们的根本区别,但是Spring推荐使用JDK的动态代理,面向接口去编程。使用CGLib去做动态代理的时候需要注意,它生产的代理类存放在JVM的Perm space里边,那么是不是生成的代理对象就不进行回收了?其实不是的,不经常回收但是还是回收的,当类被加载,加载类的classLoader什么时候变得对垃圾回收可用的时候才进行回收。也就是你自己创建所有类移除classLoader之后,那么这个classLoader就会被回收,一般非常精通CGLib的话可以进行这块内容深入开发,因为它可以做出amzing的事情如果你熟悉的话。

    转载: 黄山清水岸边石   http://www.cnblogs.com/huangqingshi/p/7651376.html

  • 相关阅读:
    高盛、沃尔玛 题做出来还挂了的吐槽
    amazon师兄debrief
    到所有人家距离之和最短的中点 296. Best Meeting Point
    问问题没人回答的情况怎么办终于有解了
    找名人 277. Find the Celebrity
    数组生存游戏 289. Game of Life
    547. Number of Provinces 省份数量
    428. Serialize and Deserialize Nary Tree 序列化、反序列化n叉树
    alias别名简介和使用
    面试官:线程池执行过程中遇到异常会发生什么,怎样处理? Vincent
  • 原文地址:https://www.cnblogs.com/up-farm/p/7653122.html
Copyright © 2011-2022 走看看