JDK动态代理是JRE提供的类库,可以直接使用,不依赖第三方。
动态代理
先了解下静态代理
静态代理的缺点:由于代理类实现了抽象角色的接口,导致代理类无法通用。
一旦接口增加方法,目标对象与代理对象都要进行修改。
静态代理的优点:接口增加方法,代理对象无需进行修改。
基于接口的动态代理:
如静态代理的内容所描述的,静态代理受限于接口的实现。
动态代理就是通过使用反射,动态地获取抽象接口的类型,从而获取相关特性进行代理。
因动态代理能够为所有的委托方进行代理。
动态代理与静态代理的区别
- 静态代理需要自己写代理类并一一实现目标方法,且代理类必须实现与目标对象相同的接口。
- 动态代理不需要自己实现代理类,JDK动态代理是JRE提供的类库,可以直接使用,不依赖第三方。动态地在内存中构建代理对象(需要我们传入被代理类),并且默认实现所有目标方法。
1. 首先,有个程序员接口,里面包含敲代码和改bug方法。
package proxy;
public interface Programmer {
void typeCodes();// 敲代码
void fixBug();// 改BUG
}
2. 现在我们来一个实现类:程序员小周
package proxy;
public class ProgrammerZhou implements Programmer {
@Override
public void typeCodes() {
System.out.println("我再写Java代码。");
}
@Override
public void fixBug() {
System.out.println("好舒服,又可以改BUG了。");
}
}
虽然 程序员小周 实现了接口,但是他并不是代理类,因为他没有设置一个中间代理来控制访问原目标对象。
3. 接下来创建一个程序员的代理类,它不需要实现程序员接口,但是他却可以为程序员小周服务
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProgrammerProxy implements InvocationHandler {
//目标类,被代理对象
private Object target;
// 第一个参数为 Proxy 类类型实例,如匿名的 $proxy 实例
// 第二个参数为委托类的方法对象
// 第三个参数为委托类的方法参数
// 返回类型为委托类某个方法的执行结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里可以做增强
System.out.println("我除了会改Bug,我还会写Bug。");
Object result = method.invoke(target, args);
return result;
}
//生成代理类
public Object getProxyedObj(Object target) {
this.target = target;
// 第一个参数,是类的加载器
// 第二个参数是委托类的接口类型,证代理类返回的是同一个实现接口下的类型,保持代理类与抽象角色行为的一致
// 第三个参数就是代理类本身,即告诉代理类,代理类遇到某个委托类的方法时该调用哪个类下的invoke方法
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
4. 最后我们写一个客户端类(测试类)
package proxy;
public class Client {
public static void main(String[] args) {
ProgrammerProxy proxy = new ProgrammerProxy();
Programmer programmer = (Programmer) proxy.getProxyedObj(new ProgrammerZhou());
programmer.fixBug();
System.out.println("-----------------------");
programmer.typeCodes();
}
}
5. 运行结果:
总结
- 必须实现 InvocationHandler 接口,表明该类是一个动态代理执行类。
- InvocationHandler 接口内有一实现方法如下: public Object invoke(Object proxy, Method method, Object[] args) 。使用时需要重写这个方法
- 获取代理类,需要使用 Proxy.newProxyInstance 这个方法去获取Proxy对象(Proxy 类类型的实例)
- 再来看看 invoke 方法,用户调用代理对象的什么方法,实质上都是在调用处理器的 invoke 方法,通过该方法调用目标方法,它也有三个参数:
// 第一个参数为 Proxy 类类型实例,如匿名的 $proxy 实例
// 第二个参数为委托类的方法对象
// 第三个参数为委托类的方法参数
// 返回类型为委托类某个方法的执行结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ...
}
-
动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。 由于我们需要增强,这个增强是需要留给开发人员开发代码的, 因此代理类不能直接包含被代理对象,而是一个InvocationHandler, 该InvocationHandler包含被代理对象,并负责分发请求给被代理对象, 分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。