1.代理模式
定义:代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的方法。
2.应用场景
(1)远程代理:也就是为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实,例如,RPC远程过程调用。
(2)虚拟代理:是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象,以达到性能的最优化。
(3)安全代理:用来控制真实对象访问时的权限。
(4)智能引用:是指当调用真实的对象时,代理处理另外一些事。如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它。
3.代理类型
下面我们通过一个购票流程来详细说明一下静态代理、动态代理和Cglib代理三种代理的优缺点。以前没有12306软件的时候,购买火车票只能到售票处购买,现在有了12306软件以后,可以实现在线购买火车票的功能了,售票处是真实实体,12306软件就是售票处的代理类。
(1)静态代理
package com.nwpu.pattern.proxy.statics;
public interface Subject {
void buyTicket();
}
package com.nwpu.pattern.proxy.statics;
public class TicketOffice implements Subject {
@Override
public void buyTicket() {
System.out.println("成功买到一张票!");
}
}
package com.nwpu.pattern.proxy.statics;
public class TicketSoftware implements Subject {
// 代理类维持一个真实实体的引用
private TicketOffice office;
public void setTicketOffice(TicketOffice office) {
this.office = office;
}
@Override
public void buyTicket() {
office.buyTicket();
}
}
package com.nwpu.pattern.proxy.statics;
public class Client {
public static void main(String[] args) {
TicketOffice office = new TicketOffice();
TicketSoftware software = new TicketSoftware();
software.setTicketOffice(office);
software.buyTicket();
}
}
从上述例子我们可以得知,静态代理的代理关系在编译期间就已确定,适合代理类较少且确定的场景,否则要新建大量的代理类。静态代理模式要求目标类和代理类要实现共同的接口,当目标类中有大量方法时,代理类也需要重写大量的方法,违背重复代码只写一次原则。此外,当接口新增方法时,代理类和目标类也需要实现此方法,增加了代码的维护成本。
(2)动态代理
package com.nwpu.pattern.proxy.dynamic;
public interface Subject {
void buyTicket();
}
package com.nwpu.pattern.proxy.dynamic;
public class TicketOffice implements Subject {
@Override
public void buyTicket() {
System.out.println("成功买到一张票!");
}
}
package com.nwpu.pattern.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private TicketOffice ticketOffice;
public void setTicketOffice(TicketOffice ticketOffice) {
this.ticketOffice = ticketOffice;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(ticketOffice, args);
return result;
}
}
package com.nwpu.pattern.proxy.dynamic;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
TicketOffice office = new TicketOffice();
MyInvocationHandler handler = new MyInvocationHandler();
handler.setTicketOffice(office);
Subject proxy = (Subject) Proxy.newProxyInstance(office.getClass().getClassLoader(),
office.getClass().getInterfaces(), handler);
proxy.buyTicket();
}
}
从上述示例中我们可以得知,利用动态代理可以实现在运行时创建一个实现了一组给定接口的新类,这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用到。
(3)Cglib代理
jdk动态代理依赖于接口的实现,而当我们只有类没有接口的时候就需要使用另一种动态代理技术 Cglib动态代理。Cglib代理是针对类来实现代理的,原理是对指定的目标类生成一个子类并重写其中业务方法来实现代理。
代理类对象是由 Enhancer 类创建的,Cglib创建动态代理类的模式如下:
- a.查找目标类上的所有非 final 的 public 类型的方法 (final 的不能被重写);
- b.将这些方法的定义转成字节码;
- c.将组成的字节码转换成相应的代理的 Class 对象然后通过反射获得代理类的实例对象;
- d.实现 MethodInterceptor 接口, 用来处理对代理类上所有方法的请求。
package com.nwpu.pattern.proxy.cglib;
public class TicketOffice {
public void buyTicket() {
System.out.println("成功买到一张票!");
}
}
package com.nwpu.pattern.proxy.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
public TicketOffice creatProxyedObj(CglibProxy cglibProxy) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TicketOffice.class);
enhancer.setCallback(cglibProxy);
return (TicketOffice) enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(object, args);
}
}
package com.nwpu.pattern.proxy.cglib;
public class Client {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
TicketOffice ticketOffice = proxy.creatProxyedObj(proxy);
ticketOffice.buyTicket();
}
}
从上述示例我们可以得知:对于需要被代理的目标类,它只是动态生成一个子类以覆盖非 final 的方法,同时绑定钩子回调自定义的拦截器。不同于jdk动态代理,我们不能使用目标对象来创建代理,目标对象只能被 Cglib创建。在示例中,默认的无参构造方法enhancer.create()被使用来创建目标对象。
原创不易,如需转载,请注明出处@author Davince!