Java设计模式之代理模式

代理模式让我评价就是一个笑面虎,结构超简单,实现比较复杂。大家都知道代理模式深入咱们都业务代码中,(就是代理类把接口都实现类引入进来,然后加以封装,再由其他类使用代理类)应用场景太多了,但是大多都是静态代理,如果真都实现一个动态代理我认为还是一个比较都事情,本文对动态代理这块写对也是比较简单

文献阅读提示:并不是他们的demo在你的本地都能跑通,同一个地方综合大家的说法理解成自己的,这样效果更好。

正文

首先介绍一下代理模式的种类,分为静态代理、jdk动态代理、cglib动态代理。

什么是代理模式?

代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。举例说明,就是一个人或者一个机构,代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。

用绘图方式举例

房屋中介这个角色就是代理模式的核心,中介可以做很多事,房屋加价,改造房屋等等,在代码中就是对被代理对象或方法的处理等等。

下面集合代码来讲解,由于本人能力不足,只能给出代理模式的大概思想,没有结合实际开发生产的场景讲解。

静态代理

静态代理是最好理解的,涉及到一个接口、一个接口实现类、还有一个修饰这个接口实现类的代理类,分别对应着租房的动作,房东,中介三者的关系。

由于很简单,三个类的代码都贴到类一起,大家一看便知。

public interface IRenting {
   //租房
   Integer letOut();
}


public class Landlady implements IRenting {
   //目标类,也可以说房东
   public Integer letOut() {
       System.out.println("房东同意租房,租金100元");
       return 100;
   }
}


public class Proxy implements IRenting {
   //代理类,可以看作中介

   public Integer letOut() {
       Landlady landlady = new Landlady();
       Integer integer = landlady.letOut();
       //现实中,代理类要处理很复杂的业务,甚至是调用其他系统操作一些事。
       System.out.println("中介把房东的房子出租给你并从中渔利100元");
       return integer + 100;

   }
}

下面来模拟调用者,也就是租客是怎么找中介租房的。

/*tianqiweiqi.com*/
public class StaticProxyTest {
   public static void main(String[] args) {
       Proxy proxy = new Proxy();
       Integer integer = proxy.letOut();

System.out.println("你一共话了" + integer +

"元成功租房,全程没有见过房东,中介把你们隔离开");

   }
}

调用方只需要把代理类注入到本类中就可以使用了。对于调用者来说,你只知道中介可以租给房子,至于他怎么操作的对你是隔离的。

JDK动态代理

jdk代理模式虽然是动态代理,但是也算比较好理解,只要你的电脑有jdk就可以玩。

其实动态代理和静态代理的思想是不变的,动态代理和静态代理的区别就是,动态代理不用我们去手编写代理类,在运行时,动态的在内存中生产代理类。

JDK动态代理的API:

 在java.lang.reflect包中有一个代理类。

java.lang.reflect.Proxy     

我们分别使用三个包就可以完成JDK动态代理。

         import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;
     import java.lang.reflect.Proxy;

方便大家看到动态代理的输出过程,我讲被代理对象多写一个方法,思想还是一样的,接口、实现类、代理工厂三者。

public interface TargetInterface {
   //被代理的两个方法
   void method1();

   int method2(Integer i);
}public class Target implements TargetInterface {
   public void method1() {
       System.out.println("被代理的method1 running ...相当于做了一些列的业务");
   }

   public int method2(Integer i) {
       System.out.println("被代理的method2 running ...并对参数做了处理");
       return i + 10;
   }
}

             与静态代理唯一有区别的地方就是这里,代理类。

代理类的作用是,无论被代理的类是什么返回值类型,什么参数类型,都可以通过我去调用到,对客户端实现隔离。
实际情况代理类需要做的事情远比我demo要复杂的多。

public class ProxyFactory {
   public staticObject getProxy(final T t) {

       //返回一个代理对象
       Object object = Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
           //invoke()方法是因为new InvocationHandler()而重写的。
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy就是目标对象,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
//比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
               System.out.println("执行方法前...");
//执行了invoke方法就相当于把Target(目标类)的所有方法都交给代理类去调用,这里都t是类,args是参数,可以有很多参数。
               Object invoke = method.invoke(t, args);
               System.out.println("执行方法后...");
               return invoke;
           }
       });

       return object;
   }
}

使用的时候,返回类型是一个接口,然后使用工厂类调用即可。

public class JDKProxyTest {
   public static void main(String[] args) {
       Target target = new Target();
       TargetInterface proxy = (TargetInterface)ProxyFactory.getProxy(target);
       proxy.method1();
       System.out.println("-------------------------");
       int i = proxy.method2(100);
       System.out.println(i);

   }
}

输出结果

注意:JDK的Proxy方式实现的动态代理 目标对象必须有接口 没有接口不能实现jdk版动态代理!

下面来说一下这个方法Proxy.newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);

返回值:Object就是代理对象

参数:

loader:代表与目标对象相同的类加载器——-目标对象.getClass().getClassLoader()

interfaces:代表与目标对象实现的所有的接口字节码对象数组—-数组因为目标类可以有多个接口

h:具体的代理的操作,InvocationHandler接口

Cglib动态代理

第三方代理技术–Cglib代理。

可以对任何类生成代理,代理的原理是可以对目标对象接口实现代理,也可以进行继承代理。

需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core包,由于我依赖了SpringBoot所以这点不用担心。

跟jdk动态代理区别,第一点是可以省略目标接口。 第二点代理工厂使用的是   org.springframework.cglib.proxy.Enhancer;  来帮助我们生成代理对象的,千万不用用net 的包,可能导致错误。

代码实现:

需要依赖springboot

‹dependencies›
    ‹dependency›
        ‹groupId›org.springframework.boot‹/groupId›
        ‹artifactId›spring-boot-starter‹/artifactId›
        ‹version›2.0.0.RELEASE‹/version›
    ‹/dependency›‹/dependencies›

目标类,被代理对象

public class Target {
    public void method1() {
        System.out.println("被代理的method1 running ...相当于做了一些列的业务");
    }

    public int method2(Integer i) {
        System.out.println("被代理的method2 running ...并对参数做了处理");
        return i + 10;
    }
}

代理类

public class CglibFactory implements MethodInterceptor {
   public Object getProxy(Class clazz) {
       Enhancer enhancer = new Enhancer(); //帮我们生成代理对象
       //设置需要创建子类的类
       enhancer.setSuperclass(clazz);
       //设置要代理的目标类,就是当前类,所以this
       enhancer.setCallback(this);
       //通过字节码技术动态创建子类实例
       return enhancer.create();
   }

   public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
       System.out.println("执行方法前dosomething。。。");
       Object invoke = methodProxy.invokeSuper(o, objects);
       System.out.println("执行方法后dosomething。。。");
       return invoke;
   }
}

让我们测试一下吧

public class CglibProxyTest {
   public static void main(String[] args) {
       CglibFactory cglibFactory=new CglibFactory();
       Target proxy= (Target) cglibFactory.getProxy(Target.class);
       proxy.method1();
       System.out.println(proxy.method2(100));

   }
}

输出结果

以上三种代理模式已经讲完,代码结构如下:

总结:

静态代理需要自己手动编写代理类和目标方法。

JDK动态代理就不需要自己手动实现代理类和目标方法,但动态代理的目标类要必须实现接口!

Cglib 代理的目标类可以实现接口也可以不实现,因为可以使用继承子类的方式代理。

作者:

喜欢围棋和编程。

 
发布于 分类 编程标签

发表评论

邮箱地址不会被公开。