什么是代理(静态代理)
假设这样一个场景:
其他人(团队)写好的一个Java类,我们需要拿来使用,但发现该类并不能完全实现我们需要的功能,由于我们得到的是该类的字节码文件,无法对源码进行修改来扩展类的方法,此时就可以考虑代理的方法。
首先考虑静态代理。
//目标类
public class UtilClass implements UtilClassInterface {
public void method1(){
}
}
//代理类
public class ProxyUtilClass implements UtilClassInterface {
public void method2(){
UtilClass util=new UtilClass();
util.method1();
System.out.println("strengthen method");
}
}
在代理类中我创建了UtilClass的对象,调用其方法,但在方法调用后我自己添加的一句代码System.out.println("strengthen method"); 实现了原方法的功能增强。这就是代理的作用。
在B类无法直接访问A类时,可以通过A类的代理来间接访问A类。
静态代理的缺点
代码难以复用。
考虑以下情景: 目标类UtilClass和代理类都实现了接口UtilClassInterface,如果接口发生改变,目标类和代理类的代码都需要修改。另一方面一个目标类可能有多个代理类,那么接口的改变,其实现类的修改将是成倍的工作量。
什么是动态代理
使用JDK的反射机制创建代理类的对象。“动态”一方面指的是在程序运行时调用JDK的方法才能创建对象,另一方面指被代理的目标类可以根据传参来改变,而不是固定写好的Java代理类。
Tips: JDK代理的目标类必须是接口的实现类。
动态代理的实现
- 创建目标类
public interface UtilClassInterface {
void method();
}
public class Target implements UtilClassInterface{
public void method(){
System.out.println("target method");
}
}
- 创建InvocationHandler接口的实现类Handler,重写invoke方法
public class Handler implements InvocationHandler {
Object target=null;
//通过构造方法给目标对象赋值
public Handler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//反射访问目标方法
method.invoke(target,args);
//方法增强
System.out.println("strengthen method");
//返回目标方法的返回值
return null;
}
}
- 第三方类创建代理对象调用目标方法
public class Test {
public static void main(String[] args) {
//创建目标对象
Target target=new Target();
//创建Handler对象
Handler handler=new Handler(target);
//创建代理
//第一个参数是指定代理类的类加载器(我们传入target的类加载器)
//第二个参数是代理类需要实现的接口(我们传入被代理类实现的接口,
//这样生成的代理类和被代理类就实现了相同的接口)
//第三个参数是invocation handler,用来处理方法的调用。
//这里传入我们自己实现的handler
UtilClassInterface proxy=(UtilClassInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
//代理对象执行增强后的方法
proxy.method();
}
}
使用动态代理之后,我们不需要写代理类的Java文件就可以创建代理对象,并且代理的目标类由传入Handler构造方法的参数决定。当目标类的接口发生改变时,只需要修改目标类即可。