详解java动态代理机制以及使用场景

(1)什么是代理?

大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲,代理这种设计模式是通过不直接访问被代理对象的方式,而访问被代理对象的方法。这个就好比 商户---->明星经纪人(代理)---->明星这种模式。我们可以不通过直接与明星对话的情况下,而通过明星经纪人(代理)与其产生间接对话。

(2)什么情况下使用代理?

(1)设计模式中有一个设计原则是开闭原则,是说对修改关闭对扩展开放,我们在工作中有时会接手很多前人的代码,里面代码逻辑让人摸不着头脑(sometimes the code is really like shit),这时就很难去下手修改代码,那么这时我们就可以通过代理对类进行增强。

(2)我们在使用RPC框架的时候,框架本身并不能提前知道各个业务方要调用哪些接口的哪些方法 。那么这个时候,就可用通过动态代理的方式来建立一个中间人给客户端使用,也方便框架进行搭建逻辑,某种程度上也是客户端代码和框架松耦合的一种表现。

(3)Spring的AOP机制就是采用动态代理的机制来实现切面编程。

(3)静态代理和动态代理

动态代理静态代理的区别,代理涉及到两个关联词代理类和委托类。静态代理一个代理类针对一个委托类!动态代理一个代理类可利用反射机制代理多个委托类

我们根据加载被代理类的时机不同,将代理分为静态代理和动态代理。如果我们在代码编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;如果不能确定,那么可以使用类的动态加载机制,在代码运行期间加载被代理的类这就是动态代理,比如RPC框架和Spring AOP机制。

(4)静态代理

我们先创建一个接口,遗憾的是java api代理机制求被代理类必须要实现某个接口,对于静态代理方式代理类也要实现和被代理类相同的接口;对于动态代理代理类则不需要显示的实现被代理类所实现的接口。

 
  1. /**

  2. * 顶层接口

  3. * @author yujie.wang

  4. *

  5. */

  6. public interface Person {

  7. public void sayHello(String content, int age);

  8. public void sayGoodBye(boolean seeAgin, double time);

  9. }

 
  1. /**

  2. * 需要被代理的类 实现了一个接口Person

  3. * @author yujie.wang

  4. *

  5. */

  6. public class Student implements Person{

  7.  
  8. @Override

  9. public void sayHello(String content, int age) {

  10. // TODO Auto-generated method stub

  11. System.out.println("student say hello" + content + " "+ age);

  12. }

  13.  
  14. @Override

  15. public void sayGoodBye(boolean seeAgin, double time) {

  16. // TODO Auto-generated method stub

  17. System.out.println("student sayGoodBye " + time + " "+ seeAgin);

  18. }

  19.  
  20. }

 
  1. /**

  2. * 静态代理,这个代理类也必须要实现和被代理类相同的Person接口

  3. * @author yujie.wang

  4. *

  5. */

  6. public class ProxyTest implements Person{

  7.  
  8. private Person o;

  9.  
  10. public ProxyTest(Person o){

  11. this.o = o;

  12. }

  13.  
  14. public static void main(String[] args) {

  15. // TODO Auto-generated method stub

  16. //s为被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问

  17. Student s = new Student();

  18. //创建代理类对象

  19. ProxyTest proxy = new ProxyTest(s);

  20. //调用代理类对象的方法

  21. proxy.sayHello("welcome to java", 20);

  22. System.out.println("******");

  23. //调用代理类对象的方法

  24. proxy.sayGoodBye(true, 100);

  25.  
  26. }

  27.  
  28. @Override

  29. public void sayHello(String content, int age) {

  30. // TODO Auto-generated method stub

  31. System.out.println("ProxyTest sayHello begin");

  32. //在代理类的方法中 间接访问被代理对象的方法

  33. o.sayHello(content, age);

  34. System.out.println("ProxyTest sayHello end");

  35. }

  36.  
  37. @Override

  38. public void sayGoodBye(boolean seeAgin, double time) {

  39. // TODO Auto-generated method stub

  40. System.out.println("ProxyTest sayHello begin");

  41. //在代理类的方法中 间接访问被代理对象的方法

  42. o.sayGoodBye(seeAgin, time);

  43. System.out.println("ProxyTest sayHello end");

  44. }

  45.  
  46. }

测试代码输出:

 
  1. ProxyTest sayHello begin

  2. student say hellowelcome to java 20

  3. ProxyTest sayHello end

  4. ******

  5. ProxyTest sayHello begin

  6. student sayGoodBye 100.0 true

  7. ProxyTest sayHello end

(5)动态代理

关于动态代理模式里面有两种实现,一种是jdk实现,一种是cglib来实现
下面来整jdk来实现动态代理的Java实例。
jdk动态代理模式里面有个拦截器的概念,在jdk中,只要实现了InvocationHandler这个接口的类就是一个拦截器类
还使用了些反射的相关概念。
拦截器的概念不了解没关系,假如写了个请求到action,经过拦截器,然后才会到action。然后继续有之后的操作。
拦截器就像一个过滤网,一层层的过滤,只要满足一定条件,才能继续向后执行。
拦截器的作用:控制目标对象的目标方法的执行。

拦截器的具体操作步骤:
1.引入类:目标类和一些扩展方法相关的类。
2.赋值:调用构造函数给相关对象赋值
3.合并逻辑处理:在invoke方法中把所有的逻辑结合在一起。最终决定目标方法是否被调用。


下面看具体的代码实例:

目标接口类:

 

 
  1. package com.lxk.designPattern.proxy.dynamicProxy.jdkDynamicProxy;

  2.  
  3. /**

  4. * 目标接口:

  5. * 包含目标方法的声明

  6. */

  7. public interface TargetInterface {

  8. /**

  9. * 目标方法

  10. */

  11. void business();

  12. }

 

 

目标类:

 
  1. package com.lxk.designPattern.proxy.dynamicProxy.jdkDynamicProxy;

  2.  
  3. /**

  4. * 被代理的类

  5. * 目标对象类

  6. * 实现目标接口.

  7. * 继而实现目标方法。

  8. */

  9. public class TargetObject implements TargetInterface {

  10.  
  11. /**

  12. * 目标方法(即目标操作)

  13. */

  14. @Override

  15. public void business() {

  16. System.out.println("business");

  17. }

  18.  
  19. }


拦截器:

 

 
  1. package com.lxk.designPattern.proxy.dynamicProxy.jdkDynamicProxy;

  2.  
  3. import java.lang.reflect.InvocationHandler;

  4. import java.lang.reflect.Method;

  5.  
  6. /**

  7. * 动态代理-拦截器

  8. * <p>

  9. * Created by lxk on 2016/11/25

  10. */

  11. public class MyInterceptor implements InvocationHandler {

  12. private Object target;//目标类

  13.  
  14. public MyInterceptor(Object target) {

  15. this.target = target;

  16. }

  17.  
  18. /**

  19. * args 目标方法的参数

  20. * method 目标方法

  21. */

  22. @Override

  23. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  24. System.out.println("aaaaa");//切面方法a();

  25. //。。。

  26. method.invoke(this.target, args);//调用目标类的目标方法

  27. //。。。

  28. System.out.println("bbbbb");//切面方法f();

  29. return null;

  30. }

  31. }


具体通过调用代理对象,来调用目标对象的目标方法的具体测试:

 

 

 
  1. package com.lxk.designPattern.proxy.dynamicProxy.jdkDynamicProxy;

  2.  
  3. import java.lang.reflect.Proxy;

  4.  
  5. public class MainTest {

  6. public static void main(String[] args) {

  7. //目标对象

  8. TargetObject target = new TargetObject();

  9. //拦截器

  10. MyInterceptor myInterceptor = new MyInterceptor(target);

  11.  
  12. /*

  13. * Proxy.newProxyInstance参数:

  14. * 1、目标类的类加载器

  15. * 2、目标类的所有的接口

  16. * 3、拦截器

  17. */

  18. //代理对象,调用系统方法自动生成

  19. TargetInterface proxyObj = (TargetInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), myInterceptor);

  20. proxyObj.business();

  21. }

  22. }


看完代码实例,要对这个动态代理进一步理解,要考虑到以下的问题。
1、代理对象是由谁产生的?

jvm产生的,不像上次的静态代理,我们自己得new个代理对象出来。
2、代理对象实现了什么接口?
实现的接口是目标对象实现的接口。同静态代理模式中代理对象实现的接口。那个继承关系图还是相同的。代理对象和目标对象都实现一个共同的接口。就是这个接口。所以Proxy.newProxyInstance()方法返回的类型就是这个接口类型。
3、代理对象的方法体是什么?
代理对象的方法体中的内容就是拦截器中invoke方法中的内容。所有代理对象的处理逻辑,控制是否执行目标对象的目标方法。都是在这个方法里面处理的。
4、拦截器中的invoke方法中的method参数是在什么时候赋值的?
在客户端,代理对象调用目标方法的时候,此实例中为:proxyObj.business();实际上进入的是拦截器中的invoke方法,这个时候
,拦截器中的invoke方法中的method参数会被赋值。

最后,为啥这个方式叫做jdk动态代理呢?
因为这个动态代理对象时用jdk的相关代码生成的,所以这个叫jdk动态代理
后面的cglib动态代理,就是因为要用到cglib的jar包,所以才叫cglib动态代理

下面是使用cglib实现的动态代理模式的Java代码

cglib:Code Generation Library,是一个强大的,高性能,高质量的Code生成类库,也是一个开源项目

 
  1. package com.lxk.designPattern.proxy.dynamicProxy.cglbDynamicProxy;

  2.  
  3. /**

  4. * 被代理的类

  5. * 目标对象类

  6. */

  7. public class TargetObject {

  8.  
  9. /**

  10. * 目标方法(即目标操作)

  11. */

  12. public void business() {

  13. System.out.println("business");

  14. }

  15.  
  16. }

 
  1. package com.lxk.designPattern.proxy.dynamicProxy.cglbDynamicProxy;

  2.  
  3. import net.sf.cglib.proxy.Enhancer;

  4. import net.sf.cglib.proxy.MethodInterceptor;

  5. import net.sf.cglib.proxy.MethodProxy;

  6.  
  7. import java.lang.reflect.Method;

  8.  
  9. /**

  10. * 动态代理-拦截器

  11. * <p>

  12. * Created by lxk on 2016/11/25

  13. */

  14. public class MyInterceptor implements MethodInterceptor {

  15. private Object target;//目标类

  16.  
  17. public MyInterceptor(Object target) {

  18. this.target = target;

  19. }

  20.  
  21. /**

  22. * 返回代理对象

  23. * 具体实现,暂时先不追究。

  24. */

  25. public Object createProxy() {

  26. Enhancer enhancer = new Enhancer();

  27. enhancer.setCallback(this);//回调函数 拦截器

  28. //设置代理对象的父类,可以看到代理对象是目标对象的子类。所以这个接口类就可以省略了。

  29. enhancer.setSuperclass(this.target.getClass());

  30. return enhancer.create();

  31. }

  32.  
  33. /**

  34. * args 目标方法的参数

  35. * method 目标方法

  36. */

  37. @Override

  38. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

  39. if(true){//不要在意这为什么是恒成立的条件语句,为的是说明一个aop的概念:切入点。

  40. System.out.println("aaaaa");//切面方法a();

  41. //。。。

  42. method.invoke(this.target, objects);//调用目标类的目标方法

  43. //。。。

  44. System.out.println("bbbbb");//切面方法f();

  45. }

  46. return null;

  47. }

  48. }

 
  1. package com.lxk.designPattern.proxy.dynamicProxy.cglbDynamicProxy;

  2.  
  3. public class MainTest {

  4. public static void main(String[] args) {

  5. //目标对象

  6. TargetObject target = new TargetObject();

  7. //拦截器

  8. MyInterceptor myInterceptor = new MyInterceptor(target);

  9. //代理对象,调用cglib系统方法自动生成

  10. //注意:代理类是目标类的子类。

  11. TargetObject proxyObj = (TargetObject) myInterceptor.createProxy();

  12. proxyObj.business();

  13. }

  14. }

结果:

 
  1. aaaaa

  2. business

  3. bbbbb

总结:动态代理的目标对象可以是多种对象,目标对象是以参数的方式传入到动态代理拦截器中的

jdk和cglib的2种区别:

首先从文件数上来说,cglib比jdk实现的少了个接口类。因为cglib返回的代理对象是目标对象的子类。而jdk产生的代理对象和目标对象都实现了一个公共接口。

动态代理:

 

 
  1. 动态代理分为两种:

  2. * jdk的动态代理

  3. * 代理对象和目标对象实现了共同的接口

  4. * 拦截器必须实现InvocationHanlder接口

  5.  
  6. * cglib的动态代理

  7. * 代理对象是目标对象的子类

  8. * 拦截器必须实现MethodInterceptor接口

  9. * hibernate中session.load采用的是cglib实现的

转自:

https://blog.csdn.net/qq_27093465/article/details/53351403

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值