Java后端06(代理模式)

te9uila / 2023-08-07 / 原文

代理模式

​ spring 中就使用了代理模式,Java中的代理模式分为一下两种

  1. 静态代理:最简单,但是每次修改都需要重新编译
  2. 动态代理:分为两种(jdk 动态代理--通过接口实现来进行代理 & cglib 动态代理--通过子类继承来实现代理)

静态代理

​ 静态代理致命问题:代理对象必须提前编译好,所有代码都是写死固定的

举个栗子

// 用一个接口来表示出租的操作
public interface Rent {
    void rentSth();
}

// 定义一个房东类
public class Landlord implements Rent{
    // 房东有自己的房子,可以直接出租
    @Override
    public void rentSth() {
        rentHouse();
    }

    public void rentHouse(){
        System.out.println("landLord -> rentHouse()");
    }
}
// 定义一个中介,来代理房东
public class Intermediary implements Rent{
    // 中介本身是没有房子的,所以需要提供一个成员变量,用来存储其代理对象的内存地址
    // 被代理对象
    private Rent rent;
    // 构造方法传参
    public Intermediary(Rent rent){
        this.rent = rent;
    }
    // 实现房屋租赁
    @Override
    public void rentSth() {
        rent.rentSth();
        earnMoney();
    }

    public void earnMoney(){
        System.out.println("获得差价");
    }
}

测试类

// 调用中介的方法,租房东的房子,实现代理关系
public static void main(String[] args) {
    // 实例化房东
    Rent Landlord = new Landlord();
    // 实例化中介
    Rent intermediary = new Intermediary(Landlord);
    // 中介出租房屋
    intermediary.rentSth();
}

JDK动态代理

核心:被代理对象和代理对象共同实现一个接口,在对象调用方法的时候,使用代理对象偷偷替换掉被代理的对象,但是仍然使用父类引用

动态代理模式下,Rent接口是必须的,房东也是必须的,但是中介类不需要我们自己编写

​ 通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)自动帮我们实现代理

参数列表:

  1. loader:被代理对象的类加载器,如果我们写的时候正常的普通类,使用ClassLoader.getSystemClassLoader()方法获取类加载器
  2. interfaces:该参数表示的是被代理对象的接口的反射对象(class对象),因为可以代理多个类,所以需要使用数组引用进行传值
  3. InvocationHandler:行为处理器,虽然反射生成的代理对象(中介)可以代理被代理对象(房东)的所有行为,但是开发者需要告诉代理对象如何执行方法(动态代理生成的代理对象,会默认重写该对象的所有方法,使所有方法都只具有一个功能,即调用InvocationHandlerinvoke()方法)

invoke(Object proxy, Method method, Object[] args)方法:无论我门调用代理对象的哪一个方法,最终都会调用这个invoke()方法,进行行为处理

参数列表:

  1. proxy:代理对象自己,这个参数尽量不要使用,因为代理对象所有方法都会调用invoke(),最终会导致死递归调用
  2. method:因为无论调用代理对象的哪一个方法,最终都会调用invoke(),所以需要通过Method对象来识别,最终要调用的方法是哪一个
  3. args:参数,表示被代理对象所调用的方法的参数
  4. Object返回值:invoke()方法的返回值就是被代理对象调用方法的返回值
public static void main(String[] args) {
    // 使用动态代理模式来实现demo
    // 利用JDK动态代理,实例化一个代理对象
    Rent rent = (Rent) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Rent.class}, new InvocationHandler() {
        // 实例化被代理对象
        Rent rent = new Landlord();
        public void rested(){
            System.out.println("sb is rested!");
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if(methodName.equals("rentSth")){
                rent.rentSth();
                rested();
            }
            return null;
        }
    });
    rent.rentSth();
}

Mybatis ---- pageHelper 分页插件

<!--添加配置文件-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
// 分页查询设置
PageHelper.offsetPage(1,2);

Mybatis ---- 一级缓存

​ mybatis中,只要通过一级缓存(sqlSession)查询的诗句,就会将数据存放在缓存中,下一次如果再查询相同的数据的时候,就不需要再从数据库获取,而是直接从缓存获取

⭐一级缓存失效的可能(数据不从缓存中获取,直接从数据库中获取):

  1. sqlSession不同
  2. sqlSession相同,但是查询条件不同(从缓存获取数据的前提,是缓存中得有对应的数据)
  3. sqlSession相同,两次查询之间执行了增删改查操作(增删改查操作可能会影响当前数据)
  4. sqlSession相同,开发者认为清除了一级缓存

Mybatis ---- 二级缓存

​ sqlSessionFactory 如果多次查询是基于同一个SqlSessionFactory创建的,就会从二级缓存中获取数据(前提是有数据)而不会从数据库中进行获取,二级缓存和一级缓存不同,并不是默认开启的,需要手动进行配置(配置配置文件 + cache标签)

1. MyBatis 配置文件中配置对应的二级缓存支持
1. 对应的 xml  中添加 `<cache/>`标签
1. ORM映射的实体类需要进行序列化支持`implements Serializable`
<!--支持二级缓存-->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>