买东西有代购,想请假要代理,创建对象你会用动态代理吗?

下一篇文章《Java 设计模式 —— 动态代理模式(拦截器)》将讲述如何利用反射,将动态代理模式封装成拦截器来使用


动态代理模式就是生成一个代理对象,来代替真实对象控制对真实对象的访问。


举一个非常简单的例子:

我们公司的高总日理万机,每天要处理上百个需求,忙的那是焦头烂额,正处于崩溃边缘……
有一天,高总突然开窍了,他为自己招募到了一个新员工——小彭,作为自己的代理人。

自从小彭做了高总的代理人,高总开始天天坐在办公室哼个小曲,喝个小茶,瞬间感觉都年轻了许多。

为什么呢?

原来,所有来找高总谈需求的客户,都要先经过小彭的接待,然后再进行筛选,低于一个亿的项目免谈!
超过一个亿的项目才有机会见到高总去继续谈需求
谈完之后,不管成功与否,高总只需要喊一声:小彭,送客!
然后继续做着喝茶哼曲儿,乐得轻松

在上述例子中,高总就是真实对象小彭就是代理对象,而客户就是调用者

显而易见,代理对象的作用,就是在真实对象被访问之前(洽谈需求之前)或者之后(洽谈需求之后)加入对应的逻辑(之前:接待客人;之后:送客人离开),或者根据自定义的规则来控制是否使用真实对象(小于一个亿的项目不允许访问高总)。


常见的动态代理模式有两种:一种是 JDK 动态代理,必须借助接口实现;另一种是 CGLIB 动态代理,使用非抽象类实现。

JDK 动态代理

JDK 动态代理必须借助接口才能产生代理对象。

  • 首先,我们定义一个接口,并实现它

    package test;
    
    public interface LeaderGao {
           
        void talkingWithCustomers();
    } 
    
    package test;
    
    public class LeaderGaoImpl implements LeaderGao {
           
        @Override
        public void talkingWithCustomers() {
           
            System.out.println("与客户洽谈中 ...");
        }
    }
    
  • 接下来,建立代理对象和真实对象的关系并获取代理对象,实现代理逻辑。JDK 动态代理的实现代理逻辑类必须实现 java.lang.reflect.InvocationHandler 接口

    package test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class JDKProxyExample implements InvocationHandler {
           
    
        // 定义全局变量,保存真实对象
        private Object target = null;
        /**
         * 代理方法逻辑
         * @param proxy 代理对象(就是 bind 方法生成的对象)
         * @param method    当前调度的方法
         * @param args  当前调度方法参数
         * @return  代理结果返回
         * @throws Throwable    异常
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           
            System.out.println("进入代理逻辑方法 —— 见到了代理人小彭");
            System.out.println("在访问真实对象之前 —— 接待客人并筛选客人是否可以访问高总");
            Object obj = method.invoke(target, args);
            System.out.println("在访问真实对象之后 —— 送客");
            return obj;
        }
    
        /**
         * 建立代理对象和真是对象的代理关系,并返回代理对象
         * @param target 真实对象
         * @return 代理对象
         */
        public Object bind(Object target){
           
            // 保存真实对象
            this.target = target;
            // 建立并生成代理对象
            /* Proxy.newProxyInstance 方法需要三个参数:
                1、类加载器。
                    这里采用 target 类本身的加载器
                2、表示最终生成的动态代理对象挂载在哪些接口下。
                    这里将其挂在 target 类实现的接口下
                3、定义实现方法逻辑的代理类,必须实现 InvocationHandler 的 invoke 方法
                    这里 this 表示当前对象
             */
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this );
        }
    }
    
    
  • 测试 JDK 动态代理

    package test;
    
    import org.junit.Test;
    
    public class TestProxyPattern {
           
        @Test
        public void testJdkProxy(){
           
            JDKProxyExample jdk = new JDKProxyExample();
            // 绑定关系,传入真实对象,因为挂在接口 LeaderGao 下,所以声明代理对象 LeaderGao。
            // JDK 动态代理之所以必须借助一个接口的,就是因为其创建动态代理必须传入一个参数(动态代理挂在的接口),而这个接口,就是在这里获取到动态代理之后的对象类型
            LeaderGao proxy = (LeaderGao)jdk.bind(new LeaderGaoImpl());
            // 此时,HelloWorld 对象已经是一个代理对象,它会进入代理的逻辑方法 invoke 里
            proxy.talkingWithCustomers();
        }
    }
    
    
  • 运行结果

CGLIB 动态代理

CGLIB 动态代理不需要提供接口,但是它是第三方包,需要手动去下载(部分框架中已经集成)。

  • 首先,需要下载 cglib jar 包并 add 到 classpath 中。下载链接: http://www.java2s.com/Code/Jar/c/Downloadcglibnodep213jar.htm,如果链接失效,大家自行搜索即可;如果环境中已经集成,跳过该步骤。

买东西有代购,想请假要代理,创建对象你会用动态代理吗?_第1张图片

  • 接下来,定义一个类,不实现任何接口,因此也无法使用 JDK 动态代理

    package test;
    
    public class LeaderGaoImpl {
           
        public void talkingWithCustomers() {
           
            System.out.println("高总正在与客户洽谈中 ...");
        }
    }
    
  • 然后,建立代理对象和真实对象的关系,获取代理对象,实现代理逻辑。

    package test;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    
    /**
     * CGLIB 动态代理对象获取及代理逻辑。
     * 至于这里为什么实现 MethodInterceptor 接口。
     * 		是因为使用 CGLIB 动态代理时,其代理类需要实现 MethodInterceptor 接口
     * 		而这里,我们 echancer.setCallback(this) 的意义就是将当前对象设置为代理类,所以当前对象就需要实现该接口,并复写 intercept 方法提供代理逻辑
     */
    public class CGLIBProxyExample implements MethodInterceptor {
           
        // 定义全局变量,保存真实对象
        private Object target = null;
        /**
         * 生成 CGLIB 代理对象
         * @param target 真实对象
         * @return  真实对象的 CGLIB 代理对象
         */
        public Object bind(Object target){
           
            this.target = target;
            // CGLIB 的增强类对象 enhancer
            Enhancer enhancer = new Enhancer();
            // 设置增强类型
            enhancer.setSuperclass(this.target.getClass());
            // 定义代理逻辑对象为当前对象 this。要求当前对象必须实现 MethodInterceptor 接口(intercept 方法)
            enhancer.setCallback(this);
            // 设置类加载器
            enhancer.setClassLoader(target.getClass().getClassLoader());
            // 生成并返回代理对象
            return enhancer.create();
        }
    
        /**
         * 代理逻辑方法
         * @param proxy 代理对象
         * @param method    方法
         * @param args  方法参数
         * @param methodProxy   方法代理
         * @return  代理逻辑返回
         * @throws Throwable
         */
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable{
           
            System.out.println("进入代理逻辑方法 —— 见到了代理人小彭");
            System.out.println("在访问真实对象之前 —— 接待客人并筛选客人是否可以访问高总");
            Object obj = methodProxy.invokeSuper(proxy, args);
            System.out.println("在访问真实对象之后 —— 送客");
            return obj;
        }
    }
    
    
  • 最后,测试 CGLIB 动态代理

    package test;
    
    import org.junit.Test;
    
    public class TestProxyPattern {
           
        @Test
        public void testCGLIBProxy(){
           
            CGLIBProxyExample cglibProxyExample = new CGLIBProxyExample();
            LeaderGaoImpl proxyObj = (LeaderGaoImpl)cglibProxyExample.bind(new LeaderGaoImpl());
            proxyObj.talkingWithCustomers();
        }
    }
    
    
  • 运行结果

  • 最后,友情提示,如果遇到了如下报错,请检查是否正确引入 cglib 包,建议使用 cglib-nodep-2.1.3.jar 版本

    java.lang.NoClassDefFoundError: Could not initialize class net.sf.cglib.proxy.Enhancer

你可能感兴趣的:(『Java』,#,●,Java,设计模式,Java,设计模式,动态代理,JDK动态代理,CGLIB动态代理)