概述
代理模式的定义是:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。(代理对象只是实现控制,附加功能,真正的业务逻辑还是在对原对象的调用上)
举个栗子:
生活中,假设我们想邀请一位明星,那么并不是直接联系明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决
看个抽象点的图
在java中,代理可以分为3种:静态代理、动态代理、CGLib代理
静态代理
参考上面的类图,直接上代码
public interface HouseOwner{
void sellHouse();
}
public class HouseOwnerImpl implements HouseOwner{
@Override
public void sellHouse(){
System.out.println("Sell house success!");
}
}
public class HousePorxy implements HouseOwner{
private HouseOwner houseOwner;
public HouseProxy(HouseOwner houseOwner){
this.houseOwner = houseOwner;
}
@Override
public void sellHouse(){
System.out.println("Before selling house");
houseOwner.sellHouse();
System.out.println("After selling house");
}
}
public static void main(){
HouseOwner houseOwner = new HouseOwnerImpl();
HousePorxy houseProxy = new HousePorxy(houseOwner;
houseProxy.sellHouse();
}
课件,静态代理的特点主要是依赖接口,代理类实现跟委托类一样的接口,通过调用代理类同委托类一样的接口,实现对委托类的控制/功能添加,这也是静态代理的优点;
同时,静态代理也有缺点,
- 当如果要有服务添加的时候,静态代理会导致代理类的数量增加
- 一旦接口改变,将会带来很多修改
动态代理
动态代理也需要利用接口,但同时还利用了java的反射功能,这样我们就不再需要手动创建代理类,只需要创建动态处理器即可,也因此克服了静态代理的两个缺点。
public class DynamicProxyHandler implements InvocationHandler{
private Object object;
public DynamicProxyHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before selling house");
Object result = method.invoke(object, args);
System.out.println("After selling house");
return result;
}
}
public static void main(){
HouseOwner houseOwner = new HouseOwnerImpl();
HouseOwner houseOwnerProxy = (HouseOwner) Proxy.newProxyInstance(houseOwner.getClass().getClassLoader(),
houseOwner.getClass().getInterfaces(),
new DynamicProxyHandler(houseOwner));
houseOwnerProxy.sellHouse();
}
当然,上面的DynamicProxyHandler可以再进行精练实用,以工厂的形式出现,因为得到代理类只需要Proxy.newProxyInstance(...)当中的几个参数
- ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
- Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
- InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
public class DynamicProxyHandlerFactory{
private Object object;
public DynamicProxyHandlerFactory(Object object) {
this.object = object;
}
public Object getProxyObject() {
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before selling house");
Object result = method.invoke(object, args);
System.out.println("After selling house");
return result;
}
});
}
public static void main(String[] args) {
HouseOwner houseOwner = new HouseOwnerImpl();
DynamicProxyHandlerFactory factory = new DynamicProxyHandlerFactory(houseOwner);
HouseOwner houseOwnerProxy = (HouseOwner) factory.getProxyObject();
houseOwnerProxy.sellHouse();
}
}
这样子看上去确实舒服了很多,动态代理减少了对接口的依赖,但它还是依赖于接口,对于没有实现接口的类无法使用。
CGLib代理
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,就需要CGLib了,因此CGLib代理也叫子类代理。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础
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();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before selling house");
Object result = methodProxy.invoke(o, objects);
System.out.println("After selling house");
return result;
}
public static void main(String[] args) {
HouseOwner houseOwner = new HouseOwnerImpl();
CglibProxy cglibProxy = new CglibProxy();
HouseOwnerImpl houseOwner1 = (HouseOwnerImpl) cglibProxy.getInstance(houseOwner);
houseOwner1.sellHouse();
}
}
CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
为何要使用代理模式
- 中介作用
某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。 - 开闭原则,增加功能
代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。