zoukankan      html  css  js  c++  java
  • 设计模式之动态代理

    一、动态代理概念

    动态代理分为JDK动态代理和cglib动态代理两种方式。

    jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。

    总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。

    还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。

    由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

    二、JDK动态代理

    以下代码使用代理模式实现一个大小写字符转换的功能。

    定义接口和实现类:

    ISomeService接口:

    package com.ietree.basicskill.designpattern.dynamicproxy.jdk;
    
    /**
     * 接口类
     * 
     * @author Root
     */
    public interface ISomeService {
        
        String doFirst();
        
        void doSecond();
    }

    SomeServiceImpl实现类:

    package com.ietree.basicskill.designpattern.dynamicproxy.jdk;
    
    /**
     * 实现类
     * 
     * @author Root
     */
    public class SomeServiceImpl implements ISomeService {
    
        @Override
        public String doFirst() {
            System.out.println("执行doFirst()...");
            String result = "abcde";
            return result;
        }
    
        @Override
        public void doSecond() {
            System.out.println("执行doSecond()...");
        }
    
    }

    JDK动态代理类:

    package com.ietree.basicskill.designpattern.dynamicproxy.jdk;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class Main {
        public static void main(String[] args) {
    
            final ISomeService target = new SomeServiceImpl();
            
            // 使用JDK的Proxy动态代理,要求目标类和代理类必须实现相同的接口,因为其底层的执行原理与静态代理的相同
            ISomeService service = (ISomeService) Proxy.newProxyInstance(
                    // 目标类的类加载器
                    target.getClass().getClassLoader(),
                    // 目标类所实现的所有接口
                    target.getClass().getInterfaces(), 
                    new InvocationHandler() {
                        // proxy:代理对象
                        // method:目标方法
                        // args:目标方法的参数列表
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // 调用目标方法
                            Object result = method.invoke(target, args);
                            if (result != null) {
                                result = ((String) result).toUpperCase();
                            }
                            return result;
                        }
                    });
            String result = service.doFirst();
            System.out.println(result);
    
            service.doSecond();
        }
    }

    三、cglib动态代理

    Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用,它的运行速度要远远快于JDK的Proxy动态代理:

    CGLIB的核心类:
        net.sf.cglib.proxy.Enhancer – 主要的增强类
        net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
        net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
        Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。

    net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
    public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;

    第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

    以下程序实现了大小写转换的功能:

    实现类SomeService:

    package com.ietree.basicskill.designpattern.dynamicproxy.cglib;
    
    /**
     * 实现类
     * 
     * @author Root
     */
    public class SomeService {
    
        public String doFirst() {
            System.out.println("执行doFirst()...");
            String result = "abcde";
            return result;
        }
    
        public void doSecond() {
            System.out.println("执行doSecond()...");
        }
    
    }

    代理类MyCglibFactory:

    package com.ietree.basicskill.designpattern.dynamicproxy.cglib;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class MyCglibFactory implements MethodInterceptor {
    
        private SomeService target;
        
        public MyCglibFactory() {
            super();
            target = new SomeService();
        }
    
        public SomeService myCglibCreator() {
            // 创建增强器对象
            Enhancer enhancer = new Enhancer();
            // 指定目标类,即父类
            enhancer.setSuperclass(SomeService.class);
            // 设置回调接口对象
            enhancer.setCallback(this);
    
            return (SomeService) enhancer.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            // 调用目标方法
            Object result = method.invoke(target, args);
            if (result != null) {
                result = ((String) result).toUpperCase();
            }
            return result;
        }
    
    }

    测试:

    package com.ietree.basicskill.designpattern.dynamicproxy.cglib;
    
    public class Main {
        public static void main(String[] args) {
    
            SomeService service = new MyCglibFactory().myCglibCreator();
            
            String result = service.doFirst();
            System.out.println("result = " + result);
    
            service.doSecond();
        }
    }

    运行结果:

    执行doFirst()...
    result = ABCDE
    执行doSecond()...
  • 相关阅读:
    解决 “invalid resource directory name”, resource “crunch”
    Unable to resolve target 'android-19'
    android SDK安装容易出错的原因
    Eclipse启动认定SDK地址,出现Error executing aapt
    解决Android SDK Manager更新下载速度慢
    Vue框架
    JSON
    Django之组件--cookie与session
    Django之组件--中间件
    Django之组件--forms
  • 原文地址:https://www.cnblogs.com/Dylansuns/p/6985711.html
Copyright © 2011-2022 走看看