Java后端06(代理模式)
代理模式
spring 中就使用了代理模式,Java中的代理模式分为一下两种
- 静态代理:最简单,但是每次修改都需要重新编译
- 动态代理:分为两种(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)
自动帮我们实现代理
参数列表:
- loader:被代理对象的类加载器,如果我们写的时候正常的普通类,使用
ClassLoader.getSystemClassLoader()
方法获取类加载器 - interfaces:该参数表示的是被代理对象的接口的反射对象(class对象),因为可以代理多个类,所以需要使用数组引用进行传值
- InvocationHandler:行为处理器,虽然反射生成的代理对象(中介)可以代理被代理对象(房东)的所有行为,但是开发者需要告诉代理对象如何执行方法(动态代理生成的代理对象,会默认重写该对象的所有方法,使所有方法都只具有一个功能,即调用
InvocationHandler
的invoke()
方法)
⭐invoke(Object proxy, Method method, Object[] args)
方法:无论我门调用代理对象的哪一个方法,最终都会调用这个invoke()方法,进行行为处理
参数列表:
- proxy:代理对象自己,这个参数尽量不要使用,因为代理对象所有方法都会调用
invoke()
,最终会导致死递归调用 - method:因为无论调用代理对象的哪一个方法,最终都会调用
invoke()
,所以需要通过Method对象来识别,最终要调用的方法是哪一个 - args:参数,表示被代理对象所调用的方法的参数
- 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)查询的诗句,就会将数据存放在缓存中,下一次如果再查询相同的数据的时候,就不需要再从数据库获取,而是直接从缓存获取
⭐一级缓存失效的可能(数据不从缓存中获取,直接从数据库中获取):
- sqlSession不同
- sqlSession相同,但是查询条件不同(从缓存获取数据的前提,是缓存中得有对应的数据)
- sqlSession相同,两次查询之间执行了增删改查操作(增删改查操作可能会影响当前数据)
- sqlSession相同,开发者认为清除了一级缓存
Mybatis ---- 二级缓存
sqlSessionFactory 如果多次查询是基于同一个SqlSessionFactory创建的,就会从二级缓存中获取数据(前提是有数据)而不会从数据库中进行获取,二级缓存和一级缓存不同,并不是默认开启的,需要手动进行配置(配置配置文件 + cache标签)
1. MyBatis 配置文件中配置对应的二级缓存支持
1. 对应的 xml 中添加 `<cache/>`标签
1. ORM映射的实体类需要进行序列化支持`implements Serializable`
<!--支持二级缓存-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>