在前面的博文中,依次介绍了Spring、Spring mvc、Mybaits框架的功能及使用。在之前的博文中都是单独的介绍,尤其是Mybaits框架,在例子中跟本就没有用到其它的框架,这说明了他们的独立性,但反观之前的Mybaits的例子,测试的时候总是要先创建sqlSession,再获取mapper接口的动态代理对象,然后才能调用mapper对象的方法,而这些代码都是必须且重复的,十分的不便。这时,Spring的作用就发挥出来了。我们可以实现将所有对象创建和依赖关系维护交给Spring管理,Spring本身就是一个大工厂,负责bean的生产。
一般来说,先整合Spring和SpringMVC,再整合Spring和Mybaits,不过为了方便,我就直接发代码了,不分步骤,xml文件中会有详细的注释说明。以《JavaWeb高级编程》一书中的Discussion-Board工程为例,这个工程本身只是整合了Spring与Spring MVC,我在此工程的基础上添加了MySQL数据库并以Mybaits为框架,有略作修改。
首先是工程的整体结构目录:
这个工程的结构与普通直接创建的MAVEN工程有些不同,这是因为我在pom.xml文件中做了设置,只是个人喜好,不同也没有关系。
接下来是Maven的pom.xml文件
我就不显示我是怎么设置的了,其中有很多不需要的包,各种包有注释说明,依据个人需求吧,并不需要全部都写
UTF-8
2.6
tools
4.3.13.RELEASE
3.4.1
1.7.25
1.0.7
4.1.2.RELEASE
org.springframework
spring-core
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-oxm
${spring.version}
org.springframework
spring-tx
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-orm
${spring.version}
org.springframework.security
spring-security-config
${spring-security.version}
org.springframework.security
spring-security-core
${spring-security.version}
org.springframework.security
spring-security-web
${spring-security.version}
org.aspectj
aspectjweaver
1.8.7
org.codehaus.jackson
jackson-mapper-asl
1.9.5
org.mybatis
mybatis
${mybatis.version}
org.mybatis
mybatis-spring
1.3.1
org.mybatis.caches
mybatis-ehcache
1.1.0
org.mybatis
mybatis-typehandlers-jsr310
1.0.1
javax.persistence
persistence-api
1.0.2
tk.mybatis
mapper
3.4.4
com.github.pagehelper
pagehelper
4.2.1
org.mybatis.generator
mybatis-generator-core
1.3.5
redis.clients
jedis
2.9.0
org.springframework.data
spring-data-redis
2.0.2.RELEASE
junit
junit
4.12
test
mysql
mysql-connector-java
5.1.40
com.alibaba
druid
1.1.6
javax.servlet
javax.servlet-api
4.0.0
provided
javax.servlet.jsp
javax.servlet.jsp-api
2.3.1
provided
org.slf4j
slf4j-api
${org.slf4j.version}
ch.qos.logback
logback-classic
${logback.version}
jar
compile
ch.qos.logback
logback-core
${logback.version}
jar
compile
org.logback-extensions
logback-ext-spring
0.1.4
jstl
jstl
1.2
org.apache.taglibs
taglibs-standard-impl
1.2.5
commons-io
commons-io
2.4
commons-beanutils
commons-beanutils
1.9.3
commons-logging
commons-logging
1.2
commons-codec
commons-codec
1.9
org.apache.commons
commons-lang3
3.0
net.sf.json-lib
json-lib
2.4
jdk15
org.apache.poi
poi
3.5-FINAL
com.google.code.gson
gson
2.1
org.json
json
20160212
com.alibaba
fastjson
1.2.17
cn.songxinqiang
com.baidu.ueditor
1.1.2-offical
commons-fileupload
commons-fileupload
1.3.1
jar
compile
com.google.zxing
core
3.3.0
com.google.zxing
javase
3.3.0
dom4j
dom4j
1.6.1
com.thoughtworks.xstream
xstream
1.4.10
net.coobird
thumbnailator
0.4.8
org.apache.httpcomponents
httpcore
4.4.6
org.apache.httpcomponents
httpclient
4.5.3
org.eclipse.persistence
javax.persistence
2.1.1
org.freemarker
freemarker
2.3.23
com.ibeetl
btjson
0.94
com.ibeetl
beetl
2.7.18
org.quartz-scheduler
quartz
2.3.0
org.quartz-scheduler
quartz-jobs
2.3.0
然后是web.xml文件,主要是配置Spring和Spring MVC,我还配置了druid连接池监控
Disscussion-Board
index.html
index.jsp
index.html
org.springframework.web.context.request.RequestContextListener
org.springframework.web.util.IntrospectorCleanupListener
contextConfigLocation
classpath:spring/applicationContext.xml
org.springframework.web.context.ContextLoaderListener
encodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
encodingFilter
/*
SpringMVC
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring/applicationContext-mvc.xml
SpringMVC
/
1440000
DruidStatView
com.alibaba.druid.support.http.StatViewServlet
DruidStatView
/druid/*
druidWebStatFilter
com.alibaba.druid.support.http.WebStatFilter
exclusions
/public/*,*.js,*.css,/druid*,*.jsp,*.swf
principalSessionName
sessionInfo
profileEnable
true
druidWebStatFilter
/*
接下来是Spring的配置文件applicationContext.xml(这个是默认的名字,如果需要更改成其他名字,需要在web.xml文件中说明,怎么说明就请你自行百度了),需要注意的是,文件配置中取消了对Controller层的扫描加载bean,因为控制将有SpringMVC控制,如果Spring又扫描的话,控制层的bean将会被扫描两遍,会引发一些莫名奇妙的错误。
classpath:config/dbconfig.properties
注意最后mybaits配置时,对mapper文件的自动加载生成代理对象的两种方式,理论上应该是两种方式都可以,但是我只能用没有被注释掉的一种,可能是前面那种发式哪里写错了,欢迎大家指出纠正。
接下来是Springmvc的配置文件applicationContext-mvc.xml(这个名字没有要求,自己爱怎么起就怎么起,但是要跟web.xml请求分发器中限定的文件名一致)
application/json;charset=UTF-8
json=application/json
xml=application/xml
html=text/html
这样Spring 、SpringMVC和Mybaits就整合完毕了,但是还确实Mybait的主配置文件,sqlMapConfig.xml(名字也随便你起,但要跟Spring配置文件中写的一致),事实上什么都没有,因为没用上它的功能,比如开启缓存啊、setting设置啊之类的,主要是省了我们写mapper映射文件加载,Spring已经自动帮我们完成了。
配置文件就是这样的了,上面的所有东西就已经将Spring、SpringMVC、Mybaits整合完成了,接下是Discussion-Board功能的具体实现。
首先是POJO类,结构如下:
只列出属性,注意一定要有get、set方法
public class Reply implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
private Integer discussionId;
private String user;
private String message;
private LocalDateTime created;
public class Discussion implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String user;
private String subject;
private String uriSafeSubject;
private String message;
private LocalDateTime created;
private LocalDateTime lastUpdated;
private Set subscribedUsers = new HashSet<>();
public class DiscussionForm implements Serializable{
private static final long serialVersionUID = 1L;
private String user;
private String subject;
private String message;
public class ReplyForm implements Serializable{
private static final long serialVersionUID = 1L;
private String user;
private String message;
接下来时mapper接口类,也就是在平常的Dao层:
public interface DiscussionMapper {
List getAll();
Discussion get(int id);
void add(Discussion discussion);
void update(Discussion discussion);
}
public interface ReplyMapper {
List getForDiscussion(int id);
void add(Reply reply);
void update(Reply reply);
}
mapper映射文件代码如下:
ReplyMapper.xml
insert into reply(discussionId,user,message,created)
values (#{discussionId},#{user},#{message},#{created})
update reply set message = #{message}, created = #{created} where id = #{id}
DiscussionMapper.xml:
insert into discussion(user,subject,uriSafeSubject,message,created,lastUpdated)
values(#{user},#{subject},#{uriSafeSubject},#{message},#{created},#{lastUpdated})
update discussion set subject = #{subject} ,uriSafeSubject = #{uriSafeSubject} , lastUpdated= #{lastUpdated} where id = #{id}
然后是Service层,采用面向接口编程的方式,结构如下:
接口文件如下:
public interface DiscussionService {
List getAllDiscussion();
Discussion getDiscussion(int id);
void saveDiscussion(Discussion discussion);
}
public interface ReplyService {
List getRepliesForDiscussion(int discussionId);
void saveReply(Reply reply);
}
接口实现类如下:
@Service
public class DefaultDiscussionService implements DiscussionService{
@Autowired
DiscussionMapper discussionMapper;
@Override
public List getAllDiscussion() {
List list = this.discussionMapper.getAll();
list.sort((d1,d2) -> d1.getLastUpdated().compareTo(d2.getLastUpdated()));
return list;
}
@Override
public Discussion getDiscussion(int id) {
// TODO Auto-generated method stub
return this.discussionMapper.get(id);
}
@Override
public void saveDiscussion(Discussion discussion) {
// TODO Auto-generated method stub
String subject = discussion.getSubject();
subject = Normalizer.normalize(subject.toLowerCase(), Normalizer.Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "")
.replaceAll("[^\\p{Alnum}]+", "-")
.replace("--", "-").replace("--", "-")
.replaceAll("[^a-z0-9]+$", "")
.replaceAll("^[^a-z0-9]+", "");
discussion.setUriSafeSubject(subject);
LocalDateTime now = LocalDateTime.now();
discussion.setLastUpdated(now);
if(discussion.getId() == null){
discussion.setCreated(now);
discussion.getSubscribedUsers().add(discussion.getUser());
this.discussionMapper.add(discussion);
}else
this.discussionMapper.update(discussion);
}
}
@Service
public class DefaultReplyService implements ReplyService{
@Autowired
ReplyMapper replyMapper;
@Autowired
DiscussionService discussionService;
@Override
public List getRepliesForDiscussion(int discussionId) {
List list = this.replyMapper.getForDiscussion(discussionId);
list.sort((r1,r2) -> r1.getId()
最后是逻辑控制层,其实现如下:
@Controller
@RequestMapping("discussion")
public class BoradController {
@Autowired
DiscussionService discussionService;
@RequestMapping(value={"","list"},method = {RequestMethod.GET,RequestMethod.POST})
public String listDiscussions(Map model,HttpServletRequest request){
model.put("discussions",this.discussionService.getAllDiscussion());
return "discussion/list";
}
@RequestMapping(value="create",method = RequestMethod.GET)
public String createDiscussion(Map model,HttpServletRequest request){
model.put("discussionForm", new DiscussionForm());
return "discussion/create";
}
@RequestMapping(value="create",method = RequestMethod.POST)
public String createDiscussion(DiscussionForm from){
Discussion discussion = new Discussion();
discussion.setUser(from.getUser());
discussion.setSubject(from.getSubject());;
discussion.setMessage(from.getMessage());
this.discussionService.saveDiscussion(discussion);
return "forward:/discussion/list";
}
}
@Controller
@RequestMapping("discussion/{discussionId:\\d+}")
public class DiscussionController {
@Autowired
DiscussionService discussionService;
@Autowired
ReplyService replyService;
@RequestMapping(value ={"","*"},method = RequestMethod.GET)
public String viewDiscussion(Map model,@PathVariable("discussionId") int id){
Discussion discussion = this.discussionService.getDiscussion(id);
if(discussion != null){
model.put("discussion", discussion);
model.put("replies", this.replyService.getRepliesForDiscussion(id));
model.put("replyForm", new ReplyForm());
return "discussion/view";
}
return "discussion/errorNoDiscussion";
}
@RequestMapping(value="reply",method=RequestMethod.POST)
public ModelAndView reply(ReplyForm form,@PathVariable("discussionId") int id){
Discussion discussion = this.discussionService.getDiscussion(id);
if(discussion != null){
Reply reply = new Reply();
reply.setDiscussionId(id);
reply.setUser(form.getUser());
reply.setMessage(form.getMessage());
this.replyService.saveReply(reply);
return new ModelAndView(new RedirectView("/discussion/"+id+"/"+discussion.getUriSafeSubject(),true,false));
}
return new ModelAndView("discussion/errorNoDiscussion");
}
}
最后是视图层,其结构如果:
create.jsp文件代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
Insert title here
Create Discussion
返回列表界面
邮箱:
Subject
Message
errorNoDiscussion.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
Insert title here
Error: Discussion Not Found
你输入的讨论不存在!
[">返回列表]
list.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--@elvariable id="discussions" type="java.util.List"--%>
Insert title here
Discussions
[">new discussion]
">
( )
There are no discussions yet.
view.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%--@elvariable id="discussion" type="com.wrox.discussionboard.pojo.Discussion"--%>
<%--@elvariable id="replies" type="java.util.List"--%>
Insert title here
Discussion:
[">return to list]
User: / Created:
Reply #${i.count} - -
There are no replies yet.
Post Reply
Your Email
Message
启动Tomcat,在浏览器地址中输入:http://localhost/Discussion-Board/discussion/list因有如下显示:
我是因为数据库中已经存有了信息才会有这些显示,如果数据库为空,应显示There are no discussions yet.
这篇博文提醒了我要搞个github账户来玩一玩,否则的话非重点的代码实在是太多了!!