2018/1/7
SpringMVC 与单例设计 :
通过《Spring(20):单例模式详解与示例分析(懒汉模式/饿汉模式/静态内部类改造)》我们知道了单例模式的概念,SpringMVC 框架中也对其单例进行了运用,而且Controller 类默认是单例的,了解这一点很重要!!当然,这样也是基于性能的考虑,因为Controller设计为单例,就不用每次都创建实例,速度和性能自然优越。注意:使用的时候也要注意 -- 一般不在Controller 中定义成员变量,因为会导致严重线程安全安全和资源使用情况。
SpringMVC 实例:
一、无查询条件
【0】新建 User.java 文件,一定包含有参数的构造器:
package com.smbms.entities;
import java.util.Date;
import java.util.List;
public class User {
private Integer id;
private String userCode;
private String userName;
private String userPassword;
private Integer gender;
private Date birthday;
private String phone;
private String address ;
private Integer userRole;
private Integer createdBy;
private Date creationDate;
private Integer modifyBy;
private Date modifyDate;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Integer getUserRole() {
return userRole;
}
public void setUserRole(Integer userRole) {
this.userRole = userRole;
}
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public Integer getModifyBy() {
return modifyBy;
}
public void setModifyBy(Integer modifyBy) {
this.modifyBy = modifyBy;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
public User(Integer id, String userCode, String userName, String userPassword, Integer gender, Date birthday,
String phone, String address, Integer userRole, Integer createdBy, Date creationDate, Integer modifyBy,
Date modifyDate) {
super();
this.id = id;
this.userCode = userCode;
this.userName = userName;
this.userPassword = userPassword;
this.gender = gender;
this.birthday = birthday;
this.phone = phone;
this.address = address;
this.userRole = userRole;
this.createdBy = createdBy;
this.creationDate = creationDate;
this.modifyBy = modifyBy;
this.modifyDate = modifyDate;
}
public User() {
super();
// TODO 自动生成的构造函数存根
}
@Override
public String toString() {
return "User [id=" + id + ", userCode=" + userCode + ", userName=" + userName + ", userPassword=" + userPassword
+ ", gender=" + gender + ", birthday=" + birthday + ", phone=" + phone + ", address=" + address
+ ", userRole=" + userRole + ", createdBy=" + createdBy + ", creationDate=" + creationDate
+ ", modifyBy=" + modifyBy + ", modifyDate=" + modifyDate + "]";
}
}
【1】写控制器 IndexController.java:
package com.smbms.controller;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.smbms.entities.User;
@Controller
@RequestMapping("/a")
public class IndexController {
private Logger log = Logger.getLogger(IndexController.class.getName());
private ArrayList<User> userList = new ArrayList<User>();
public IndexController(){
try{
userList.add(new User(11,"test001","测试用户001","1111111",1,new SimpleDateFormat("yyyy-MM-dd").parse("1986-12-10"),"13456666998","北京朝阳区北苑",1,1,new Date(),1,new Date()));
userList.add(new User(12,"zhaoyan","赵燕","2222222",1,new SimpleDateFormat("yyyy-MM-dd").parse("19846-12-10"),"13456666998","北京朝阳区车姑路",1,1,new Date(),1,new Date()));
userList.add(new User(13,"test003","测试用户003","333333",1,new SimpleDateFormat("yyyy-MM-dd").parse("1986-12-10"),"1345666766998","北京朝阳区北苑",1,1,new Date(),1,new Date()));
userList.add(new User(11,"wanglin","王林","1111111",1,new SimpleDateFormat("yyyy-MM-dd").parse("1989-12-10"),"13457777998","北京朝阳区学府",1,1,new Date(),1,new Date()));
userList.add(new User(11,"zhaojing","赵静","1111111",1,new SimpleDateFormat("yyyy-MM-dd").parse("1981-12-10"),"13874366998","北京朝阳区五山",1,1,new Date(),1,new Date()));
}catch(Exception e){
e.printStackTrace();
}
}
//POJO+Model
@RequestMapping(value="/hello",method=RequestMethod.GET)
public String hello5(Model model){
log.info("无查询条件下,获取userlist(公共查询)====userlist");
model.addAttribute("queryUserList",userList);
return "helloB";
}
}
解释:没有用到数据库,因此,id重复暂不做处理。
【2】写一个超链接文本index.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<a href="http://www.baidu.com">click</a>
<a href="a/hello?username=mmb">hello-username</a>
<a href="a/hello?usernameA=mmb">hello-usernameA</a>
<a href="a/hello?usernameB=mmb">hello-usernameB</a>
<table width="400">
<a href="a/hello"> userlist </a>
</table>
</body>
</html>
解释:点击userlist连接。
<a href="a/hello"> userlist </a>
【3】写响应视图 helloB.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>查询用户列表</h1>
<c:forEach var="user" items="${queryUserList}">
<div>
id:${user.id} ---
userCode:${user.userCode} ---
userName:${user.userName} ---
code:${user.userPassword} ---
birthday:${user.birthday} ---
address:${user.address} ---
</div>
</c:forEach>
</body>
</html>
解释:(1)使用了JSTL标签和EL表达式,注意加入依赖包;
(2)<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>需要依赖包;按照查到的资料,JSTL taglib需要jstl.jar来支持。
在1.0和1.1版本的时候,还需要standard.jar来配合。但从1.2版本开始,jar文件名字变成了jstl-1.2.jar,也不再需要standard.jar了。
【4】输出结果:
18/01/08 23:09:00 INFO controller.IndexController: 无查询条件下,获取userlist(公共查询)====userlist
上面的无查询条件下,获取userlist正常显示。但如果增加了查询条件,就会出问题了。
二、加入查询条件
【1】修改IndexController.java,添加以下内容:
private ArrayList<User> queryUserList = new ArrayList<User>();
//增加查询条件:username
@RequestMapping(value="/list" ,method=RequestMethod.POST)
public String hello6(@RequestParam(value="userName",required=false)
String userName,Model model){
log.info("加入查询条件: username: "+ userName+", ====userlist");
if(userName != null && !userName.equals("")){
for(User user:userList){
if(user.getUserName().contains(userName)){
queryUserList.add(user);
// log.info("user.toString()"+user.toString());
}
}
model.addAttribute("quertuserList",queryUserList);
}else{
model.addAttribute("quertuserList",userList);
}
return "helloC";
}
解释:(1)再添加一个处理器hello6()方法,响应的连接为“a/list”(是通过helloB页面添加的一个提交表单)。
(2)进行用户名的模糊查询,如果已有的数据包含输入字段,则再次通过页面展现。
【2】修改helloB.jsp ,添加了以下内容,多了一个提交按钮:
<h1>有条件查询用户列表</h1>
<form action="${pageContext.request.contextPath}/a/list" method="post">
用户名称name:<input type="text" name="userName" value=""/>
<input type="submit" name="查询check"/>
</form>
【3】新建
helloC.jsp ,和 helloB.jsp 大同小异 ,展示条件筛选后的结果:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>有条件查询用户列表结果</h1>
<c:forEach var="user" items="${quertuserList}">
<div>
id:${user.id} ---
userCode:${user.userCode} ---
userName:${user.userName} ---
code:${user.userPassword} ---
birthday:${user.birthday} ---
address:${user.address} ---
</div>
</c:forEach>
</body>
</html>
【4】输出结果:
控制台:
18/01/09 00:02:10 INFO controller.IndexController: 无查询条件下,获取userlist(公共查询)====userlist
18/01/09 00:02:26 INFO controller.IndexController: 加入查询条件: username: 3, ====userlist
Web:
图1
图2
(因为测试了两次,因此ArrayList存放了两个数据)
解释:(1)在高并发模式下,用户a和b同时输入不同的查询条件后,有可能数据显示不正确 --- 即b用户的CPU抢占了资源,那么a用户显示的就是b的查询结果了。
(2)根据业务需求就要更改Controller的scope 为prototype,在@Controller之前增加@Scope("prototype"),就不再是单例了,但这种做法很少用。SpringMVC 框架设计为单例,自然为了提高性能。
(3)上面配的两幅图,是单用户查询结果,所以是正确的。。
三、总结
通过以上实例,SpringMVC的Controller是单例的。其内部的成员变量是公开的,开发的时候需要注意资源使用的问题。上面讲到了按条件查询,实际运用还有更新(update),会出现类似:一个线程修改了某个成员变量,其他线程都会拿到修改后的结果,显然是不对的。
因此,在Controller 内,我们可以声明Service 成员变量,通过@Resource 或者 @Autowired 方式装载。
eg:
@Resource
private Userservice userService;
由于Service 是公用的,并且它是接口,接口内不含有成员变量,都是方法,而方法内的变量也都是线程级的,故不会出现数据资源抢占或内存溢出。所以一般情况下,Controller 内的成员变量就只有Service 对象了。
以上是博文的全部内容。