代理模式
在代理模式里面,使用代理类控制对目标类的访问,通过代理对象控制对原对象的访问。这里的代理对象其实就是相当于是生活中的中介。
举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。
代理对象的存在隔离了用户和目标对象,并且可以通过给代理对象添加额外的功能来扩展目标类的功能,并且不用修改目标类的代码。总而言之,代理对象解决了直接访问对象而带来的问题。比如,要访问的对象在远程机器上面。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
用户只会访问代理对象,代理对象是目标对象的扩展,代理对象会调用目标对象,用户不会直接访问目标对象。
静态代理
静态代理是由程序员或是特定工具生成源代码,再对其进行编译,在程序员运行之前代理类的.class文件就已经被创建了。
BuyHouse接口:
public interface BuyHouse {
void buyHouse();
}
BuyHouse接口实现:
public class BuyHouseImpl implements BuyHouse{
@Override
public void buyHouse() {
System.out.println("I want to buy a house!");
}
}
代理对象的实现:
public class BuyHouseProxy implements BuyHouse{
private BuyHouse buyHouse;
public BuyHouseProxy(BuyHouse buyHouse) {
this.buyHouse=buyHouse;
}
@Override
public void buyHouse() {
System.out.println("before buy house");
buyHouse.buyHouse();
System.out.println("after buy house");
}
}
测试类实现:
public class ProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
buyHouse.buyHosue();
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
buyHouseProxy.buyHosue();
}
}
JDK动态代理
动态代理的代理对象是是在程序运行时通过java的反射机制生成的。在动态代理里面,我们并不需要为每一个服务都创建代理类,我们只需要实现一个代理处理器,真正的对象由JDK动态的为我们创建。
动态处理器:
public class DynamicProxyHandler implements InvocationHandler{
private Object object;
public DynamicProxyHandler(Object object) {
// TODO Auto-generated constructor stub
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before method");
Object res=method.invoke(object, args);
System.out.println("after method");
return res;
}
}
测试代码:
public class DynamicProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse=new BuyHouseImpl();
BuyHouse proxyBuyHouse=(BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(),
new Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHouse();
}
}
也可以直接将动态处理器写在Proxy.newProxyInstance方法中,这样代码比较简洁。
public class DynamicProxyTest {
public static void main(String[] args) {
final BuyHouse buyHouse=new BuyHouseImpl();
BuyHouse proxyBuyHouse=(BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(),
new Class[]{BuyHouse.class},
new InvocationHandler(){@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("before buy house");
Object resObject=method.invoke(buyHouse, args);
System.out.println("after buy house");
return resObject;
}});
proxyBuyHouse.buyHouse();
}
}
JDK实现代理,只需要实现newProxyInstance方法,该方法需要接受三个参数。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
loader:目标类的类加载器。
interfaces:目标类实现的接口集合,可以是一个,可以是多个。
invocationHandler:动态处理器,当调用目标类方法时会调用动态处理器的invoke方法,这时invoke(Object proxy, Method method,Object[] args)的参数proxy是代理类,method是调用的代理类对象方法,args则是方法的参数。
JDK动态代理的目标类一定要实现接口,否则不能用JDK的动态代理。
CGlib动态代理
创建CGlib代理类:
public class CglibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(final Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("买房前准备");
Object result = methodProxy.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
创建测试类:
public class CglibProxyTest {
public static void main(String[] args){
BuyHouse buyHouse = new BuyHouseImpl();
CglibProxy cglibProxy = new CglibProxy();
BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse);
buyHouseCglibProxy.buyHosue();
}
}
这里CGLib和JDK动态代理实现代理模式的方法不同,JDK动态代理实现是通过代理类和目标类实现相同的接口实现的,而CGLib是通过让代理类继承目标类来实现的。
CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。