何为代理?
- 说到代理,开发中随处可见。nginx服务器正向代理、反向代理,jdk5动态代理、cglib动态代理等。面试中也常问何为代理模式?
- 代理举例就是说同学A拥有买东西的能力,但是他不在自己做,让同学B去帮忙买回来。这个同学B就是代理。
- 代理有什么好处呢?同上,如果让同学B去帮忙买东西,同学B是不是就可以在买东西的时候动手脚啦?比如想办法花更少的钱买到一样的东西,多的就成B同学的了,大赚一笔。在代码中其实也一样,这个“动手脚”就是“多余”的工作,既不影响完成帮A同学买东西,还能赚钱“搞事”。将真正的实现类的方法抽离出来,需要有前置处理可以直接在代理中处理,通过代理可以让调用者与实现者之间解耦。参考博文:代理模式
动态代理
-
自我理解是所谓的动态代理,其实就是在代理模式中扮演代理的角色是根据需求动态去创建,不是一早定死的。在实际的使用中只需去使用不需要去关注细节实现,在jdk、三方包中已经实现好。(喜欢研究源码的兄弟可以看看源码实现,其实也蛮不错的)
-
JDK动态代理简单实现代码:
public class Main { public static void main(String[] args) { Student student = new Student(); StudentHandler studentHandler = new StudentHandler(student); Hello hello = (Hello) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), studentHandler); System.out.println("结果:" + hello.sayHello()); } /** * 接口 */ public interface Hello { Boolean sayHello(); } /** * 学生实现类 */ public static class Student implements Hello { public Boolean sayHello() { System.out.println("student say hello"); return false; } } /** * 学生代理类 */ public static class StudentHandler implements InvocationHandler { private Object student; public StudentHandler(Object student) { this.student = student; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //这里可以判断方法进行代理前置操作 System.out.println("调用方法:" + method.toString()); Object invoke = method.invoke(student, args); System.out.println("Student方法返回:" + invoke); return true; } } }
输出:
调用方法:public abstract java.lang.Boolean com.li.blog.Main$Hello.sayHello()
student say hello
Student方法返回:false
结果:true
- Proxy.newProxyInstance方法动态生成了动态类。实现InvocationHandler 类中的invoke方法在调用方法时就会进入次方法,在这个方法中可以进行方法前置处理,比如参数返回值等。源码阅读可参考:彻底搞懂jdk动态代理并自己动手写一个动态代理 从上面输出可以看出,调用的方法,返回值也做了处理。
- 比较常见的动态代理的实现就是JDK动态代理、cglib动态代理
JDK动态代理、cglib动态代理简单比较
JDK动态代理
- 最小化依赖关系,减少依赖意味着简化开发和维护,JDK 本身的支持,可能比 cglib 更加可靠。
- 平滑进行 JDK 版本升级,而字节码类库通常需要进行更新以保证在新版 Java 上能够使用。
- 代码实现简单。
cglib动态代理
- 有的时候调用目标可能不便实现额外接口,从某种角度看,限定调用者实现接口是有些侵入性的实践,类似 cglib 动态代理就没有这种限制。
- 只操作我们关心的类,而不必为其他相关类增加工作量。
- 高性能。
我工作中遇到使用动态代理的地方
- Spring AOP 实现默认使用JDK动态代理,可以设置使用cglib
- Spring JPA 持久层接口解析时
- Mybatis 接口xml映射