代理模式总结

代理模式

代理模式 :客户端 不直接 访问目标对象,而是通过代理访问目标对象

作用:

  1. 客户端不想直接访问实际的对象,或者访问实际的对象存在困难

  2. 原程序代码的前提下,对功能进行扩充

使用场景:

​ 1.可以记录日志 目标对象 时长

​ 2.可以拦截

​ 3.事务控制 权限控制

代理模式:

​ 静态代理 :一个代理只能代理 一个目标对象

​ 动态代理:一个代理 可以代理多种对象( jdk cglib 这两种)

静态代理

静态代理主要包括以下几个主要部分

接口:

//租房
public interface Rent {
    public void rent();
}

真实角色:

//房东要出租房子
public class HouseHolder implements Rent{
    public void rent() {
        System.out.println("房东要出租房子");
    }
}

代理角色:

public class Proxy implements Rent {
    //用组合的方式引入房东,继承也行,但是有局限性,不好
    private HouseHolder houseHolder;

    public Proxy() {
    }

    public Proxy(HouseHolder houseHolder) {
        this.houseHolder = houseHolder;
    }

    public void rent() {
        houseHolder.rent();
        fee();
    }
    //收中介费,中介还能实现其他功能
    public void fee(){
        System.out.println("收中介费");
    }
}

客服端访问代理角色:

public class Client {
    public static void main(String[] args) {
        //房东要租房子
        HouseHolder houseHolder = new HouseHolder();
        //中介代理房东租房子,代理也可由自己的附属操作
        Proxy proxy = new Proxy(houseHolder);

        //你不用面对房东,直接租房即可
        proxy.rent();
    }
}

静态代理的好处

1、可以使真实角色的操作更加纯粹,不用关注一些公共业务

2、公共业务交给代理角色,方便业务的分工

3、公共业务发生扩展的时候,方便集中管理

静态代理的缺点

一个真实角色就会产生一个代理角色,这样代码量会翻倍,开发效率低

这就间接的引出了AOP的实现机制,在原有的基础增加新的功能,但又不能修改原有的代码,于是便使用代理模式,用代理对象去实现。(代理模式就是AOP机制实现的底层)

代理模式总结

动态代理

JDK动态代理:

动态代理包含的几个主要的部分和静态代理是一样的,但是动态代理的代理角色是自己生成的,不是写死的。动态代理的实现主要利用Proxy类和InvocationHandler接口

接口:

//租房
public interface Rent {
    public void rent();
}

真实角色:

//房东要出租房子
public class HouseHolder implements Rent {
    public void rent() {
        System.out.println("房东要出租房子");
    }
}

准备生成代理角色的类:

//用这个类,去实现自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    //被代理的接口
    private Rent rent;
    public void setRent(Rent rent){
        this.rent=rent;
    }

    //生成得到代理类
    public Object getProxy(){
      /**
       *ClassLoader loader,  类加载器  类加载器 将class 加载到内存中,
       *Class<?>[] interfaces, 目标对象 实现的接口
       *InvocationHandler h  当代理对象要 对应的接口方法时 ,会回调(触发invoke        方法) ,然后 调用 目标对象的方法
       */
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(rent, args);
        return result;
    }
}

客服端访问代理角色:

public class Client {
    public static void main(String[] args) {
        //真实角色
        HouseHolder houseHolder = new HouseHolder();
        //代理角色,原本不存在,用刚才创建的类去自动生成
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
        proxyInvocationHandler.setRent(houseHolder);
        //动态生成代理类
        Rent proxy = (Rent) proxyInvocationHandler.getProxy();
        proxy.rent();
    }
}

动态代理的优点

1、可以使真实角色的操作更加纯粹,不用关注一些公共业务

2、公共业务交给代理角色,方便业务的分工

3、公共业务发生扩展的时候,方便集中管理

4、一个动态代理类代理的是一个借口,一般就是对应的一类业务

5、一个动态代理类可以代理多个类,只要是实现同一个接口即可

【总结】动态代理中首先要记着的就是代理角色不是写死的,而是通过实现Proxy类和InvocationHandler接口而自动创建的。全程都是基于反射的思想。

cglib动态代理:

cglib动态代理 原理是 通过asm 生成字节码文件(按照你的目标类,创建一个对应的子类并生成 字节码)子类会继承目标类。

首先需要先导入依赖

<!-- 导入cglib 依赖 -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib-nodep</artifactId>
            <version>2.2</version>
        </dependency>

也是包含几个重要部分:

接口:

//租房
public interface Rent {
    public void rent();
}

真实角色:

//房东要出租房子
public class HouseHolder implements Rent {
    public void rent() {
        System.out.println("房东要出租房子");
    }
}

代理角色:

public class CgLibProxy implements MethodInterceptor {

    /**
     * 创建代理角色
     *  Class superclass 目标对象实现类
     * @return
     */
    public Object getProxyInstance(Class superclass){

        // cglib 工具类
        Enhancer enhancer = new Enhancer();

        // 设置 目标类 为代理类的 父类
        enhancer.setSuperclass(superclass);

        // 设置回调方法
        enhancer.setCallback(this);

        // 创建代理对象
        return  enhancer.create();

    }

    // 在代理对象 执行方法是回调
    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

//        Object proxy 代理对象

//        MethodProxy methodProxy 代理对象执行的方法
//        Object[] objects 代理对象执行方法传递的参数
        System.out.println("代理对象 开始调用 父类 对应方法");
        // 调用目标对象  父类
        Object result =   methodProxy.invokeSuper(proxy, objects);

        System.out.println("代理对象 完成调用 父类 对应方法");

        return result;
    }
}

客户端访问代理角色:

public class Client {

    public static void main(String[] args) {

        // 设置 cglib 产生 字节码文件的位置
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"c://cglib_proxy_classes");
        //真实角色
        HouseHolder houseHolder = new HouseHolder();

        // 代理对象 和目标对象 是同一对象  只不过代理对象 是 目标对象 的 子类
        HouseHolder proxy = (HouseHolder) cgLibProxy.getProxyInstance(HouseHolder.class);

        proxy.buyTicket();

        // cglib 代理目标 类 无论是否实现接口 都可以完成 代理
    }
}

jdk 和 cglib 区别?

jdk: 使用jdk 默认实现 ,只能实现目标对象有接口的对象 执行(通过反射调用目标对象) 效率(反射效率低) 加载速度快(运行时反射执行)

cglib: 使用asm 技术生成目标类对应子类的字节码文件 , 目标类无论是否实现接口都可实现代理 执行(调用父类执行)

效率(效率高和普通继承一致) 加载速度慢(因为在程序执行时,先生成字节码文件,再加载jvm,再生成代理类)

上一篇:代理模式


下一篇:Spring 静态代理与 动态代理 【在理解为学习AOP奠定基础】(七)