原生的方法调用直接通过SqlSession方法调用:提供了selectOne,selectList,insert,delete…方法。
//原有方法调用形式
/**
* 查询操作:
* 返回多个结果时,使用selectList
* 返回的结果不管是单个还是多个,在resultType属性都是返回的Java对象全路径
*
* 返回单个结果对象使用selectOne
*/
sqlSession.selectList("com.tulun.maventest.dao.StudentMapper1.getStudentByName", "%王%");
代理模式是Java中的一种设计模式:
公司里开发的一款软件产品,产品的开发是由开发人员开发,由销售人员去卖产品,产品由销售卖给顾客。
代理模式中代理类和委托类具有相同的接口。
代理类的主要职责就是为委托类预处理消息,过滤消息等。
代理类的对象本身并不是真正的实现服务,而是通过委托类的对象的相关方法,来提供特定的一些服务。
代理类和委托类之间存在关联关系,一个代理类的对象和一个委托类的对象相关联。
访问实际对象,是通过代理对象类方法的。
代理模式分为静态代理和动态代理:
静态代理是在程序编译阶段确定代理对象。
动态代理是在程序运行阶段确定代理对象。
动态代理是在运行时根据Java代码指示动态生成的,相比静态代理,优势在于方便的对代理类的函数进行统一的处理,而不用修改每个代理类的方法。
Java中提供的动态代理方式有两种:JDK自带的动态代理和CGLib实现的代理。
JDK自带的代理方式需要实现invocationHandler接口,实现invoke的方法。
先提供一个共有的接口和委托类实现
接口:
/**
* 接口类
* 定义委托类和代理类公共的方法
*/
public interface IUser {
void talk();
}
委托类:
/**
* 委托类
* 实现了接口IUser中的talk方法
*/
public class User implements IUser{
@Override
public void talk() {
System.out.println("doing User.talk");
}
}
实现动态代理,首先创建一个实现了invocationHandler接口的辅助类:
/**
* 代理的辅助类
*/
public class UserProxy implements InvocationHandler {
private Object object;
public UserProxy(Object o) {
this.object = o;
}
/**
* 实现动态代理,就需要实现InvocationHandler接口中的invoke方法,该方法有三个参数
* @param proxy:动态代理生成的代理对象
* @param method:调用的方法
* @param args:表示该方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理类提供委托类提供的功能,也可以提供自己特有的功能
System.out.println("doing UserProxy.invoke");
//调用委托类提供的方法
method.invoke(object,args);
System.out.println("doing UserProxy.invoke end");
return null;
}
使用产生代理对象时,需要调用代理辅助类,调用委托方法
public static void main(String[] args) {
//产生代理对象
IUser iUser = (IUser) Proxy.newProxyInstance(UserProxy.class.getClassLoader(), new Class[]{IUser.class}, new UserProxy(new User()));
iUser.talk();
}
日志打印:
通过日志的观察,当前的代理类调用talk方法,调用到代理辅助类UserProxy中的invoke方法,还调用到了委托类的talk实现。
JVM是如何自动实现invoke方法的调用呢?
在代理类main方法调用中添加一个监控操作
//看看JDK生成的自动代理类
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
通过执行查看,JVM生成了一个$Proxy0.class字节码文件,源码如下:
public final class $Proxy0 extends Proxy implements IUser {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void talk() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.tulun.maventest.proxy.IUser").getMethod("talk");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
$Proxy0的定义中可知,确实实现了IUser接口。和代理模式下的代理是完全一致的
通过分析:当代理类实例中调用方法talk时,根据Java中的多态,调用了$Proxy0的talk方法,$Proxy0重写talk方法。
代理提供了一种可扩展机制来控制被代理对象的访问,就是对象的访问做了一层封装。
当代理没有接口的类,此时Proxy和InvocationHandler机制不能使用了(JDK自带的代理模式的使用必须需要有接口的),此时可以使用CGLib库,采用底层字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截父类所有方法的调用,采用横切的逻辑。Spring AOP(横向切面)的技术就是采用动态代理。
CGLib的使用需要引入第三方库:
<!--CGLib-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.2</version>
</dependency>
父类:
public class CGLibSuper {
public void doing(){
System.out.println("CGLibSuper.doing");
}
}
辅助类:
/**
* 通过已有的类产生代理类时
* 在当前辅助类需要实现MethodInterceptor接口
*/
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public <T> T getProxy(Class<T> clazz){
//设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码动态的创建子类实例
return (T)enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLibProxy doing");
//拦截到父类响应的方法
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("CGLibProxy doing end");
return o1;
}
}
通过CGLib实现代理:
public static void main(String[] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
//动态产生的CGLibSuper的子类实例
CGLibSuper proxy = cgLibProxy.getProxy(CGLibSuper.class);
proxy.doing();
}
执行结果:
通过执行结果可知,当前产生的代理调用相应的方法(doing),会被代理辅助类中的intercept方法拦截,在该方法中可以实现扩展,也能调用父类中的方法。
Mybatis中产生的代理是哪种方式?
JDK自带的方式:仅在其中实现了接口
mapper是如何添加进去的?
通过代码形式产生的会话工厂实例和读取配置形式是类似的,
通过代码形式中可以看到,Mapper接口是通过Configuration.addMapper()形式来添加,参数为接口的class。
类似于:
Configuration configuration = new Configuration();
configuration.addMapper(StudentMapper.class); //添加mapper
addMapper方法的实现:
public <T> void addMapper(Class<T> type) {
this.mapperRegistry.addMapper(type);
}
mapper实际上被添加到了MapperRegistry类中,继续:
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {//只添加接口
if (this.hasMapper(type)) {//不允许接口重复添加
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
this.knownMappers.put(type, new MapperProxyFactory(type));//将接口存放到HashMap中,重点
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}
执行的configuration.addMapper()操作,最终是被放入到HashMap中,其名为knownMappers,knownMappers是MapperRegistry中的属性,它是一个HashMap对象,key为当前Class对象,value为MapperProxyFactory实例。
从getMapper入手分析:
sqlSession中调用getMapper()方法操作,实际调用到DefaultSqlSession中的实现。
DefaultSqlSession类:
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
其中DefaultSqlSession没有逻辑,直接调用configuration中的getMapper方法,configuration是将配置信息存放住。
configuration类中的操作:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
其中getMapper的实现是调用mapperRegistry中的方法,mapperRegistry是存放了一个HashMap的。
mapperRegistry类:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);//重点看这里,通过这个操作返回了代理对象
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
调用sqlSession.getMapper操作,最终会到上面的这个方法,它会根据接口在HashMap中找到对应的value值,是一个mapperProxyFactory的对象,然后通过调用该对象的newInstance方法,获取到代理对象。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
//JDK自带的代理方式生成的映射代理对象
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
public class MapperProxy<T> implements InvocationHandler, Serializable {
//当前类实现了InvocationHandler接口
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
//实现invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);//其实实现是调用委托类方法
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
} else {
MapperMethod mapperMethod = this.cachedMapperMethod(method); //使用缓存
//执行增删改查相关操作
return mapperMethod.execute(this.sqlSession, args);
}
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
this.methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
Mapper是如何注册的,Mapper接口是如何产生动态代理对象的?
Mapper接口在初始sqlSessionFactory的时候进行注册
Mapper接口注册到了mapperRegistry类的HashMap中,key是Mapper的class,value是当前的Mapper工厂
Mapper注册之后,可以从sqlSession中get获取对象
sqlSession.getMapper使用了JDK自带的动态代理,产生目标接口Mapper的代理对象
动态代理的代理辅助类MapperProxy