1) 有哪些对象允许被代理?有哪些方法可以被拦截?
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
2) 怎么样创建代理
3) 什么时候创建代理对象?是在 MyBatis 启动的时候创建,还是调用的时 候创建?
4) 被代理后,调用的是什么方法?怎么调用到原被代理对象的方法(比如 Executor 的 query()方法)?
1、编写自己的插件类
-
1)实现 Interceptor 接口 这个是所有的插件必须实现的接口。
-
2)添加@Intercepts({@Signature()}),指定拦截的对象和方法、方法参数 方法名称+参数类型,构成了方法的签名,决定了能够拦截到哪个方法。 问题:拦截签名跟参数的顺序有关系吗?
-
3)实现接口的 3 个方
// 用于覆盖被拦截对象的原有方法(在调用代理对象 Plugin 的 invoke()方法时被调用)
Object intercept(Invocation invocation) throws Throwable;
// target 是被拦截对象,这个方法的作用是给被拦截对象生成一个代理对象,并返回它
Object plugin(Object target);
// 设置参数
void setProperties(Properties properties);
2、插件注册,在 mybatis-config.xml 中注册插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="offsetAsPageNum" value="true"/> ……后面全部省略……
</plugin>
</plugins>
3、插件登记
MyBatis 启 动 时 扫 描 标 签 , 注 册 到 Configuration 对 象 的 InterceptorChain 中。property 里面的参数,会调用 setProperties()方法处理
问题 1:四大对象什么时候被代理,也就是:代理对象是什么时候创建的?
Executor 是 openSession() 的 时 候 创 建 的 ; StatementHandler 是 SimpleExecutor.doQuery()创建的;里面包含了处理参数的 ParameterHandler 和处理 结果集的 ResultSetHandler 的创建,创建之后即调用 InterceptorChain.pluginAll(), 返回层层代理后的对象。
问题 2:多个插件的情况下,代理能不能被代理?代理顺序和调用顺序的关系?
问题 3:谁来创建代理对象?
Plugin 类 。 在 我 们 重 写 的 plugin() 方 法 里 面 可 以 直 接 调 用 return Plugin.wrap(target, this);返回代理对象。
问题 4:被代理后,调用的是什么方法?怎么调用到原被代理对象的方法?
因为代理类是 Plugin,所以最后调用的是 Plugin 的 invoke()方法。它先调用了定义 的拦截器的 intercept()方法。可以通过 invocation.proceed()调用到被代理对象被拦截 的方法
1.管理对象
2.通过一个Template封装方法
1、 SqlSessionFactory 是什么时候创建的?
<!-- 在Spring启动时创建 sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.gupaoedu.crud.dao"/>
</bean>
2、 SqlSession 去哪里了?为什么不用它来 getMapper?
3、 为什么@Autowired 注入一个接口,在使用的时候却变成了代理对象?在 IOC 的容器里面我们注入的是什么? 注入的时候发生了什么事
1.整合jar包
2.
3.
4.注入使用
Spring 对 MyBatis 的对象进行了管理,但是并不会替换 MyBatis 的核心对象。也就 意味着:MyBatis jar 包中的 SqlSessionFactory、SqlSession、MapperProxy 这些都 会用到。而 mybatis-spring.jar 里面的类只是做了一些包装或者桥梁的工作。
它是线程不安;
我们现在已经有一个 DefaultSqlSessionFactory,按照编程式的开发过程,我们接 下来就会创建一个 SqlSession 的实现类,但是在 Spring 里面,我们不是直接使用 DefaultSqlSession 的,而是对它进行了一个封装,这个 SqlSession 的实现类就是 SqlSessionTemplate。这个跟 Spring 封装其他的组件是一样的,比如 JdbcTemplate, RedisTemplate 等等,也是 Spring 跟 MyBatis 整合的最关键的一个
所有的方法都会先走到内部代理类 SqlSessionInterceptor 的 invoke()方法。
SqlSessionTemplate 里面有 DefaultSqlSession 的所有的方法:selectOne()、 selectList()、insert()、update()、delete(),不过它都是通过一个代理对象实现的。这 个代理对象在构造方法里面通过一个代理类创建
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
public abstract class SqlSessionDaoSupport extends DaoSupport {
private SqlSessionTemplate sqlSessionTemplate;
/**
* Set MyBatis SqlSessionFactory to be used by this DAO.
* Will automatically create SqlSessionTemplate for the given SqlSessionFactory.
*
* @param sqlSessionFactory a factory of SqlSession
*/
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
}
}
}
也就是说我们让 DAO 层的实现类继承 SqlSessionDaoSupport,就可以获得 SqlSessionTemplate,然后在里面封装 SqlSessionTemplate 的方法。
当 然 , 为 了 减 少 重 复 的 代 码 , 我 们 通 常 不 会 让 我 们 的 实 现 类 直 接 去 继 承 SqlSessionDaoSupport,而是先创建一个 BaseDao 继承 SqlSessionDaoSupport。在 BaseDao 里面封装对数据库的操作,包括 selectOne()、selectList()、insert()、delete() 这些方法,子类就可以直接调用。
public class BaseDao extends SqlSessionDaoSupport {
//使用sqlSessionFactory
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Autowired
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
super.setSqlSessionFactory(sqlSessionFactory);
}
}
通过接口扫描
我们只需要重写 postProcessBeanDefinitionRegistry()方法,在这里面操作 Bean 就可以了。
processBeanDefinitions 方法里面,在注册 beanDefinitions 的时候,BeanClass 被改为 MapperFactoryBean(注意灰色的注释)。
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + beanClassName + "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
mapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());
Spring 会根据 Mapper 的名字从 BeanFactory 中获取它的 BeanDefination,再从 BeanDefination 中 获 取 BeanClass , EmployeeMapper 对 应 的 BeanClass 是 MapperFactoryBean(上一步已经分析过)。
第一步:接下来就是创建 MapperFactoryBean,因为实现了 FactoryBean 接口,同样是调 用 getObject()方法。
因 为 MapperFactoryBean 继 承 了 SqlSessionDaoSupport , 所 以 这 个 getSqlSession()就是调用父类的方法,返回 SqlSessionTemplate;
第二步,SqlSessionTemplate 的 getMapper()方法,里面又有两个方法
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
// SqlSessionDaoSupport.java
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
// SqlSessionTemplate.java
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
// SqlSessionTemplate.java
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
// Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
我们注入到 Service 层的接口,实际上还是一个 MapperProxy 代理对象。 所以最后调用 Mapper 接口的方法,也是执行 MapperProxy 的 invoke()方法,后面的 流程就跟编程式的工程里面一模一样了。