一、定义
代理模式:给某一个对象提供一个代理,由代理来控制对原对象的引用。
二、分类
1 静态代理:在编译阶段就生成代理类完成对代理对象的操作。
a、静态代理的参与者
主题接口:代理类实现的接口
目标对象:被代理对象
代理对象:用来封装调用真实主题类的代理类
b、代理类实现逻辑
目标对象和代理对象均要实现同一个行为接口
代理类和目标类分别具体实现接口
代理类的构造函数中实例化一个目标类
在代理类中实现调用目标对象的接口
c、静态代理实例
主题接口
public interface Theme {
void sayHelloWorld();
void sayGoodByeWorld();
}
目标类
public class RealTheme implements Theme {
@Override
public void sayHelloWorld() {
System.out.println("Hello World");
}
@Override
public void sayGoodByeWorld() {
System.out.println("GoodBye World");
}
}
代理类
public class StaticProxyTheme implements Theme {
private RealTheme realTheme = null;
@Override
public void sayHelloWorld() {
//懒加载,需要时才加载
if (realTheme == null){
realTheme = new RealTheme();
}
realTheme.sayHelloWorld();
}
@Override
public void sayGoodByeWorld() {
if (realTheme == null){
realTheme = new RealTheme();
}
realTheme.sayGoodByeWorld();
}
}
客户端
public class Client {
public static void main(String[] args) {
StaticProxyTheme staticProxyTheme = new StaticProxyTheme();
staticProxyTheme.sayHelloWorld();
staticProxyTheme.sayGoodByeWorld();
}
}
2 动态代理
动态代理:顾名思义,是在运行中动态生成代理类。即代理类字节码在运行时生成并载入ClassLoader。
好处:不需要为真实主题类写一个形式相似的封装类。假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统维护。
生成动态代理的方法有很多: JDK中自带动态代理, CGlib。
a、jdk自带代理类实例
public class DynamicProxy implements InvocationHandler {
private RealTheme realTheme = null;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (realTheme == null){
realTheme = new RealTheme();
}
method.invoke(realTheme, args);
return realTheme;
}
}
b、客户端实例
public class Client {
public static void main(String[] args) {
Theme theme = (Theme) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),RealTheme.class.getInterfaces(),new DynamicProxy());
theme.sayHelloWorld();
theme.sayGoodByeWorld();
}
}
从上面的代码中可看出,利用静态方法Proxy.newProxyInstance(ClassLoader, Interfaces[], InvokeHandler)可创建一个代理类,三个参数分别为类加载器,需要代理的接口列表,实现InvokeHandler接口的实例。
2.1 CGLib动态代理
因为java的单继承,jdk生成的代理类已经继承了Proxy类,所以jdk实现的动态代理无法实现继承式代理,所以就有了cglib来实现继承式的动态代理。最成功的应用就是Spring 中有cglib代理。
a 具体主题
public class Train {
public void move(){
System.out.println("move to Bj");
}
}
b 生成代理
public class CGLibProxy implements MethodInterceptor {
//字节码增强器,它可以方便的对你想要处理的类进行扩展
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 拦截所有目标类方法的调用
* 参数:
* obj目标实例对象
*method 目标方法的反射对象
* args方法的参数
* proxy代理类的实例
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//代理类调用父类的方法
System.out.println("日志开始");
methodProxy.invokeSuper(o, objects);
System.out.println("日志结束");
return null;
}
}
c 测试
public class CGLibProxyTest {
public static void main(String[] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
Train train = (Train) cgLibProxy.getProxy(Train.class);
train.move();
}
}
结果:
小结:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。Proxy 很美很强大,但是仅支持 interface 代理。Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。好在有cglib为Proxy提供了弥补。