代理模式(Proxy pattern)

代理模式:
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式作用:
在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
静态代理模式:
静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。

/**
 * @author Administrator
 * 服务类接口
 * 买房交易
 */
public interface JiaoYi1 {
    void maifang(String houseName,double money);
}
/**
 * @author Administrator
 * 实现服务接口
 * 购房者
 */
@Slf4j
public class FangNu1 implements JiaoYi1{
    @Override
    public void maifang(String houseName, double money) {
        log.info("FangNu1买房。");
    }
}
/**
 * @author Administrator
 * 代理类
 */
@Slf4j
public class Proxy1 implements JiaoYi1{

    private FangNu1 fangNu1;

    public Proxy1(FangNu1 fangNu1){
        this.fangNu1 = fangNu1;
    }

    @Override
    public void maifang(String houseName, double money) {
        log.info("静态代理前置内容 ... ");
        fangNu1.maifang(houseName,money);
        log.info("静态代理后置内容 ... ");
    }
}

测试类以及结果:

//静态代理
    @Test
    public void staticProxy(){
        //代理的真实对象
        FangNu1 fangNu1 = new FangNu1();
        //代理对象
        Proxy1 proxy1 = new Proxy1(fangNu1);
        proxy1.maifang("9-107",15000000);
    }


2019-09-29 13:38:39.091  INFO 3612 --- [main] c.z.l.d.proxypattern.staticproxy.Proxy1  : 静态代理前置内容 ... 
2019-09-29 13:38:39.091  INFO 3612 --- [main] c.z.l.d.p.staticproxy.FangNu1            : FangNu1买房。
2019-09-29 13:38:39.092  INFO 3612 --- [main] c.z.l.d.proxypattern.staticproxy.Proxy1  : 静态代理后置内容 ... 

静态代理总结:
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

动态代理模式:
动态代理是在程序运行时通过反射机制动态创建的。

/**
 * @author Administrator
 * 买房交易
 */
public interface JiaoYi2 {
    void maifang(String houseName,double money);
}
/**
 * @author Administrator
 * 购房者
 */
@Slf4j
public class FangNu2 implements JiaoYi2{
    @Override
    public void maifang(String houseName, double money) {
        log.info("FangNu2买房。");
    }

}
/**
 * @author Administrator
 * 代理类
 */
@Slf4j
public class Proxy2 implements InvocationHandler {
    private Object object;
    public Proxy2(Object object){
        this.object = object;
    }
    /**
     * @param o 真实角色的对象
     * @param method 要调用的真实对象的方法
     * @param objects 要调用的真实对象方法的参数列表
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        // 在代理真实对象前我们可以添加一些自己的操作
        log.info("静态代理前置内容 ... ");
        // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object obj = method.invoke(object,objects);
        // 在代理真实对象后我们也可以添加一些自己的操作
        log.info("静态代理后置内容 ... ");
        return obj;
    }
}

测试类以及结果:

/**
     *动态代理
     * 第一步: 首选生成一个真实角色的对象
     * 第二步: 生成一个代理类的对象(代理类一定是实现了InvocationHandler 接口的),用一个InvocationHandler 引用去指向它, 并且获得handle类的信息。
     * 第三步: 获得真实角色对象所实现的全部接口,并且用数组存储表示
     * 第四步: 调用Proxy类的 newProxyInstance方法,动态的获得一个代理实例化的对象,并且将其强制转化为 接口类型的对象
     * 第五步: 使用动态生成的代理对象调用需要调用的方法(系统内部会自动调用到真实对象的方法)
     */
    @Test
    public void dynamicProxy(){
        FangNu2 fangNu2 = new FangNu2();
        InvocationHandler proxy = new Proxy2(fangNu2);
        Class<?> classType = fangNu2.getClass();
        Class<?>[] classInterface = fangNu2.getClass().getInterfaces();
        JiaoYi2 jiaoYi2 = (JiaoYi2)Proxy.newProxyInstance(classType.getClassLoader(),classInterface,proxy);
        jiaoYi2.maifang("8-108",15000000);
    }

2019-09-29 14:47:42.430  INFO 3192 --- [main] c.z.l.d.p.dynamicproxy.Proxy2            : JDK代理前置内容 ... 
2019-09-29 14:47:42.430  INFO 3192 --- [main] c.z.l.d.p.dynamicproxy.FangNu2           : FangNu2买房。
2019-09-29 14:47:42.430  INFO 3192 --- [main] c.z.l.d.p.dynamicproxy.Proxy2            : JDK代理后置内容 ... 

Cglib代理:

  1. 静态代理和动态代理都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理——这就是 Cglib 代理。
  2. Cglib 代理也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将 Cglib 代理归属于动态代理。
  3. Cglib 是一个非常强大的高性能的代码生成包,它可以在运行期扩展 java 类与 实现 java 接口。它广泛的被许多 AOP 框架使用,例如:Spring AOP,实现方法的拦截。
  4. 在AOP编程中如何选择代理:目标对象需要实现接口,用 JDK 代理。目标对象不需要实现接口,用 Cglib 代理。
  5. Cglib 包在底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。
/**
 * @author Administrator
 * 购房者
 */
@Slf4j
public class FangNu3 {
    public void maifang(String houseName,double money){
      log.info("FangNu3买房");
    }
}
/**
 * @author Administrator
 * cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,
 * 并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
 */
@Slf4j
public class Proxy3 implements MethodInterceptor {
    // 业务类对象,供代理方法中进行真正的业务方法调用
    private Object object;
    public Proxy3(Object o){
        this.object = o;
    }
    // 相当于JDK动态代理中的绑定
    public Object getProxyInstance(){
        // 创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();
        // 为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        enhancer.setSuperclass(this.object.getClass());
        // 设置回调函数
        enhancer.setCallback(this);
        // 创建子类(代理对象)
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        log.info("CGLIB代理前置内容");
        log.info("Method:" + method);
        //调用业务类(父类中)的方法
        Object obj = methodProxy.invokeSuper(o,objects);
        log.info("CGLIB代理后置内容");
        return obj;
    }
}
//CGLIB代理
    @Test
    public void CglibProxy(){
        FangNu3 fangNu3 = (FangNu3)new Proxy3(new FangNu3()).getProxyInstance();
        fangNu3.maifang("2-365",1565856855);
    }


2019-09-30 13:57:57.352  INFO 252 --- [main] c.z.l.d.proxypattern.cglibproxy.Proxy3   : CGLIB代理前置内容
2019-09-30 13:57:57.352  INFO 252 --- [main] c.z.l.d.proxypattern.cglibproxy.Proxy3   : Method:public void com.zhw.learning.designpattern.proxypattern.cglibproxy.FangNu3.maifang(java.lang.String,double)
2019-09-30 13:57:57.356  INFO 252 --- [main] c.z.l.d.proxypattern.cglibproxy.FangNu3  : FangNu3买房
2019-09-30 13:57:57.357  INFO 252 --- [main] c.z.l.d.proxypattern.cglibproxy.Proxy3   : CGLIB代理后置内容

1.静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,
用户通过代理类调用被包装过的业务方法;
2.JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
3.CGlib(Code Generation Library)动态代理(Spring)是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;

发布了31 篇原创文章 · 获赞 5 · 访问量 639

猜你喜欢

转载自blog.csdn.net/qq_37365741/article/details/101693885
今日推荐