概述
代理模式是我们常用的设计模式之一。所谓的代理模式是指客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象,简单来说,就是不改变原有对象的情况下,对目标对象功能的扩展。代理模式主要注重的是过程。
应用场景
客户端不想直接与实际的对象交互,或者访问实际对象有困难,或者不属于客户端专业范围内,就需要通过代理对象来完成这项工作。
举个生活中的例子,小伙子刚来大城市打拼,需要租房子,自己找到合适的房子比较困难,正好中介手中有资源,小伙把房子要求告诉中介,中介根据要求提供小伙合适的房子,中介在这里就是一个代理对象。
静态代理
静态代理可以实现在不修改目标对象功能的前提下,对目标功能的扩展。
静态代理需要定义接口或者父类,代理对象和被代理对象需要实现相同接口或者继承相同父类。
下面用中介找房的做例子,代码示例:
1、定义找房接口:
public interface Handsome {
void findHouse(String name);
}
2、目标对象接口:
public class HandsomeImpl implements Handsome {
@Override
public void findHouse(String name) {
System.out.println("找个" + name + "的房子");
}
}
3、代理对象接口
public class IntermediaryProxy implements Handsome {
private Handsome handsome;
public IntermediaryProxy(Handsome handsome) {
this.handsome = handsome;
}
@Override
public void findHouse(String name) {
System.out.println("准备房源~");
handsome.findHouse("找个" + name + "的房子");
System.out.println("找到房子~");
}
}
4、测试
public class Test {
public static void main(String[] args) {
Handsome handsome = new HandsomeImpl();
Handsome proxySinger = new IntermediaryProxy(handsome);
handsome.findHouse("一室一厨一卫");
}
}
静态代理的缺点是代理对象需要实现和目标对象相同的接口,这样的话会产生很多的代理类,同时目标对象的接口每次需要增加,这样太过于繁琐。
动态代理
动态代理不需要代理对象实现父类的接口,通过实现Jdk或者Cglib的API接口,在内存中通过字节码重组的方式,对目标对象进行代理。
JDK代理
示例代码:
public class JdkProxy implements InvocationHandler {
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("准备房源!");
Object result = method.invoke(target, args);
System.out.println("找到房子!");
return result;
}
private Object getJdkProxy(Object targetObj) {
this.target = targetObj;
return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces(), this);
}
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy();
Handsome handsome = (Handsome) jdkProxy.getJdkProxy(new HandsomeImpl());
handsome.findHouse("一室一厨一卫");
}
}
上述代码中主要使用newProxyInstance方法,且接收的三个参数依次为:
ClassLoader loader:
指定当前目标对象使用类加载器,获取加载器的方法是固定的Class<?>[] interfaces:
目标对象实现的接口的类型,使用泛型方式确认类型InvocationHandler h:
事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
Cglib代理
示例代码:
public class CglibProxy implements MethodInterceptor {
private Object target;
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("准备房源!");
Object invoke = method.invoke(target, args);
System.out.println("找到房子!");
return invoke;
}
public Object getCglibProxy(Object objectTarget) {
this.target = objectTarget;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(objectTarget.getClass());
enhancer.setCallback(this);
Object result = enhancer.create();
return result;
}
public static void main(String[] args) {
CglibProxy cglib = new CglibProxy();
Handsome handsome = (Handsome) cglib.getCglibProxy(new HandsomeImpl());
handsome.findHouse("一室一厨一卫");
}
}
静态代理和Jdk代理模式都是要求目标对象是实现接口,而Cglib可以代理一个没有实现任何接口的目标对象。
示例代码:
1、目标对象
public class Handsome {
public void findHouse(String name) {
System.out.println("找个" + name + "的房子");
}
}
2、代理对象
public class CglibProxy implements MethodInterceptor {
private Object target;
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("准备房源!");
Object invoke = method.invoke(target, args);
System.out.println("找到房子!");
return invoke;
}
public Object getCglibProxy(Object objectTarget) {
this.target = objectTarget;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(objectTarget.getClass());
enhancer.setCallback(this);
Object result = enhancer.create();
return result;
}
public static void main(String[] args) {
CglibProxy cglib = new CglibProxy();
Handsome handsome = (Handsome) cglib.getCglibProxy(new Handsome());
handsome.findHouse("一室一厨一卫");
}
}
这种动态代理应用在Spring Aop中,容器中的目标对象有实现接口用JDK代理,目标对象没有实现接口用Cglib代理,也可通过配置强制容器使用Cglib代理。