Java学习之动态代理
Posted Shadowdsp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java学习之动态代理相关的知识,希望对你有一定的参考价值。
Java动态代理
为什么使用动态代理
当需要对某个类的某个方法进行修饰(增强)的时候,可以使用继承、装饰者模式和动态代理。
三种方式局限性:
- 继承:增强的对象不能改变,增强的内容不能改变。
- 装饰者模式:增强的对象不能改变,增强的内容能改变。
- 动态代理:增强的对象可以改变,增强的内容可以改变。
使用动态代理可以更加灵活地提高代码的复用程度。
举个栗子:
对于计算机教师这个类,有个teach()方法,时过境迁,旧的教学模式可能已经不适合高速发展的计算机行业了,因此在使用这个teach()方法可能需要前面或者后面要增加一点东西。但是对于老旧的项目,我们很难去理解前人的写法和思维方式,因此不能轻易修改他们的代码,所以我们可以使用代理去实现对teach()方法的增强(我这里分为前置增强和后置增强)。
如何使用动态代理
Object Proxy.newInstance(ClassLoader loader, Class[] interfaces, InvocationHandler ih)
- loader:类加载器,将类加载到内存当中。
- interfaces:代理对象要实现的接口。
- ih:InvocationHandler,其中实现invoke()方法,来实现对目标对象的增强。
- 返回值:jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象
InvocationHandler接口
该接口需要实现invoke()方法。
Object invoke(Object proxy, Method method, Object[] args)
- proxy:代理对象。
- method:代理对象使用的目标方法。
- args:目标方法的参数。
- 返回值: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学习之动态代理的主要内容,如果未能解决你的问题,请参考以下文章
Spring阶段性学习总结AOP编程入门学习之动态代理实现代码的优化