引言
我们在写一个功能函数的时候,常常要写入一些与功能无关的代码,如日志记录、参数校验、安全和事务支持等。功能无关代码混在函数中,会带来一些麻烦
- 对OO造成破坏
- 加深类之间的耦合
- 代码重用性降低
代理模式可以解决这个问题,关键功能代码放在函数中,枝节性代码放在代理类中
代理模式
1、作用
代理模式的作用是,为其他对象提供一种代理,在不改变接口的前提下,以控制对这个对象的访问
2、角色
代理模式一般涉及到3个角色:抽象角色(接口)、代理角色、真实(被代理)角色
3、类型
静态代理、动态代理(jdk代理、CGLIB代理)
静态代理
代理类、被代理类需要实现相同的接口,或者继承相同的父类
/** * Created by danny.yao on 2017/10/9. * 静态代理中,代理类和被代理类需要实现此接口 */ public interface IUser { public void doSomething(String thingsToDo); } /** * Created by danny.yao on 2017/10/9. * 被代理类,真实业务逻辑 */ public class Danny implements IUser { public void doSomething(String thingsToDo) { System.out.println(this.getClass().getName() + "一边唱歌一边" + thingsToDo); } } /** * Created by danny.yao on 2017/10/9. * 代理类,被代理类作为其属性,增强被代理类功能 */ public class UserProxy implements IUser { private IUser user; public UserProxy(IUser user){ this.user = user; } // 在不改变danny行为的基础上,增强功能 public void doSomething(String thingsToDo) { doBefore(); user.doSomething(thingsToDo); doAfter(); } private void doBefore(){ System.out.println("赖床。。。"); System.out.println("起床洗漱。。。"); System.out.println("吃早饭。。。"); System.out.println("然后。。。"); } private void doAfter(){ System.out.println("那啥之后。。。"); System.out.println("开始打王者荣耀!"); } }
测试类
public class UserProxyTest { @Test public void testDoSomething(){ UserProxy dannyProxy = new UserProxy(new Danny()); dannyProxy.doSomething("上班!!"); } }
缺点:
- 被代理类须已经存在,而且作为代理类的内部属性
- 每个业务类都要一个代理类,如果业务量庞大,会导致类急剧膨胀
动态代理
动态代理类的源码是在程序运行期间,由JVM根据反射等机制动态生成的
1、jdk代理
被代理类,须实现接口;代理类,不需要实现接口
// 接口和实现类,同静态代理 public class DynamicProxy implements InvocationHandler { // 把静态代理中的类引用,改为Object类型 private Object object; public DynamicProxy(Object object){ this.object = object; } // 把方法调用过程改为反射方式,不依赖接口的具体方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { doBefore(); method.invoke(object, args); doAfter(); return null; } private void doBefore(){ System.out.println("那啥之前。。。"); } private void doAfter(){ System.out.println("那啥之后。。。"); } // 返回动态代理 newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) public <T> T getProxy(){ return (T) Proxy.newProxyInstance( object.getClass().getClassLoader(), object.getClass().getInterfaces(), this ); } }
测试类
public class DynamicProxyTest { @Test public void testInvoke() throws Exception { DynamicProxy dynamicProxy = new DynamicProxy(new Danny()); IUser dannyProxy = dynamicProxy.getProxy(); dannyProxy.doSomething("上班!!"); } }
缺点:
- 被代理类必须实现接口
2、CGLIB代理
也叫作子类代理,它是在内存中构建一个子类对象,从而实现对目标对象功能的扩展。被代理类不需要实现接口
// 未实现接口的类 public class Pig { public void doSomething(String thingsToDo){ System.out.println("懒洋洋地" + thingsToDo); } } public class CGLibProxy implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { doBefore(); Object result = methodProxy.invokeSuper(o, args); doAfter(); return result; } // cglib 中通过加强器来创建代理 public <T> T getProxy(Class<T> tClass){ return (T) Enhancer.create(tClass, this); } private void doBefore(){ System.out.println("那啥之前。。。"); } private void doAfter(){ System.out.println("那啥之后。。。"); } }
测试类
public class CGLibProxyTest { @Test public void testIntercept() throws Exception { CGLibProxy cgLibProxy = new CGLibProxy(); Pig pigProxy = cgLibProxy.getProxy(Pig.class); pigProxy.doSomething("思考人生。。。"); } }
缺点:
- 会拦截并包装被代理类的所有方法,做不到方法级别的个性化拦截
参考:
https://www.zhihu.com/question/20794107
http://www.cnblogs.com/machine/archive/2013/02/21/2921345.html