现在最流行的应该就是 SSM 了,SSH 一去不复返,SSM 指的是:SpringMVC + Spring + MyBatis
弃用 Struts2 的原因在于漏洞比较多吧(毕竟人用的也多),危害还比较大,官方实力甩锅
至于弃用 Hibernate 倒不是它不好,因为太大了,对于小项目用不着这么重量级的东西,MyBatis 这种轻量级的框架是很适合的,并且性能也好
不得不说,相比 SSH 来说,SSM 整合确实更加简单,错觉?
Spring和MyBatis整合
万年不变定律,先导包,除了 Spring 和 MyBatis 的基本包,别忘了还有一个 MyBatis-spring 的“插件包”,应该也是需要连接池的吧,除了 C3P0 还可以选择 bonecp,据说它的性能比 C3P0 还高,如果用它就别忘了导入 bonecp-spring 的依赖,我的栗子用的 Maven 构建的,pom 文件一览:
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
这样大部分依赖就处理完了,接下来就是配置 Spring、MyBatis 的环境,就是写写配置文件,这里我拆了一下,分成 jdbc.properties 、log4j.properties 、applicationContext.xml 、applicationContext-mybatis.xml 等几个文件:
日志:
1 | log4j.rootLogger=DEBUG,A1 |
applicationContext:
1 | "1.0" encoding="UTF-8" xml version= |
applicationContext-mybatis (数据源的定义我想抽在这里也许也比较好):
1 | "1.0" encoding="UTF-8" xml version= |
基本实现了自动化,根据定义的接口自动实例化 Mapper ,通过包扫描的方式来自动扫描定义的接口,这样不需要使用 getMapper 方法了,也不需要自己在 beans 中一个个的定义 Mapper 了。
其中引用了 MyBatis 的配置文件,这个文件的大部分内容都可以在 spring 中配置了,少数几个还不行,比如 Settings、插件等还是需要配置在 MyBatis 的配置文件中才行:
1 | "1.0" encoding="UTF-8" xml version= |
这样配置文件就算是写完了,接下来就是写几个 mapper 映射文件和 mapper 对应的接口去测试一下了,详情参考 Github
与SpringMVC整合
这个就更简单了,SpringMVC 本来就是 spring 的子项目,可以说是无缝整合,不需要其他任何的插件包,直接导入 spring-web 包就可以用了,记得写写配置文件就行:
firstssm-servlet.xml :
1 | "1.0" encoding="UTF-8" xml version= |
然后把关键的 web.xml 文件搞一搞就完事了!
1 | "1.0" encoding="UTF-8" xml version= |
因为配置的是拦截所有的请求,所以记得在 springmvc 的配置文件中放行静态资源,要不然 css、js 都是会 404 的
测试
然后就可以写个简单的栗子看看行不行了,来看定义的 controller :
1 | "user") ( |
有了自动包扫描就是方便,只需要配置一个 @Controller 注解就可;然后是 service,很薄:
1 |
|
省下的 mapper 接口和相应的映射文件就不贴了,完整代码:Github
关于外部配置文件
有时候需要从 properties 文件中加载配置,最原始的方案是这样的:
1 | <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> |
到后来是这样的(Spring 也推荐这样用):
1 | <context:property-placeholder location="classpath:spring/jdbc.properties" /> |
以上两种方式,在 bean 定义中依然可以通过“${}”这种方式来取值。<context:property-placeholder/>
这个基于命名空间的配置,其实内部就是创建一个 PropertyPlaceholderConfigurerBean 而已,并且它有一个“有趣”的现象:
使用分散配置的方式,在两个模块中都引入了这个标签,单独运行某个模块没问题,当一起运行时就会报 Could not resolve placeholder...
的错误。
原因:
Spring 容器采用反射扫描的发现机制,在探测到 Spring 容器中有一个 PropertyPlaceholderConfigurer 的 Bean 就会停止对剩余PropertyPlaceholderConfigurer 的扫描(Spring 3.1 已经使用 PropertySourcesPlaceholderConfigurer 替代PropertyPlaceholderConfigurer 了)。
换句话说,即 Spring 容器仅允许最多定义一个 PropertyPlaceholderConfigurer (或<context:property-placeholder/>
),其余的会被 Spring 忽略掉
至于如何解决,我找到了两种,第一种就是不使用分散配置了,集合读取:
1 | "1.0" encoding="UTF-8" xml version= |
这种方式应该算是比较优雅的了,如果有特殊需求,不能使用这种方式,可以用第二种来“强制”支持多个pp标签:
1 | <context:property-placeholder order="1" location="classpath*:conf/conf_a.properties" ignore-unresolvable="true"/> |
首先来解释下这两个属性:
- ignore-unresolvable
单独使用来看是“是否忽视不存在的配置项”,不仅如此,经过测验,有一个隐含意思:是否还要扫描其他配置项:如果扫描到的为 false,则会忽视后续的 property-placeholder
默认值为 false - order
会反应出顺序,值越小优先级越高即越早执行
也就是说,当配置多个 property-placeholder 的时候,要配置 order,并且最后一个的 ignore-unresolvable 要保证为 false,其他的为 true
关于事务
上面的基本配置中应该是没有加入事务管理的,除去纯 AOP 的配置,常用的还有“声明式”和“编程式”,他们两个是可以共同使用的。
1 | <!-- 配置事务管理器 --> |
这是“声明式”事务的简单配置,这样可以在要启用事务的方法上直接使用 @Transactional
注解来开启事务,然后通过 propagation 属性可以配置其传播特性等,只需要注意下嵌套方法调用的情况就好了。
还有就是事务一般是加在 Service 层,加在 Controller 层会失效,起码和 SpringMVC 时是这样,这里和父子容器有点关系,还有就是在扫描时注意子容器的范围,这个在 Github 已经做了笔记。
然后下面就来介绍“编程式”事务:
1 | <!-- transactionManager 配置和上面一样,省略 --> |
其实就是配置了个 TransactionTemplate,然后在代码中手动调用 TransactionTemplate 来管理事务,这样能解决多线程下 @Transactional
事务失效的问题。
1 |
|
最后,如果不想写 @Transactional
注解,可以使用纯 AOP 的配置方式,简单贴下:
1 | <!-- 定义事务管理器 --> |
不过这种总体来说用的不算多,但是某些情景下是非常实用的。
评论框加载失败,无法访问 Disqus
你可能需要魔法上网~~