• 设计模式之代理模式


    在java的项目中经常用到代理模式,它不是什么高深的技术,只是一种解决问题的思路。

    代理模式的由来

           程序来源于生活,前贤的总结总是那么精辟。在生活中处处可以见到代理模式的影子。如明星和经纪人与业务方(广告商,导演),租房客和中介与房东,大姑娘和媒婆与小伙子。发现它们的规律了吗?参与方总是有三方,通过中间商赚差价...呸,通过中间商提供沟通的桥梁,不用亲力亲为,还能享受额外的服务,例如经纪人除了告诉明星关于导演广告商提供的信息,还能提前筛选收集资料,分析可行性。中介除了可以给租客介绍房子带看房,还能给介绍房子的相关朝向,过户流程之类的额外服务。

    其实总结下来就是两点

      1.不用为一些事情投入过多精力,亲力亲为

      2.通过中间人,能获得额外的服务和信息

    转到程序里面就是

     1.有业务需要调用外部接口,内部或者外部可能经常变更的时候,可以采用代理模式,内部业务不需要动,只改代理类就行了。这样代码无侵入性,无耦合

     2.代理能在接口调用前后做额外工作,如记录调用时间,次数,能数据校验,拦截等

    代理模式的实现方式

     1.静态代理

     2.动态代理

        a. cglib方式

        b.jdk方式

    静态代理是比较容易理解的,其结构不外乎,创建一个类,里面持有被用者的引用对象,并提供一个方法,供使用者调用。需要调用的时候,找代理类就行,你不用关心他怎么实现的

    动态代理其实就是静态代理类,只不过是由jvm通过反射(asm)动态生成一个匿名代理类.class文件.接下来可以了解一下它们的使用与要点。

    前置需求

    有一个下班接口,有方法go,需要在下班前下班后做点事情

    interface OffWork{
    void go();
    }

     有一个实现类

    public class Employee implements OffWork {
        public  Employee(){}
        private String name;
    
        public Employee(String name) {
            this.name = name;
        }
    
        @Override
        public void go() {
            System.out.println("员工:"+name+"下班了");
        }
    }
    
    

    静态代理

    优点:结构简单,实现也简单,一目了然

    缺点:项目中有大量代理类,结构一致,代码重复,导致项目包文件过大。新增接口麻烦,接口变动代理类也得跟着变动,就算有工具可以生成代理类,也得替换和检查

     1 public class StaticProxy {
     2     public StaticProxy(OffWork e){
     3         this.work=e;
     4     }
     5    private OffWork work;
     6     public void go(){
     7         System.out.println("下班前准备,好像需要关电脑...");
     8         work.go();
     9         System.out.println("下班路上顺便买个菜");
    10     }
    11 }
    
    

    动态代理-cglib方式

    步骤:

     1.创建代理类并实现MethodInterceptor 接口

    2.持有调用者引用,并提供一个create方法,返回匿名代理类

    3.在调用方法前后做额外工作before,after

     1 public class CglibProxy implements MethodInterceptor {
     2     private Object target;
     3 
     4     public Object create(Object target) {
     5         this.target = target;
     6         Enhancer enhancer = new Enhancer();
     7         enhancer.setSuperclass(target.getClass());
     8         enhancer.setCallback(this);
     9         return enhancer.create();
    10     }
    11 
    12     @Override
    13     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    14         before();
    15         Object res=   method.invoke(target, objects);
    16         after();
    17         return res;
    18     }
    19     private void before(){
    20         System.out.println("执行之前找点事情干干");
    21     }
    22     private void after(){
    23         System.out.println("完成之后收工");
    24     }
    25 }

     注意!划重点

    以上cglib方式只能用于无参构造,。

    下面是有参构造,注意他们之前的区别

     1 public class CglibProxy implements MethodInterceptor {
     2     private Object target;
     3 
     4     public Object create(Object target) {
     5         this.target = target;
     6         Enhancer enhancer = new Enhancer();
     7         enhancer.setSuperclass(target.getClass());
     8         enhancer.setCallback(this);
     9         return enhancer.create();
    10     }
    11     public <T> T getInstance(T target,Class[] args,Object[] argsValue){
    12         Enhancer enhancer = new Enhancer();
    13         enhancer.setSuperclass(target.getClass());
    14         enhancer.setCallback(this);
    15         return (T) enhancer.create(args,argsValue);
    16     }
    17     @Override
    18     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    19         before();
    20         Object res=    methodProxy.invokeSuper(o, objects);
    21         /**被注释的是无参构造用法**/
    22        //   Object res=method.invoke(target, objects);
    23         after();
    24         return res;
    25     }
    26     private void before(){
    27         System.out.println("执行之前找点事情干干");
    28     }
    29     private void after(){
    30         System.out.println("完成之后收工");
    31     }
    32 }

    动态代理-jdk方式

    在java.lang.reflect包下面InvocationHandler接口 

    步骤:

    1.创建代理类并实现InvocationHandler接口

    2.创建成员变量,用来存储调用者的引用

    3.提供create接口,通过方法注入,给成员变量赋值,返回匿名代理类

    4.在invoke方法中调用接口,并添加额外业务

     1 public class DynamicProxy implements InvocationHandler {
     2     private Object target;
     3 
     4 
     5     @Override
     6     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     7         before();
     8         Object res=  method.invoke(target,args);
     9         after();
    10         return res;
    11     }
    12     public Object create(Object t ){
    13         target=t;
    14         return    Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    15     }
    16     private void before(){
    17         System.out.println("执行之前找点事情干干");
    18     }
    19     private void after(){
    20         System.out.println("完成之后收工");
    21     }
    22 }

    调用

     1 public class MainTest {
     2     public static void main(String[] args) {
     3         /**静态代理方式**/
     4         OffWork employee=  new Employee("张一山");
     5         new StaticProxy(employee).go();
     6         System.out.println("==========================");
     7         /**jdk动态代理方式**/
     8         DynamicProxy   dynamicProxy=   new DynamicProxy();
     9         OffWork employee1=(OffWork) dynamicProxy.create(employee);
    10         employee1.go();
    11         System.out.println("==========================");
    12         /**cglib动态代理方式**/
    13         CglibProxy cglibProxy=new CglibProxy();
    14         /**无参构造方式**/
    15         OffWork offWork= (OffWork)cglibProxy.create(employee);
    16         offWork.go();
    17         System.out.println("==========================");
    18         /**有参构造方式**/
    19         OffWork s=  cglibProxy.getInstance(employee,new Class[]{String.class},new Object[]{"张一山"});
    20        s.go();
    21     }
    22 }

     输出结果

    下班前准备,好像需要关电脑...
    员工:张一山下班了
    下班路上顺便买个菜
    ==========================
    执行之前找点事情干干
    员工:张一山下班了
    完成之后收工
    ==========================
    执行之前找点事情干干
    员工:null下班了
    完成之后收工
    ==========================
    执行之前找点事情干干
    员工:张一山下班了
    完成之后收工

     分析

    我们常用的动态代理jdk方式和cglib 有什么不同呢,如何选择?

    这就要聊到它们的实现方式上了。jdk动态代理和cglib动态代理。两种方法的存在,各有各自的优势。

    jdk方式 InvocationHandler

    jdk动态代理是由java内部的反射机制来实现的

    特点:

        jdk反射机制在生成类的过程中比较高效,在执行时效率较低

    优点:无依赖,直接利用jdk反射

    缺点:被代理的类必须实现接口方法,否则无法编译通过,无法使用

    cglib方式 MethodInterceptor

    cglib动态代理底层则是借助asm来实现。

    特点:生成代理类的过程中比较慢,但生成后使用速度快

    优点:

        被代理类无需实现接口,普通类就可以了,没有局限性

     缺点:有外部依赖,不过目前已经被封装到spring-core.jar中

     

    相比较来说,cglib的使用范围更广泛,更通用一点

     总结

        动态代理给我们程序添加了无数的可能,或者可以这样说,动态编译+反射+动态代理 给java程序带来了很多奇思妙想,spring的核心也是在这里,利用它实现了容器的加载,方法的增强,ioc 与aop 都是通过它们实现的。

  • 相关阅读:
    Django用户认证系统(三)组与权限
    Django用户认证系统(二)Web请求中的认证
    Django用户认证系统(一)User对象
    Django QuerySet API文档
    带有Header的SOAP 请求
    环境的配置,就需要理解组件的原理和机制。
    Hotspot JVM的常用选项
    Java程序员必学的Hotspot JVM选项
    三大Java 虚拟机垃圾回收机制的比较(HotSpot, JRockit, IBM JVM)
    [原]使用Fiddler捕获java的网络通信数据
  • 原文地址:https://www.cnblogs.com/jingch/p/10496180.html
Copyright © 2020-2023  润新知