1、设计模式的分类
-
创建型:工厂方法模式、抽象方法模式、单例模式、建造者模式、原型模式
-
结构型:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
-
行为型:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
2、代理模式概述
1. 概念
-
代理模式【proxy】是对象的结构模式,代理模式给某一个对象提供一个代理对象,并由代理对象控制对象的引用。
-
代理就是⼀个⼈或者⼀个机构代表另⼀个⼈或者另⼀个机构采取⾏动。在⼀些情况 下,⼀个客户不想或者不能够直接引⽤⼀个对象,⽽代理对象可以在客户端和⽬标对象之间 起到中介作⽤。
-
代理模式的主要⽬标是: 保护(隔离)和增强⽬标对象
2. 代理模式涉及的角色
-
抽象角色
-
声明真实对象和代理对象共同的接⼝
-
通过接口或抽象类声明真实角色实现的业务方法
-
-
代理角色
-
代理对象⻆⾊内部含有对真实对象的引⽤,从⽽可以操作真实对象,同时代理对象提供与真 实对象相同的接⼝以便在任何时刻都能代替真实对象。同时,代理对象可以在执⾏真实对象操作 时,附加其他的操作,相当于对真实对象进⾏封装
-
-
真实角色
-
代理⻆⾊所代表的真实对象,是我们最终要引⽤的对象
-
实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用
-
3.代理模式种类
-
动态代理:
-
在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象,代理类编译之前不存在,运⾏期动态⽣成
-
-
静态代理:
-
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了
-
3、动态代理
1. 动态代理种类
-
JDK的动态代理
-
CGLib动态代理
2. JDK的动态代理
JDK动态代理过程实际上代理的是接口,在创建代理对象时,调用的是java.lang.reflect包中Proxy类的newProxyInstance方法,该方法需要三个参数:
-
类加载器:用来加载动态代理生成的二进制字节码数组到JVM中
-
接口:JDK的动态代理要求 必须 代理某个接口
-
InvocationHandler:处理器:代理对象中 每一个方法执行,都是甩给invoke方法,我们在Invoke方法中实现 自定义的非核心的代码
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
……
}
案例实现:
//抽象角色
public interface Renter {
void rent();
}
//房东:真实角色
public class Landlord implements Renter{
private String name;
public Landlord(String name){
this.name = name;
}
@Override
public void rent() {
System.out.println("房东:" +name+"说:我有一套房子出租!");
}
}
//中介:代理角色
public class HouseAgent implements Renter{
private Landlord landlord = new Landlord("宋叔");
@Override
public void rent() {
System.out.println("1.租房子之前:带看房子......");
landlord.rent();
System.out.println("2.租房子后:收租金......");
}
}
测试类:
public class RenterProxyFactory {
public static void main(String[] args) {
//创建Renter接口的代理
Renter renter = new RenterProxyFactory().getRenter();
//执行方法
renter.rent();
}
public Renter getRenter(){
return (Renter) Proxy.newProxyInstance(
Renter.class.getClassLoader(), //加载字节码文件
new Class[]{Renter.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method 执行:::::"+method);
return null;
}
});
}
}
执行结果:
执行接口中的方法时,都会被InvocationHandler处理器处理,并交给invoke方法执行,在invoke方法中可以实现需要增强的业务
3. 实现原理
-
底层通过ProxyGenerator.generatProxyClass先生成class字节码数组
-
使用指定的、传入的类加载把Class的自己码数组加载JVM,生成Class对象
-
使用Class对象调用构造方法(InvocationHandler) ,生成 代理类的对象
4. 动态代理应用
-
getMapper使用了JDK的动态代理
使用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法
而该方法又会调用mapperProxyFactory.newInstance(sqlSession)
mapperProxyFactory.newInstance(sqlSession)会调用本类中的newInstance(mapperProxy)生成具体的代理对象
该MapperProxy类实现了InvocationHandler接口,并且实现了该接口的invoke方法,完成后续的一系列sql操作,并返回映射后的Java类
-
PooledConnection获取代理连接也使用的JDK的动态代理
PoolState主要就是有这两个List,一个保存空闲连接,一个保存活跃连接,PooledDataSource持有一个PoolState字段
在执行getConnection()时会执行popConnection获得PooledConnection对象过程中会在该类中给proxyConnection属性使用动态代理赋值 然后getProxyConnection时获取到的为代理对象
如果存在空闲连接,则直接从列表上取下来一个,如果不存在空闲连接,但是当前活跃连接数小于配置的最大连接数,那么新建一个连接
在构造方法中使用动态代理给属性赋值
-
MyBatis中的Interceptor拦截器所有动态代理
XMLConfigBuilder解析MyBatis全局配置文件的pluginElement私有方法调用addInterceptor方法
当构造 四大对象 new Executor/new StatementHandler/newResutlSetHandler 等四大对象时触发 interceptorChain.pluginAll
遍历每一个Interceptor调用 plugin方法
默人每个方法都会Plugin.warp方法
动态代理生成代理对象
Executor.update/query\StatementHandler\ResultSetHandler\ParrameterHandler中对应的方法执行时,触发 对应Interceptor中的invoke
5. CGlib动态代理
-
引入CGLib依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
-
创建目标类
public class Student {
public void show(){
System.out.println("show....");
}
}
-
测试
//构造Enhancer对象
Enhancer enhancer = new Enhancer();
//setSuperClasss设置父类 需要代理的类
enhancer.setSuperclass(Student.class);
//setCallback 设置 回调用方法
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("执行目标方法之前");
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("执行目标方法之后");
return result;
}
});
//create 创建代理对象
Student student = (Student) enhancer.create();
student.show();
执行结果: