zoukankan      html  css  js  c++  java
  • spring aop源码想到的代理模式

    代理(pxoxy)无处不在,哪怕在现实生活中

    在看源码的时候,看到了aop,

    就以此引出代理的知识扩展。

    为什么要有代理呢,主要是不改变原来代码的功能的前提下,增加一些特殊功能,比如记录日志,权限认证,事务相关。

    代理有静态代理和动态代理,而动态代理有两种实现方式:jdk代理和cglib代理,下面分别介绍下其实现方式

    0  静态代理

    这是很古老的方式了,一开始入行时用的代理就是这样方式实现的。核心伪代码如下

    /**
     * 
     * @author dgm
     * @describe "产品接口"
     * @date 2020年4月20日
     */
    public interface ProductDao {
        Product createProduct(String name, String size);
    }
    
    public class ProductDaoImpl implements ProductDao {
    
        public ProductDaoImpl() {
        }
        public Product createProduct(String name, String size) {
            return new Product(name,size);
        }
    }
    
    /**
     * 
     * @author dgm
     * @describe "静态代理"
     * @date 2020年4月20日
     */
    public class ProductStaticProxy implements ProductDao {
    
        private ProductDao target;
        
        public ProductStaticProxy(ProductDao target) {
            this.target = target;
        }
    
        @Override
        public Product createProduct(String name, String size) {
            System.out.println("代理执行,开始工地干活生产房子办公楼产品了...");
            return target.createProduct(name,size);
        }
    }
    
    public class ProductStaticProxyTest {
    
        public static void main(String[] args) {
            // 目标对象
            ProductDao dao = new ProductDaoImpl();
            System.out.println("目标对象: " + dao.getClass());
            // 获取代理对象
            ProductDao proxy = new ProductStaticProxy(dao);
            System.out.println("代理对象: "+proxy.getClass());
            // 代理生产产品
            System.out.println(proxy.createProduct("手机屏幕", "4寸"));
        }
    }

    执行结果图示

     此种方式简单,但随着项目量越来越大,维护的代码也就越多,且接口改动也频繁,迫切需要一种新的方式,这时动态代理登场了

    1.  jdk动态代理(自带功能,不需要引入第三方包)

    先介绍下java.lang.reflect.Proxy类,它提供的一个newProxyInstance方法用来创建一个对象的代理对象

     public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)

    newProxyInstance方法用来返回一个代理对象,这个方法总共有3个参数,ClassLoader loader用来指明生成代理对象使用哪个类装载器,Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,InvocationHandler h用来指明产生的这个代理对象要做什么事情。所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。

    示例代码:

    /**
     * 
     * @author dgm
     * @describe "jdk动态代理"
     * @date 2020年4月20日
     */
    public class ProductJdkDynamicProxy {
        // 接收一个目标对象
        private Object target;
    
        public ProductJdkDynamicProxy(Object target) {
            this.target = target;
        }
    
        // 返回对目标对象(target)代理后的对象(proxy)
        public Object getProxyInstance() {
            Object proxy = Proxy.newProxyInstance(target.getClass()
                    .getClassLoader(), // 目标对象使用的类加载器
                    target.getClass().getInterfaces(), // 目标对象实现的所有接口
                    new InvocationHandler() { // 执行代理对象方法时候触发
                        @Override
                        public Object invoke(Object proxy, Method method,
                                Object[] args) throws Throwable {
    
                            System.out.println("原生jdk开启生产产品线,拼命加班中");
                            // 执行目标对象方法
                            Object result = method.invoke(target, args);
                            System.out.println("原生jdk干的不错,结束生产产品");
    
                            return result;
                        }
                    });
            return proxy;
        }
    }
    
    
    public class ProductJdkDynamicProxyTest {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            //目标对象
            ProductDao target = new  ProductDaoImpl();
            System.out.println("目标对象: "+target.getClass());
            //代理生产产品
            ProductDao proxy = (ProductDao) new ProductJdkDynamicProxy(target).getProxyInstance();
            System.out.println("代理对象: "+proxy.getClass());
            //执行代理业务具体业务操作
            System.out.println(proxy.createProduct("手机屏幕", "4寸"));
        }
    
    }

     测试效果:

    spring aop的实现方式之一就是它实现的

    2.  cglib代理

    慢慢地工作到了一定阶段,时代在发展为了快速开发,项目化运作,开始大量引进框架和第三方库,真的很好用(特别是项目开发),比如cglib,由于它是针对类实现的代理,故类不能是final类型。

    /**
     * 
     * @author dgm
     * @describe "cglib动态代理"
     * @date 2020年4月20日
     */
    public class ProductCglibDynamicProxy implements MethodInterceptor {
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args,
                MethodProxy methodProxy) throws Throwable {
            try {
                before(method);// 前置通知
                Object ret = methodProxy.invokeSuper(obj, args);// 目标方法执行
                after(method, ret);// 后置通知
                return ret;
            } catch (Exception e) {
                exception();// 异常通知
            } finally {
                afterReturning();// 方法返回通知
            }
            return null;
        }
    
        // 前置增强
        private void before(Method method) {
            System.out.printf("cglib 动态代理:%s", method.getName());
        }
    
        // 后置增强
        private void after(Method method, Object ret) {
            System.out.printf(",cglib动态代理:%s, ret:%s", method.getName(), ret);
        }
    
        // 异常增强
        private void exception() {
            System.out.println(",程序出错了!");
        }
    
        // after返回增强
        private void afterReturning() {
            System.out.println(",产品成型");
        }
    
    }
    
    
    /**
     * 
     * @author dgm
     * @describe "测试cglib动态代理"
     * @date 2020年4月20日
     */
    public class ProductCglibDynamicProxyTest {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            //代理对象
            ProductDao proxy = CglibProxyFactory.create();
            System.out.println("代理对象: "+proxy.getClass());
            //执行代理业务具体业务操作
            System.out.println(proxy.createProduct("手机屏幕", "4寸"));
        }
    
    }

    运行结果:

     spring-aop的另一实现也是基于此

    简单总结:

    1. 静态代理实现较简单,只要代理对象对目标对象进行包装,即可实现增强功能,但静态代理只能为一个目标对象服务,如果目标对象过多,则会产生很多代理类。
    2. JDK动态代理需要目标对象实现业务接口,代理类只需实现InvocationHandler接口。
    3. 静态代理在编译时产生class字节码文件,可以直接使用,效率高。
    4. 动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
    5. cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。

    延伸阅读:

    0. java动态代理原理及解析 https://www.sohu.com/a/246547051_132276

    1. Spring AOP 实现原理与 CGLIB 应用 https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/

    2. 从动态代理到SpringAop以及AspectJ风格  https://segmentfault.com/a/1190000015262333

    3. Java 动态代理机制分析及扩展,第 1 部分 https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

    4. spring AOP是什么?你都拿它做什么?  https://zhuanlan.zhihu.com/p/28097563

    5. The Proxy Pattern https://blogs.oracle.com/javamagazine/the-proxy-pattern

  • 相关阅读:
    解决加密PDF文档无法复制文字的问题
    Linux创建用户时让每个用户家目录中自带说明文档
    Linux基础命令cp之拷贝隐藏文件
    Linux基础命令之getent
    Linux用户和组管理命令-用户创建useradd
    Linux用户和组管理命令-用户删除userdel
    Linux用户和组管理命令-用户属性修改usermod
    Linux用户和组管理命令-切换用户su
    Linux-京西百花山
    tee命令
  • 原文地址:https://www.cnblogs.com/dongguangming/p/12735887.html
Copyright © 2011-2022 走看看