说到代理,脑袋中浮现一大堆代理相关的名词,代理模式,静态代理,jdk代理,cglib代理等等。
记忆特别深刻的是,一次面试中,一位面试官问我,spring的AOP核心采用的设计模式是什么什么模式,阅读过24种设计模式,以及阅读过spring源代码的我竟然答错了,回想起来,真是日了狗了,学过那么多遍的东西都忘记了,结果是装逼失败,自己要狠下心来,把代理都搞懂!
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。下面通过例子展示。
/** * 定义一个账户接口 * * @author Fighter * @date 2016-04-20 * */ public interface Count { // 查看账户方法 public void queryCount(); // 修改账户方法 public void updateCount(); }
/** * 委托类(包含业务逻辑) * * @author Fighter * @date 2016-04-20 * */ public class CountImpl implements Count { @Override public void queryCount() { System.out.println("查看账户方法..."); } @Override public void updateCount() { System.out.println("修改账户方法..."); } }
/** * 这是一个代理类(增强CountImpl实现类) * * @author Fighter * @date 2016-04-20 * */ public class CountProxy implements Count { private CountImpl countImpl; /** * 覆盖默认构造器 * * @param countImpl */ public CountProxy(CountImpl countImpl) { this.countImpl = countImpl; } @Override public void queryCount() { System.out.println("事务处理之前"); // 调用委托类的方法; countImpl.queryCount(); System.out.println("事务处理之后"); } @Override public void updateCount() { System.out.println("事务处理之前"); // 调用委托类的方法; countImpl.updateCount(); System.out.println("事务处理之后"); } }
/** *测试Count类 * * @author Fighter * @date 2016-04-20 * */ public class TestCount { public static void main(String[] args) { CountImpl countImpl = new CountImpl(); CountProxy countProxy = new CountProxy(countImpl); countProxy.updateCount(); countProxy.queryCount(); } }
特点:只能对实现了接口的类生产代理,不能针对类
/** * 创建业务接口,包含业务可以提供对外的接口 * * @author Fighter * @date 2016-04-19 * */ public interface UserService{ /** * 目标方法 */ public void add(); }
/** * 创建业务接口实现类 * * @author Fighter * @date 2016-04-19 * */ public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("----------add----------"); } }
/** * 自定义简单的Invocation,对接口提供的方法进行增强 * * @author Fighter * @date 2016-04-19 */ public class MyInvocationHandler implements InvocationHandler { //目标对象 private Object target; /** * 构造方法 * @param target 目标对象 */ public MyInvocationHandler(Object target) { super(); this.target=target; } <span style="white-space:pre"> </span>/** * 执行目标对象的方法 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在目标方法执行前简单打印一下 System.out.println("----------before----------"); //执行目标方法对象 Object result=method.invoke(target, args); //在目标方法执行之后简单打印一下 System.out.println("----------after----------"); return result; } /** * 获取目标对象的代理对象 * @return 代理对象 */ public Object getProxy(){ return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), this.target.getClass().getInterfaces(),this); } }
public class ProxyTest{ @Test public void testProxy() throws Throwable{ //实例化目标对象 UserService userService=new UserServiceImpl(); //实例化Invocation MyInvocationHandler invocationHandler=new MyInvocationHandler(userService); //根据目标生成代理对象 UserService proxy=(UserService)invocationHandler.getProxy(); //调用代理对象方法 proxy.add(); } }
CGLIB是一个强大的高性能的代码生成包。被广泛的许多AOP框架使用,如Spring的AOP和dynaop,为他们提供方法的interceptor(拦截),最流行的是OR Mapping工具Hibernate也是使用CGLIB来代理单端的single-ended(多对一和一对一)关联(对集合的延迟抓取是采用其他机制实现)。EsayMock和jMock是通过模仿(moke)对象来测试java代码的包。他们都是通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
我们先通过demo来快速了解CGLIB的使用示例。
/** * 这个是没有实现接口的实现类 * * @author student * */ public class BookFacadeImpl { public void addBook() { System.out.println("增加图书的普通方法..."); } }
/** * 使用cglib动态代理 * * @author student * */ public class BookFacadeCglib implements MethodInterceptor { private Object target; /** * 创建代理对象 * * @param target * @return */ public Object getInstance(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 obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("事物开始"); proxy.invokeSuper(obj, args); System.out.println("事物结束"); return null; } }
public class TestCglib { public static void main(String[] args) { BookFacadeCglib cglib=new BookFacadeCglib(); BookFacadeImpl bookCglib=(BookFacadeImpl)cglib.getInstance(new BookFacadeImpl()); bookCglib.addBook(); } }