什么是代理模式
代理模式(Proxy Pattern) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英 文叫做Proxy或Surrogate,它是一种对象结构型模式。
代理模式包含如下角色:
Subject: 抽象主题角色
Proxy: 代理主题角色
RealSubject: 真实主题角色
MyBatis中实现的代理模式
代理模式可以认为是Mybatis的核心使用的模式,正是由于这个模式,我们只需要编写Mapper.java接口,不需要实现,由Mybatis后台帮我们完成具体SQL的执行。
这里有两个步骤,第一个是提前创建一个Proxy,第二个是使用的时候会自动请求Proxy,然后由Proxy来执行具体事务;
当我们使用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法,而该方法又会调用mapperProxyFactory.newInstance(sqlSession)来生成一个具体的代理:
public class MapperProxyFactory {
private final Class mapperInterface;
private final Map methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class getMapperInterface() {
return mapperInterface;
}
public Map getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },
mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
在这里,先通过T newInstance(SqlSession sqlSession)方法会得到一个MapperProxy对象,然后调用T newInstance(MapperProxy
而查看MapperProxy的代码,可以看到如下内容:
public class MapperProxy implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
非常典型的,该MapperProxy类实现了InvocationHandler接口,并且实现了该接口的invoke方法。
通过这种方式,我们只需要编写Mapper.java接口类,当真正执行一个Mapper接口的时候,就会转发给MapperProxy.invoke方法,而该方法则会调用后续的sqlSession.cud>executor.execute>prepareStatement等一系列方法,完成SQL的执行和返回。
JDK实现的动态代理
java 运行环境JDK,jdk中也给我们提供了动态代理,jdk中的动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler它是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
Proxy利用InvocationHandler动态创建一个符合某一接口的实例,用来生成目标类的代理对象。
对于异常处理,日志功能,权限的检查,事务等都是贯穿到各个模块之中,因此进行AOP。代理技术有面向接口和生成子类,面向接口的实现如下:
public Object getProxy(final Object targetObj) {
Obejct obj = Proxy.newProxyInstance(
targetObj.getClass().getClassLoader(), //被代理类的类加载器
targetObj.getClass().getInterfaces(), //被代理类接口的字节码
new InvocationHandler() { //类似于一个回调函数 代理功能就在里面实现
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
try{
beforeAdvice(); //前置通知
method.invoke(targetObj, args); //拦截的点
afterAdvice(); //后置通知
}catch (Exception e) {
exceptionAdvice(); //异常通知
}finally{
finalAdvice(); //必须执行通知
}
return obj;
}
});
}
Spring中的动态代理
Spring 的 AOP 代理是完全由 Spring 的 IoC 容器负责生成、管理的,其依赖关系也由 IoC 容器负责管理。因此,AOP 代理可以直接使用容器中的其他 Bean 实例作为目标,这种关系可由 IoC 容器的依赖注入提供。
Spring是默认采取的动态代理机制实现AOP的,当然,在当动态代理不可用时候(代理类无接口)它就会使用CGlib的机制了。
Spring提供了两种方式来生成代理对象: 一个是JDK的Proxy,另一个就是Cglib了,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。Spring会自动选择使用那种方式,我们不用担心,此处做个了解。
CGLib实现的动态代理
CGLib动态代理的全称为 Code Generation Library,它是一个强大的高性能的,高质量的代码生成类库,就可以在运行期扩展Java类与实现Java接口,CGLib它封装了asm,可以再运行期动态生成新的class。CGLIB和JDK动态代理相比较的话:JDK创建的代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则就可以通过CGLib来创建动态代理了。
小结:JDK 对接口创建代理实例,CGLIB 可以对类创建代理实例。
参考资料:https://mp.weixin.qq.com/s/-nMUwRPp_XYg-YZF9P0bTg