SSM整合细节教程(spring怎么就融合了mybatis?)(三)
什么是ssm?
官方描述:
Spring
Spring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象。也可以称之为项目中的粘合剂。
Spring的核心思想是IoC(控制反转),即不再需要程序员去显式地new一个对象,而是让Spring框架帮你来完成这一切。
SpringMVC
SpringMVC在项目中拦截用户请求,它的核心Servlet即DispatcherServlet承担中介或是前台这样的职责,将用户请求通过HandlerMapping去匹配Controller,Controller就是具体对应请求所执行的操作。SpringMVC相当于SSH框架中struts。
mybatis
mybatis是对jdbc的封装,它让数据库底层操作变的透明。mybatis的操作都是围绕一个sqlSessionFactory实例展开的。mybatis通过配置文件关联到各实体类的Mapper文件,Mapper文件中配置了每个类对数据库所需进行的sql语句映射。在每次与数据库交互时,通过sqlSessionFactory拿到一个sqlSession,再执行sql命令。
页面发送请求给控制器,控制器调用业务层处理逻辑,逻辑层向持久层发送请求,持久层与数据库交互,后将结果返回给业务层,业务层将处理逻辑发送给控制器,控制器再调用视图展现数据。
问题一,IOC到底是什么,为什么不需要显示地new对象了?new个对象又是什么鬼? spring框架是怎么完成这一切的?
问题二,springMvc怎么就实现了对用户的拦截?request—》response 数据走向是如何的?
先来整合ssm,先实现这个功能再说。我仍然用mavenSsm这个项目来试验。
配置web.xml:
第一部分,监听java程序启动,随即加载配置文件,spring.xml
第二部分,启动前端控制器,拦截目标请求,加载spring-mvc.xml , ----给对应的controller处理。
spring和springmvc都出现了,那mybatis在哪?
先提一下mybatis的主要作用----实现程序与数据库交互。
在上一个博文中,实现对dept表的查询,是加载了一次mybatis.xml,打开会话,才对数据访问。
那么,在一个web项目中,难道需要用点一下请求就开一下数据库吗? 数据库会话在什么时候打开最好呢?
数据库会话能够打开多长时间呢?
一,没有任何一个服务器敢这样去 消耗资源的,open-close-open-close…, 这浪费的可时间和钱啊,而且极度卡顿。
二,肯定是在服务器开启时,数据库会话就已经建立了。
二,数据库会话open默认时间为8小时。
spring容器在项目启动后,只加载一次。把mybatis融合到spring中算是最佳选择了。
来看,web.xml 加载spring.xml, spring 加载mybatis.xml
从这个角度理解,还是比较容易的。毕竟想要明白spring支持orm框架这个东西,还是需要读源码。
下面我会根据用spring+mybatis,和只用mybatis作比较,看看spring怎么就对mybatis作了封装,
导入jar包,mybatis-spring.
这个jar包一眼就能看出来,它肯定是为spring与mybatis融合作了桥梁。
好,先看不用spring时的结果。
新建mybatis.xml
建立一个打开会话的工具类。
可以看出:
第一,我需要读取mybatis.xml
第二,我需要利用xml信息建立SqlsessionFactory
第三,打开会话,操作数据库。
好,来看用了spring后。
可以发现,数据源交给了spring容器。
先提一下mybatis的作用-----》使程序与数据库进行交互
现在我的spring容器完全接管了配置数据源的功能。
那mybatis.xml还有用吗?
SqlSession又该怎么建立?
来看mybaits-spring.jar 提供的一个Bean;
spring容器分别给它传了两个属性。
dataSource 和 mapperLocations;
先看看dataSource的去向。
点开这个Bean,进去看看。
会看到一系列极其熟悉的参数。
dataSouorce,SqlSessionFactory,configuration,mapperLocations
来看这个方法。
我把上面的英文用词典翻译一下:
设置此实例应该为其管理事务的JDBC {@code DataSource}。{@code数据源}
*应该匹配{@code SqlSessionFactory}所使用的:例如,您可以指定相同的
* JNDI数据源。
*
*此{@code DataSource}的事务性JDBC {@code Connection}将提供给应用程序代码
*通过{@code DataSourceUtils}或{@code DataSourceTransactionManager}直接访问{@code DataSource}。
*
*这里指定的{@code DataSource}应该是管理事务的目标{@code DataSource},而不是
{@code TransactionAwareDataSourceProxy}。只有数据访问代码可以使用
* {@code TransactionAwareDataSourceProxy},而事务管理器需要在
*底层目标{@code DataSource}。如果仍然有{@code TransactionAwareDataSourceProxy}
*传入后,将打开包装以提取其目标{@code DataSource}。
这个dataSource最终是为jdbc设置的。到了这里,该明白,这个Bean会把数据源交给谁了吧?
如果还不明白,点击getTargetDataSource()这个方法,进去看看。如下图
getConnection 是不是很熟悉?
既然连接数据库这个问题被spring解决了, 那么我的mybatis的dao映射文件呢?该放到哪里去?
在未使用spring前,映射文件都是连接在mybatis.xml主文件中的。
好,搜索一下,mapperLocation这个属性,看看我 传的映射文件到底去了哪里。
下面是一段主函数。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
if (this.cache != null) {
configuration.addCache(this.cache);
}
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
真特么长。 (但是看到底层源码用的也是if/else我就放心了)
不慌! 看这个函数的返回值!
return this.sqlSessionFactoryBuilder.build(configuration);
是不是很熟悉?
在未使用spring之前,sqlSessionFactoryBuilder.build() 这个方法是用来根据mybatis.xml文件建立Sqlsession的。
configuration难道就是mybaits.xml?
可是我没有建立它啊!
我取了一段其中的代码,看下图。
可以看到,SqlSessionFactoryBean确实是给我们建立了一个xml!!!
再看,mapperLocations,是不是被遍历分解了! 它就是映射文件啊!
我在官方搜罗到了这么一句话:
SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。
这个封装我也是醉了。
好,既然有了该有的东西,那么谁又来创建Sqlsession会话,对数据库进行操作呢?
在未用spring之前,只需要获取到SqlSessionFactory对象,然后.openSession() 就Ok了。
但是,现在没有SqlSessionFactoy对象啊!
看下图:
第一步spring容器帮我们加载完成了。
看第二步,点击MapperScannerCongiruer进去看看,看看basePackage去哪了。
它的主函数,如下图
再点击去这个类,搜索一下scan看看。
它的父类我就不说了,大概就是对包进行扫描,然后进行注册并且返回相应bean。
重点看方法上面的那一段话,我用词典翻译一下:
调用将搜索并注册所有候选人的父搜索。然后对注册的对象进行后期处理,将它们设置为
MapperFactoryBeans
它们会被设置为,MapperFactoryBeans. 好,来看看这是个什么东西。
dao接口的去处不就是在这!
再往下翻,会发现一段代码:
…很熟悉。
.Ok,getObject()方法,就能得到SqlSession对象!
至此,spring+mybatis 其实就算融合完毕了。mybatis拥有的功能,spring容器真是将它吃的一干二净。
好,现在配置spring-mvc.xml;
好,至此。 业务层Spring,表现层springMvc,持久层Mybatis建立完毕。
运行一下:
数据流程:
上一篇: 收件人列表(C++)
下一篇: Helm部署和体验jenkins