Java学习之动态代理

Posted Shadowdsp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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改一下就可以了。

附上一篇讲的不错的文章

以上是关于Java学习之动态代理的主要内容,如果未能解决你的问题,请参考以下文章

java学习之动态代理模式

Spring阶段性学习总结AOP编程入门学习之动态代理实现代码的优化

java mybatis学习之$和#区别,mapper代理接口,动态SQL,在日志中输出mybatis的sql语句

设计模式学习之代理模式

spring再学习之AOP准备

框架学习之Spring----AOP and jdbcTemplate