参考博文:
https://blog.csdn.net/gdutxiaoxu/article/details/81394050
一,概述
代理的设计理念是限制对象的直接访问,即不能通过new直接访问对象,必须通过代理类
二,UML类图
Subject(抽象角色):声明真实对象和代理对象的共同接口;
ProxySubject(代理角色):代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
(RealSubject)真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。调用 RealSubject 的方法,都要经过 ProxySubject 进行代理。
三,静态代理
一般来说,主要有以下几个步骤
抽象一个接口 ISubject
实现该接口 RealSubject
创建代理对象类 ProxySubject
客户端发起调用
抽象一个接口 ISubject
public interface ISubject {
void doAction(String action);
}
实现该接口 RealSubject
public class RealSubject implements ISubject {
public void doAction(String action) {
// TODO Auto-generated method stub
System.out.println("I am RealSubject, do action "+ action);
}
}
创建代理对象类 ProxySubject
public class ProxySubject implements ISubject {
ISubject mRealSubject;
public ProxySubject(ISubject mRealSubject) {
super();
this.mRealSubject = mRealSubject;
}
public void doAction(String action) {
// TODO Auto-generated method stub
preRequest();
mRealSubject.doAction(action);
postRequest();
}
protected void postRequest() {
// TODO Auto-generated method stub
System.out.println("postRequest");
}
protected void preRequest() {
// TODO Auto-generated method stub
System.out.println("preRequest");
}
}
客户端发起调用
private static void testStatical() {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.doAction(“play”);
}
将会看到以下 log
preRequest
I am RealSubject, do action play
postRequest
四,动态代理
实现 InvocationHandler 接口,创建自己的调用处理器;
通过 Proxy.newProxyInstance 生成动态代理类实例
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target,args);
}
}
public class ClientTest {
public static void main(String[] args) {
Subject proxy = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),new Class<?>[]{Subject.class},new DynamicProxy(new RealObject()));
proxy.doSomething();
}
}
五,CGLIB代理
这种代理不要求被代理对象必须实现某个接口
使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码,下面通过一个例子看看使用CGLib如何实现动态代理。
1、定义业务逻辑
public class UserServiceImpl {
public void add() {
System.out.println(“This is add service”);
}
public void delete(int id) {
System.out.println("This is delete service:delete " + id );
}
}
2、实现MethodInterceptor接口,定义方法的拦截器
public class MyMethodInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {
System.out.println(“Before:” + method);
Object object = proxy.invokeSuper(obj, arg);
System.out.println(“After:” + method);
return object;
}
}
3、利用Enhancer类生成代理类;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class);
enhancer.setCallback(new MyMethodInterceptor());
UserServiceImpl userService = (UserServiceImpl)enhancer.create();
4、userService.add()的执行结果:
Before: add
This is add service
After: add
代理对象的生成过程由Enhancer类实现,大概步骤如下:
1、生成代理类Class的二进制字节码;
2、通过Class.forName加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。
六,代理模式与装饰器模式比较
装饰模式:以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案;
代理模式:给一个对象提供一个代理对象,并有代理对象来控制对原有对象的引用;
装饰模式应该为所装饰的对象增强功能;代理模式对代理的对象施加控制,并不提供对象本身的增强功能
二者的实现机制确实是一样的,可以看到他们的实例代码重复是很多的。但就语义上说,这两者的功能是相反的,模式的一个重要作用是简化其他程序员对你程序的理解,
你在一个地方写装饰,大家就知道这是在增加功能,你写代理,大家就知道是在限制,