Spring 设计模式(代理模式)

Spring 设计模式(代理模式)

博客用于学习记录:方便以后回顾

看他人学习的结果感觉自己都理解了,真正自己开始写内容的时候还是很多的地方理解不到位

代理模式 : 为被代理对象提供一个代理对象,并由代理对象控制被代理对象的引用,已达到增强功能。这里,被代理对象和代理对象这里是has-a 的关系,是一个组合关系。

委派模式 : 类 B和类 A 是两个互相没有任何关系的类,B 具有和 A 一模一样的方法和属性;并且调用 B 中的方法,属性就是调用 A 中同名的方法和属性。B 好像就是一个受 A 授权委托的中介。

和代理模式主要区别是 被委派的类是功能没有被增强只是被选择出来干活的,被代理对象是被功能增强的。

委派模式主要关注的是分发选择,和代理模式关注的是方法增强

装饰器模式 : 对已经存在的类进行装饰,以此来扩展一些功能,新装饰出来的类与原类是是is-a 的关系,继承关系。

学习来源:咕泡学院,

​ 设计模式—代理模式
​ ProxyGenerator生成代理类的字节码文件解析

源码: https://github.com/RunningPig0820/pattern

代理模式的定义:

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。用图表示如下:

Spring 设计模式(代理模式)_第1张图片

为什么要用代理模式?

  • **中介隔离作用:**在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

  • **开闭原则,增加功能:**代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类

有哪几种代理模式?

​ 我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理(InvocationHandler)是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理(Cglib)是在程序运行时通过反射机制动态创建的。

1.静态代理类

我们这里已租房为例,我有一个租房需求,但是没有具体的房源信息,我们就需要房屋中介来帮助我们进行住房,中介帮我们代理了租房需求

一:创建基本接口

/**
 * @ClassName Person
 * @Description 基本类
 * @Author taosha
 * @Date 2018/12/12 14:11
 * @Version 1.0
 **/
public interface Person {
    /**
     * 租房
     */
    public void rentHouse();
}

二:创建被代理类

/**
 * @ClassName Renter
 * @Description 房屋租客
 * @Author taosha
 * @Date 2018/12/12 14:16
 * @Version 1.0
 **/
public class StaticRenter implements Person {
    public void rentHouse() {
        System.out.println("我想找个一室一厅一卫的房子");
    }
}

三:创建代理类

/**
 * @ClassName Intermediary
 * @Description 房屋中介
 * @Author taosha
 * @Date 2018/12/12 14:21
 * @Version 1.0
 **/
public class StaticHouseIntermediary {
    public void rentHouse(Person person) {
        System.out.println("从房源数据库中查找匹配的房子...");
        person.rentHouse();
        System.out.println("如果合适的话,出租");
    }
}

测试

public class RenterTest {
    @Test
    public void test_static_renter_house() throws Exception {
        StaticHouseIntermediary staticHouseIntermediary = new StaticHouseIntermediary();
        staticHouseIntermediary.rentHouse(new StaticRenter());
    } 
}

静态代理总结:

​ 静态代理类十分容易和实现,但是代码还是侵入式的,当接口进行变化的时候,需要我们对代码继续进行修改,代理模式可以用作日志打印,如果按照静态代理方式实现,其实和在原来方法上加入日志没有明显改进。

2.动态代理

动态代理原理:字节码重组

  • 1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取

  • 2、JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口

  • 3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)

  • 4、编译新生成的Java代码.class

  • 5、再重新加载到JVM中运行

2.1 JDK代理

一:创建基本接口

/**
 * @ClassName Person
 * @Description 基本类
 * @Author taosha
 * @Date 2018/12/12 14:11
 * @Version 1.0
 **/
public interface Person {
    /**
     * 租房
     */
    public void rentHouse();
}

二:创建被代理类

/**
 * @ClassName Renter
 * @Description 房屋租客
 * @Author taosha
 * @Date 2018/12/12 14:16
 * @Version 1.0
 **/
public class StaticRenter implements Person {
    public void rentHouse() {
        System.out.println("我想找个一室一厅一卫的房子");
    }
}

三:创建代理类

public class HouseIntermediary implements InvocationHandler {

    private Person person;

    public Object getInstance(Person person) throws Exception{
        this.person = person;
        Class<?> clazz = person.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("从房源数据库中查找匹配的房子...");
        method.invoke(this.person,args);
        System.out.println("如果合适的话,出租");
        return null;
    }
}

测试

public class RenterTest {
	@Test
    public void test_renter_house() throws Exception {
        Person renter = new Renter();
        Person person = (Person)new HouseIntermediary().getInstance(new Renter());
        person.rentHouse();
        person.buyCar();
        //说明代理类和被代理类不是同一个类
        Assert.assertNotSame(renter, person);

        //通过反编译工具可以查看源代码
        byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
        FileOutputStream os = new FileOutputStream("$Proxy0.class");
        os.write(bytes);
        os.close();
    }
 }

我们可以通过 ProxyGenerator类的generateProxyClass()方法 生成代理类的字节码,具体可以察看JDK动态代理4]----ProxyGenerator生成代理类的字节码文件解析

我本地生产的字节码入下:

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m2;
    private static Method m4;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void rentHouse() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void buyCar() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.taosha.pattern.proxy.common.Person").getMethod("rentHouse");
            m3 = Class.forName("com.taosha.pattern.proxy.common.Person").getMethod("buyCar");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

有自动生成的代理类可以看出,代理类自动生成了Person 接口对应的方法,并且调用了 HouseIntermediary的invoke方法已达到代理的作用,HouseIntermediary类的主要作用是为了实现invoke接口。

JDK代理类和静态代理类是在创建代理类的时候有些不同,在Person接口进行变更之后,新增加的方法也会被代理,无需想静态代理一样手动添加所修改的方法。JDK代理的时候是必须需要一个接口作为引用的,有时候我们并不一定有一个接口实现给我进行代理,这个时候我们需要通过Cglib进行代理生成。

2.2 Cglib动态代理

一:创建被代理类

public class CglibRenter  {

    public void rentHouse() {
        System.out.println("我想找个一室一厅一卫的房子");
    }

    public void buyCar() {
        System.out.println("我想找一个车子");
    }
}

二:创建被代理类

public class CglibHouseCarIntermediary implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) throws  Exception{
        Enhancer enhancer = new Enhancer();
        //要把哪个设置为即将生成的新类父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return  enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //业务的增强
        System.out.println("从房源数据库中查找匹配的房子...");

        methodProxy.invokeSuper(o,objects);

        System.out.println("如果合适的话,就准备办事");
        return null;
    }
}

三: 测试

public class RenterTest {
	@Test
    public void test_cglib_renter_house() throws Exception {
        CglibRenter cglibRenter = (CglibRenter)new CglibHouseCarIntermediary().getInstance(CglibRenter.class);
        cglibRenter.rentHouse();

        //通过反编译工具可以查看源代码
        byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{CglibRenter.class});
        FileOutputStream os = new FileOutputStream("$Proxy0.class");
        os.write(bytes);
        os.close();
    }
}

你可能感兴趣的:(Spring)