zoukankan      html  css  js  c++  java
  • Java学习之动态代理

    Java动态代理

    为什么使用动态代理

    当需要对某个类的某个方法进行修饰(增强)的时候,可以使用继承、装饰者模式和动态代理。

    三种方式局限性:

    1. 继承:增强的对象不能改变,增强的内容不能改变。
    2. 装饰者模式:增强的对象不能改变,增强的内容能改变。
    3. 动态代理:增强的对象可以改变,增强的内容可以改变。

    使用动态代理可以更加灵活地提高代码的复用程度。

    举个栗子:
    对于计算机教师这个类,有个teach()方法,时过境迁,旧的教学模式可能已经不适合高速发展的计算机行业了,因此在使用这个teach()方法可能需要前面或者后面要增加一点东西。但是对于老旧的项目,我们很难去理解前人的写法和思维方式,因此不能轻易修改他们的代码,所以我们可以使用代理去实现对teach()方法的增强(我这里分为前置增强和后置增强)。

    如何使用动态代理

    Object Proxy.newInstance(ClassLoader loader, Class[] interfaces, InvocationHandler ih)

    1. loader:类加载器,将类加载到内存当中。
    2. interfaces:代理对象要实现的接口。
    3. ih:InvocationHandler,其中实现invoke()方法,来实现对目标对象的增强。
    4. 返回值:jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象

    InvocationHandler接口

    该接口需要实现invoke()方法。

    Object invoke(Object proxy, Method method, Object[] args)

    1. proxy:代理对象。
    2. method:代理对象使用的目标方法。
    3. args:目标方法的参数。
    4. 返回值:method方法的返回值。

    在使用代理对象使用某个方法的时候,会调用生成代理对象时候传入的InvocationHandler的invoke()方法,因此可以在这个invoke()里面写需要增强的内容。

    Demo

    先给出两个接口和一个实现类

    public interface Subject {
        public int outPut(int num);
    }
    
    public interface BSubject {
        public int outPut2(int num);
    }
    
    public class realSubject implements Subject, BSubject {
        @Override
        public int outPut(int num) {
            System.out.println("outPut " + String.valueOf(num) + " people");
            return num;
        }
    
        @Override
        public int outPut2(int num) {
            System.out.println("outPut2 " + String.valueOf(num) + " people");
            return num;
        }
    }
    

    使用动态代理

    前置增强接口:

    public interface BeforeAdvance { // 前置增强接口
        public void before();
    }
    

    后置增强接口:

    public interface AfterAdvance { // 后置增强接口
        public void after();
    }
    

    创建代理工厂,可以更加方便调用:

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyFactory {
        private Object targetObject; // 目标代理对象
        private BeforeAdvance beforeAdvance; // 前置增强
        private AfterAdvance afterAdvance; // 后置增强
    
        public Object createFactory() {
            ClassLoader loader = this.getClass().getClassLoader(); // 加载.class到内存
            Class[] interfaces = targetObject.getClass().getInterfaces(); // 得到realSubject所有接口(Waiter), 给代理对象提供了一组接口,这个代理对象就会实现这组接口,
            InvocationHandler ih = new InvocationHandler() {
                @Override
                // proxy 是代理对象,是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象
                // Method 是目标对象需要增强的方法
                // args是实参
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if(beforeAdvance != null) { // 调用前置增强方法
                        beforeAdvance.before();
                    }
                    Object result = method.invoke(targetObject, args); // 通过反射调用目标代理对象的目标方法, result是目标方法的返回值
                    if(afterAdvance != null) { // 调用后置增强方法
                        afterAdvance.after();
                    }
                    return result;
                }
            };
            return Proxy.newProxyInstance(loader, interfaces, ih); // 得到一个代理对象
    
        }
    
        public AfterAdvance getAfterAdvance() {
            return afterAdvance;
        }
    
        public void setAfterAdvance(AfterAdvance afterAdvance) {
            this.afterAdvance = afterAdvance;
        }
    
        public BeforeAdvance getBeforeAdvance() {
            return beforeAdvance;
        }
    
        public void setBeforeAdvance(BeforeAdvance beforeAdvance) {
            this.beforeAdvance = beforeAdvance;
        }
    
        public Object getTargetObject() {
            return targetObject;
        }
    
        public void setTargetObject(Object targetObject) {
            this.targetObject = targetObject;
        }
    }
    

    示例:

    import org.junit.Test;
    
    public class Demo {
        @Test
        public void fun() {
            ProxyFactory proxyfactory = new ProxyFactory();
            // 代理的目标对象
            proxyfactory.setTargetObject(new realSubject());
            // 前置增强接口实现
            proxyfactory.setBeforeAdvance(new BeforeAdvance() {
                @Override
                public void before() {
                    System.out.println("hello");
                }
            });
            // 后置增强接口实现
            proxyfactory.setAfterAdvance(new AfterAdvance() {
                @Override
                public void after() {
                    System.out.println("byebye");
                }
            });
            // 生成对Subject接口的实现类对象
            Subject subject = (Subject) proxyfactory.createFactory(); // 通过newInstance()得到一个代理对象,这个代理对象实现了Subject接口,因此可以强转为Subject接口类。
            BSubject subject2 = (BSubject) proxyfactory.createFactory();
            Object result = subject.outPut(100); // 对应InvocationHandler的invoke方法:subject为proxy, outPut为method, 参数为args
            System.out.println(result);
            System.out.println("-----");
            Object result2 = subject2.outPut2(50);
            System.out.println(result2);
        }
    }
    
    输出:
    hello
    outPut 100 people
    byebye
    100
    -----
    hello
    outPut2 50 people
    byebye
    50
    

    在这里面,只要使用反射,method.invoke()就可以调用代理的目标对象的该方法,然后实现前置增强BeforeAdvance接口和后置增强AfterAdvance接口,就可以实现对方法内容的增强了。
    其中不同接口的不同实现方法都可以重用BeforeAdvance和AfterAdvance。
    对于不同的增强对象也可以使用,只要targetObject改一下就可以了。

    附上一篇讲的不错的文章

  • 相关阅读:
    iOS 新建xib文件时,最外层view的约束问题
    React native 无法弹出调试控件的问题
    从GitHub下载demo时遇到的依赖问题
    Mac 解决 Sourcetree 同步代码总需要密码的问题
    Mac 安装JRE 1.8
    正则表达式-- (.*?) 或 (.*+)
    字符串内有多个#号,每俩#号为一组,JavaScript 截取每组#号之间的字符
    Js/jQuery实时监听input输入框值变化
    Redis设置密码
    redis本机能访问 远程不能访问的问题
  • 原文地址:https://www.cnblogs.com/fightfordream/p/7965479.html
Copyright © 2011-2022 走看看