代理模式(Proxy Pattern)是指为其他对象提供一种代理,以控制这个对象的访问。代理模式属于结构型设计模式。
代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口,对应代理接口(Subject);
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象,对应委托类(RealSubject);
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装,对应代理类(ProxySubject)
让我们举一个最长碰到的例子——找对象。
首先,我们需要一个抽象对象,那就是人。他本身包含一个找对象的方法。
public interface Person {
void findPartner();
}
接下来,真是对象,也就是你。找对象的要求也不高,只求看对眼。
public class Son implements Person {
@Override
public void findPartner() {
System.out.println("王八看绿豆");
}
}
最后,你的妈妈就要出动了。他会根据你的要求和自己的要求来帮你完成这个方法。
public class Mother {
private Son son;
public Mother(Son son) {
this.son = son;
}
public void findPartner(){
System.out.println("帮助物色对象");
son.findPartner();
System.out.println("对上眼了。");
}
}
而在外人看到的就是你的妈妈代理并扩展了你找对象的方法。
public class FindTest {
public static void main(String[] args) {
Mother mother = new Mother(new Son());
mother.findPartner();
}
}
最终结果
帮助物色对象
王八看绿豆
对上眼了。
静态代理类优缺点
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
动态代理和静态对比基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩展适应性更强。如果还以找对象为例,使用动态代理能够适应复杂的业务场景。 媒婆就属于需要面对复杂业务的职业。而实现的方式有JDK原生动态代理和CGLIB动态代理
首先创建一个顾客类,让他实现找对象方法,提出自己的需求。
public class Customer implements Person {
@Override
public void findPartner() {
System.out.println("肤白貌美大长腿!");
}
}
接着创建媒婆类,他需要实现 InvocationHandler
这个接口中的invoke()
方法,方法的调用都会转发到这个方法里。
接着是通过Proxy.newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler handler)
方法来动态获得代理对象。
newProxyInstance()
会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()
方法。而在invoke()
方法中我们还可以添加其他方法,如媒婆获取需求等等。public class JDKMatchmaker implements InvocationHandler {
private Object target;
public Object getInstance(Object target){
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object o = method.invoke(this.target,args);
after();
return o;
}
private void before(){
System.out.println("登记需求!");
System.out.println("开始物色!");
}
private void after(){
System.out.println("结束,收费。");
}
}
接下来就是见证奇迹的时刻——测试
public class JDKTest {
public static void main(String[] args) {
Person person = (Person) new JDKMatchmaker().getInstance(new Customer());
person.findPartner();
}
}
结果
登记需求!
开始物色!
肤白貌美大长腿!
结束,收费。
Java动态代理为我们提供了非常灵活的代理机制,但Java动态代理是基于接口的,如果没有接口,我们就需要使用CGLib的方式了。
CGLIB (Code Generation Library)是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。
/CGLib媒婆上线/
public class CGLibMatchmaker implements MethodInterceptor {
public Object getInstance(Class clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
private void before(){
System.out.println("登记需求!");
System.out.println("开始物色!");
}
private void after(){
System.out.println("结束,收费。");
}
}
媒婆类同样需要实现MethodInterceptor
中intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
的方法。通过getInstance()
方法中,CGLIB的Enhancer来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()方法得到代理对象获取代理对象。
通过调用MethodProxy.invokeSuper()方法,我们将调用转发给原始对象(不包括final修饰的方法,因为它不能被重载)。
优点: