代理模式的定义:给对象提供一个代理以控制对这个对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。属于结构型模式。
为什么使用代理模式:当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
代理模式的结构:
- 抽象主题(Subject)类:声明真实主体和代理对象要实现的业务方法,可以是接口或抽象类。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是负责执行真实业务逻辑的对象。
- 代理(Proxy)类:提供了与真实主题相同的方法,其内部含有对真实主题的引用,它会在代理对象的方法前后增加一些处理,已达到控制或扩展真实主题的功能。
代理模式的实现:代理模式有两种实现方式,静态代理和动态代理。
- 静态代理:在程序运行前,显示的创建代理类。
- 动态代理:在程序运行时,动态创建代理类。
静态代理的实现:
//抽象主体类
public interface ISubject {
void request();
}
//真实主体类
public class RealSubject implements ISubject{
@Override
public void request() {
System.out.println("处理真实业务逻辑...");
}
}
//代理类
public class Proxy implements ISubject{
private RealSubject realSubject;
public Proxy(RealSubject realSubject){
this.realSubject = realSubject;
}
@Override
public void request() {
beforeRequest();
realSubject.request();
afterRequest();
}
private void beforeRequest() {
System.out.println("前置处理...");
}
private void afterRequest() {
System.out.println("后置处理...");
}
}
//测试类
public class ProxyTest {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
Proxy proxy = new Proxy(realSubject);
proxy.request();
}
}
静态代理的结构图:
静态代理模式中存在两个问题:
- 真实主题与代理类一一对应,增加真实主题也要增加代理,没有一个通用的代理类。
- 设计代理以前真实主题必须事先存在,不太灵活。
针对静态代理中存在的问题,可以使用动态代理来解决。在Java中使用广泛的有Jdk动态代理和Cglib动态代理。
Jdk动态代理的实现:
//抽象主体类
public interface ISubject {
void request();
}
//真实主体类1
public class RealSubject1 implements ISubject{
@Override
public void request() {
System.out.println("处理真实业务逻辑...");
}
}
//真实主体类2
public class RealSubject2 implements ISubject{
@Override
public void request() {
System.out.println("处理另一套真实业务逻辑...");
}
}
//Jdk动态代理
public class JdkProxy implements InvocationHandler {
private ISubject target;
public ISubject getInstance(ISubject target){
this.target = target;
Class<? extends ISubject> clazz = target.getClass();
return (ISubject) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeRequest();
method.invoke(this.target,args);
afterRequest();
return null;
}
private void beforeRequest() {
System.out.println("前置处理...");
}
private void afterRequest() {
System.out.println("后置处理...");
}
}
//测试类
public class ProxyTest {
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy();
RealSubject1 realSubject1 = new RealSubject1();
ISubject proxy = jdkProxy.getInstance(realSubject1);
proxy.request();
System.out.println("---------------------分割线---------------------------");
RealSubject2 realSubject2 = new RealSubject2();
ISubject proxy2 = jdkProxy.getInstance(realSubject2);
proxy2.request();
}
}
Jdk动态代理的结构图:
Jdk动态代理采用字节码重组的方式重新生成对象来替代原始对象,已达到动态代理的目的。
Jdk动态代理生成对象的步骤:
- 通过反射获取被代理对象的引用,并且获取它的所有接口。
- Jdk动态代理类重新生成一个新的类,并实现被代理类实现的所有接口。
- 动态生成Java代码,将被代理类的业务逻辑进行增强。
- 编译新生成的Java代码为Class文件。
- 重新加载到JVM虚拟机运行。
Cglib动态代理的实现:
//抽象主体类
public interface ISubject {
void request();
}
//真实主体类1
public class RealSubject1 implements ISubject{
@Override
public void request() {
System.out.println("处理真实业务逻辑...");
}
}
//真实主体类2
public class RealSubject2 implements ISubject{
@Override
public void request() {
System.out.println("处理另一套真实业务逻辑...");
}
}
//Cglib动态代理
public class CglibProxy implements MethodInterceptor {
public ISubject getInstance(Class<?> clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return (ISubject) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeRequest();
methodProxy.invokeSuper(o,objects);
afterRequest();
return null;
}
private void beforeRequest() {
System.out.println("前置处理...");
}
private void afterRequest() {
System.out.println("后置处理...");
}
}
//测试类
public class ProxyTest {
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
RealSubject1 realSubject1 = new RealSubject1();
ISubject proxy = cglibProxy.getInstance(realSubject1.getClass());
proxy.request();
System.out.println("---------------------分割线---------------------------");
RealSubject2 realSubject2 = new RealSubject2();
ISubject proxy2 = cglibProxy.getInstance(realSubject2.getClass());
proxy2.request();
}
}
Cglib动态代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现动态代理的。
Cglib动态代理使用FastClass机制,它的实现原理就是为代理类和被代理类各生成一个类,这个类会为代理类或被代理类的方法分配index索引,FastClass根据index直接定位到要调用的方法并直接调用,省去了反射调用,所以调用效率比Jdk通过反射代理调用高。
FastClass并不是和代理类一起生成的,它是在第一次执行MethodProxy的invoke()或invokeSuper()方法时生成并放入缓存的。
Jdk和Cglib动态代理的对比:
- Jdk动态代理实现了被代理类的接口,Cglib代理继承了被代理类。
- Jdk动态代理和Cglib代理都是在运行期间生成字节码,Jdk动态代理直接生成Class字节码,Cglib代理使用ASM框架生成字节码,Cglib代理生成代理类比Jdk动态代理效率低。
- Jdk动态代理通过反射调用代理方法,Cglib代理通过FastClass机制直接调用,Cglib代理的执行效率比Jdk动态代理高。
代理模式的优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用。
- 代理对象可以扩展目标对象的功能。
- 代理模式使客户端与目标对象分离,降低了系统的耦合度,增加了程序的可扩展性。
代理模式的缺点:
- 代理模式会造成系统设计中类的数量增加。
- 在客户端和目标对象之间增加一个代理对象,会导致请求处理速度变慢。
- 增加了系统的复杂度。