Spring MVC是当前最优秀的MVC框架,自从Spring 2.5版本发布后,由于支持注解配置,易用性有了大幅度的提高。Spring 3.0更加完善,实现了对老牌的MVC框架Struts 2的超越。现在版本已经到了Spring7.x了,我们这是基于Spring5.0.6的SpringMVC框架来写案例。现在绝大多数的开发团队选择了Spring MVC。
感受一下SpringMVC:
一个简单的例子。新建一个maven项目springmvcdemo
,sts开发工具建maven项目,参考spring例子中的,springdemo整合hibernate。
1.导包,其它和Spring项目中一样
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
2.web.xml中配置DispatcherServlet。把单标签变成双标签,即把’/>‘改成’>’
使用springmvc的核心的调度器,servlet配置之。
<web-app......>
<!-- 使用springmvc的核心的调度器,servlet配置之 -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定springmvc的配置文件所在的位置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 指定映射地址,一般是网站根目录,包括静态资源,但不包括其它servlet,一般用jsp文件对应控制层的名字 -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!-- 不能写成 /*,这种是用在filter,拦截器中 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
3.springmvc.xml —bean configuration file
以下一定要勾选如图所示的命名空间。配注解创建bean所在包基本类包和视图解析器。
<?xml version="1.0" encoding="UTF-8"?>
<beans .....>
<!-- 指定注解所在的基本包 -->
<context:component-scan base-package="cn.ybzy.springmvcdemo"></context:component-scan>
<!-- 为springmvc指定视图解析器,视图指定在WEB-INF/jsp中 ,视图扩展名为jsp-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
4.创建视图(/WEB-INF/jsp/index.jsp)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>index</title>
</head>
<body>
这里是index.jsp,你可能是由控制的@RequestMapping("/index")映射过来的
</body>
</html>
5.控制层IndexController.java,跳转或转发到视图
@Controller //不象在spring中,那样要继承HttpServlet了
public class IndexController {
@RequestMapping("/index")
public String index() {
return "index";
}
}
6.测试:localhost:8080/springmvcdemo/index回车
这个例子的大致流程分析:
@RequestMapping注解
@RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一。这个注解会将 HTTP 请求映射到控制器(controller类)的处理方法上。在 Spring MVC 应用程序中,RequestDispatcher (在 Front Controller 之下) 这个 servlet 负责将进入的 HTTP 请求路由到控制器的处理方法。 在对 Spring MVC 进行的配置的时候, 需要我们指定请求与处理方法之间的映射关系。
@RequestMapping 注解在前面的例子中是在方法的级别上使用。除此外,这个注解还可使用在类级别的:
在类的级别上的注解会将一个特定请求或者请求模式映射到一个控制器之上。之后才是另外添加在方法级别的注解来进一步指定到处理方法的映射关系。
下面是一个同时在类和方法上应用了 @RequestMapping 注解的示例:
说白了,体现在浏览器上访问地址上,就是父目录:xxxxxx/index/index
RequestMapping里面的注解包含的参数
1、value, method
value(别名:path): 指定请求的实际地址,指定的地址可以是URI Template 模式;
value的uri值为以下三类:
/*
* localhost:8080/springmvcdemo/indexid/100回车,控制台会打印ids====12,网页会是index.jsp内容
*/
@RequestMapping("/indexid/{id}")
public String indexid(@PathVariable ("id") int ids) {
System.out.println("ids====="+ids);
return "index";
}
method:默认会自动匹配GET或POST请求,可以指定请求的method类型 取值可以是GET、POST、PUT、DELETE等;
举例说明一下method,看JSP 页面
<a href="springmvc/testMethod">Test Method</a> //href 默认为get 请求。
//controller里限制接收post 请求。
@RequestMapping(value = "/testMethod", method = RequestMethod.POST)
public String testMethod() {
System.out.println("testMethod");
return "index";
}
2、 consumes,produces;
consumes: 指定处理请求的提交内容类型(Content-Type)
例如application/json, text/html;
JSP 页面,仍以testMethod为例,提交表单。
默认contentType为Content-Type:application/x-www-form-urlencoded。
例:controller–限制接收post 请求以及consumes=“application/json”。
【状态码415表示:由于媒介类型不被支持,服务器不会接受请求。。】
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
浏览器请求头
其中最后一项 : */*;q=0.8。
该项表明可以接收任何类型的,权重系数0.8表明如果前面几种类型不能正常接收。则使用该项进行自动分析。
application/json 几种主流浏览器都可以自动解析。
3、 params,headers;
params:指定request中必须包含某些参数值,才让该方法处理。
JSP页面(index.jsp)
<body>
<form action="${pageContext.request.contextPath}/testConsumes" method="post">
<input type="text" name="username" value="xiongshaowen">
<input type="text" name="age" value="10">
<input type="submit" value="提交">
</form>
</body>
后台代码
@RequestMapping(value="/testConsumes",method=RequestMethod.POST,params= {"username","age!=10"})
public String testConsumes() {
System.out.println("进来了testComsumes方法");
return "success";
}
测试:由最上面的例子,由index后台映射到index.jsp上
localhost:8080/springmvcdemo/index回车,如果提交的数为10,则发发生错误 HTTP Status 400 – Bad Request
,不是10 都可到success.jsp中
headers
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
这里我们把接收的语言,中文(zh-CN)改为英文(en-US).再测试
@RequestMapping(value="/testConsumes",method=RequestMethod.POST,
headers= {"Accept-Language=en-US,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"})
localhost:8080/springmvcdemo/index回车,会发生HTTP Status 404 – Not Found
错误 。
4.最后一个name参数:
SpringMVC 4.0开始支持基于name值来构建访问的url,需要通过配置
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
你可以自定义name属性,默认是通过类的大写字符+#+方法名构建的,比如TestController的getUser方法,name值默认为TC#getUser.
使用上,官方说明是主要可以通过使用Spring jsp tag包里面的mvcUrl,来生成jsp到controller的链接.
例:
@RequestMapping(value="/testConsumes",method=RequestMethod.POST,name="yyyy")
public String testConsumes() {
System.out.println("进来了testComsumes方法");
return "success";
}
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>index</title>
</head>
<body>
<!-- <form action="${pageContext.request.contextPath}/testConsumes" method="post">-->
<form action="${s:mvcUrl('yyyy').build()}" method="post">
<input type="text" name="username" value="xiongshaowen">
<input type="text" name="age" value="10">
<input type="submit" value="提交">
</form>
</body>
</html>
感觉上比之前自己拼装URL(pageContext.request.contextPath)简单靠谱一点。
<a href="${s:mvcUrl('yyyy').build().arg(0,"123").build()}">Get Person</a>,其中arg还可以带参值,如上面的indexid例中的 /indexid/123
RequestMapping注解中的通配符:
RequestMapping注解中的映射请求可以支持3种通配符:
1、?:匹配文件中的一个字符
2、* :匹配任意 多个字符
3、** :匹配多层路径
例:
@RequestMapping(value="/testConsumes/*",method=RequestMethod.POST)
public String testConsumes() {
System.out.println("进来了testComsumes方法");
return "success";
}
<form action="${pageContext.request.contextPath}/testConsumes/ba" method="post">
<input type="text" name="username" value="xiongshaowen">
<input type="text" name="age" value="10">
<input type="submit" value="提交">
</form>
注解@RequestParam
@RequestParam注解是在SpringMvc后台进行获取参数数据的注解:
@RequestMapping("/indexid/{id}")
public String indexid(@PathVariable("id") int ids){
xxxx
return "success";
}
前面讲的restFull风格获取的是点位符的参数值,我们这里要从表单提交中获取值。
例:后台获取表单提交的姓名,年龄,get请求也一样用
IndexController.java
@RequestMapping(value="/testRequestParam",method=RequestMethod.POST)
//public String testRequestParam(@RequestParam String username,int age) {
public String testRequestParam(@RequestParam(name="username") String un,@RequestParam(name="age") int nianling) {
//System.out.println("username===="+username);
//System.out.println("age========="+age);
System.out.println("username===="+un);
System.out.println("age========="+nianling);
return "success";
}
@RequestMapping({"/index","/"," "}) //为了测试方例,我只要输入项目名即可转到index.jsp页中
public String index() {
return "index";
}
index.jsp
<form action="${pageContext.request.contextPath}/testRequestParam" method="post">
<input type="text" name="username" value="xiongshaowen">
<input type="text" name="age" value="10">
<input type="submit" value="提交">
</form>
springmvc会自动根据参数名字来注入,所以要名字一致,不然不会注入.
参数名字不一致的话,需要在@RequestParam后面指定参数名字,才能为后面的参数进行赋值。
设置默认值
如果要@RequestParam为一个int型的数据传值,假如前端并未输入,那么将会为int型的数据赋值为null。显然,这是不允许的,直接报错。 可以设置默认值为0来解决这种报错情况!
@RequestMapping(value="/testRequestParam",method=RequestMethod.POST)
public String testRequestParam(@RequestParam String username,@RequestParam(defaultValue="0") int age) {
System.out.println("username===="+username);
System.out.println("age========="+age);
return "success";
}
------------------------------------------------------------------------------
username====xiongshaowen
age=========0
可以通过required=false或者true来要求@RequestParam配置的前端参数是否一定要传
@RequestMapping(value="/testRequestParam",method=RequestMethod.POST)
public String testRequestParam(@RequestParam String username,@RequestParam(required=false) int age) {
System.out.println("username===="+username);
System.out.println("age========="+age);
return "success";
}
不过这种方式,当数据定义为int时,会产生错误页面,不好看。
可以改为Integer.
@RequestMapping(value="/testRequestParam",method=RequestMethod.POST)
public String testRequestParam(@RequestParam String username,@RequestParam(required=false) Integer age){
xxxxxxx
}
---------------------------------------
username====xiongshaowen
age=========null
注解@RequestHeader和@CookieValue
@RequestHeader和@CookieValue也是在SpringMvc后台进行获取数据的注解,只不过不是前端传来的参数,而是前端请求的头部信息和Cookie信息:
@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader("Accept-Encoding") String ec) {
System.out.println(ec);
return "success";
}
-----------------------localhost:8081/springmvcdemo/testRequestHeader----------------------------------
gzip, deflate, br
@RequestMapping("/testRequestCookie")
public String testRequestCookie(@CookieValue("JSESIONID") String sessioncookie) {
System.out.println(sessioncookie);
return "success";
}
------------------------localhost:8081/springmvcdemo/testRequestCookie----------------------------------
9A97CC0821C30FD7972DBE64B6C5876D
页面里传数据到SpringMVC中的处理
1.直接读取表单的数据。
直接读取表单的数据。在控制器里的方法里,按照这种格式写,就是以前讲的mvcproject中一样,利用形如req,resp对象来获取转发数据。
@RequestMapping("/testjsp2mvc")
public void testJsp2Mvc(HttpServletRequest req,HttpServletResponse resp) throws IOException{
req.setCharacterEncoding("utf-8");
String username=req.getParameter("username");
int age = Integer.parseInt(req.getParameter("age"));
System.out.println("username===="+username);
System.out.println("age========="+age);
resp.getWriter().print(username);//输出到页面上, 不用其它页面模板
}
<!-- <form action="${pageContext.request.contextPath}/testRequestParam" method="post">-->
<form action="${pageContext.request.contextPath}/testjsp2mvc" method="post">
<input type="text" name="username" value="xiongshaowen">
<input type="text" name="age" value="10">
<input type="submit" value="提交">
</form>
直接读取还有另一种方式,用Requestparam注解
@RequestMapping(value="/testRequestParam",method=RequestMethod.POST)
//public String testRequestParam(@RequestParam String username,@RequestParam(required=false) Integer age) {
public String testRequestParam(String username,int age) { //参数名与提交的名字一样的话,并且没有空的情况下,@RequestParam可省略
//public String testRequestParam(@RequestParam(name="username") String un,@RequestParam(name="age") int nianling) {
System.out.println("username===="+username);
System.out.println("age========="+age);
//System.out.println("username===="+un);
//System.out.println("age========="+nianling);
return "success";
}
2)采取普通对象(POJO)传值的方式:
jsp页面里的表单元素的name名字为java对象的字段名。
比如:用户的注册页面:
User.java
public class User {
private int id;
private String username;
private String password;
//get,set,toString(),无参构造方法
}
index.jsp
<form action="${pageContext.request.contextPath}/addUser" method="post">
<input type="text" name="username" value="xiongshaowen">
<input type="text" name="password" value="123456">
<input type="submit" value="提交">
</form>
IndexController.java
@RequestMapping("/addUser")
public String addUser(User user) {
System.out.println("User.username==="+user.getUsername());
System.out.println("User.password==="+user.getPassword());
return "success";
}
@RequestMapping({"/index","/"," "}) //为了测试方例,我只要输入项目名即可转到index.jsp页中
public String index() {
return "index";
}
3)通过url参数传递。将url的参数和形参进行一个匹配:
自动映射URL对应的参数到Action上面的数值,RequestParam 默认为必填参数。
@RequestMapping(value="/testRequestParam",method=RequestMethod.POST)
public String testRequestParam(@RequestParam String username,@RequestParam(required=false) Integer age) {
System.out.println("username===="+username);
System.out.println("age========="+age);
return "success";
}
springmvc控制层注入数据到页面
让我们看看在controller中定义的方法的参数的类别:
默认支持的参数类型
一、综述
Spring MVC 提供了以下途径来输出模型数据:
- ModelAndView
当处理方法返回值类型为 ModelAndView时, 方法体即可通过该对象添加模型数据到请求域(request域空间中,可通过EL获取输出)。 - Map 及 Model和ModelMap
输入参数为org.springframework.ui.Model、org.springframework.ui.ModelMap 或 java.uti.Map 时,处理方法返回时,Map中的数据会自动添加到模型中。 - @SessionAttributes
该注解将模型中的某个属性暂存到HttpSession 中,以便多个请求之间可以共享这个属性。 - @ModelAttribute: 方法输入参数标注该注解后, 入参的对象就会放到数据模型中。
二ModelAndView和Map&Model&ModelMap
ModelAndView对象中包含了一个model属性和一个view属性。
model:是一个ModelMap的类型。而ModelMap又是一个LinkedHashMap的子类。
view:包含了一些视图信息。
实现原理:
当视图解释器解析ModelAndVIew类型的参数时,其中model是一个Map的实现类的子类,视图解析器将model中的每个元素都通过request.setAttribute(name, value)方法,添加request请求域中。这样就可以在JSP页面中通过EL表达式来获取对应的值
操作方法:
添加模型数据:
addObject(String attributeName, Object attributeValue)
设置视图:
setViewName(String viewName)
例:因为要循环遍历Map.List集事等,用到jstl标签库,所以要导两个包
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-spec</artifactId>
<version>1.2.5</version>
<type>bundle</type>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
<type>bundle</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin> <!-- 插件是处理bundle包用的-->
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
</plugin>
</plugins>
</build>
</project>
下面举例,处理四种数据类型
@RequestMapping("/testMV")
public ModelAndView testMV() {
ModelAndView mv =new ModelAndView();
//字符串
mv.addObject("name","xiongshaowen");
//对象
User user=new User();
user.setUsername("熊少文");
user.setPassword("xiong");
mv.addObject("user",user);
//集合List
User user1=new User();
user1.setId(1);
user1.setUsername("徐会凤");
user1.setPassword("xu");
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user);
mv.addObject("users", users);
//集合map
Map<String,User> map = new HashMap<String, User>();
map.put("user", user);
map.put("user1", user1);
mv.addObject("map",map);
mv.setViewName("success");
return mv;
}
-----------------------------------------------------------------------
@RequestMapping("/testMV2")
public String testMV2(Map<String, Object> map) {
// 字符串
map.put("name", "xiongshaowen");
// 对象
User user = new User();
user.setUsername("熊少文");
user.setPassword("xiong");
map.put("user", user);
// 集合
User user1 = new User();
user1.setId(1);
user1.setUsername("徐会凤");
user1.setPassword("xu");
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user);
map.put("users", users);
Map<String, User> maps = new HashMap<String, User>();
maps.put("user", user);
maps.put("user1", user1);
map.put("maps", maps);
return "success";
}
----------------------------------------------------------------------------
@RequestMapping("/testModel")
public String testModel(Model model) { //ModelMap model也一样,下面代码都不用改,而且可用put添加
// 字符串
model.addAttribute("name", "xiongshaowen");
// 对象
User user = new User();
user.setUsername("熊少文");
user.setPassword("xiong");
model.addAttribute("user", user);
// 集合
User user1 = new User();
user1.setId(1);
user1.setUsername("徐会凤");
user1.setPassword("xu");
List<User> users = new ArrayList<User>();
users.add(user1);
users.add(user);
model.addAttribute("users", users);
Map<String, User> maps = new HashMap<String, User>();
maps.put("user", user);
maps.put("user1", user1);
model.addAttribute("maps", maps);
return "success";
}
success.jsp
<body>
访问成功!<br>
name:${requestScope.name} <br>
name:${name}
<br><!-- User对象 -->
User: ${user}
<br><br><!-- 集合,这里只打印多个对象的姓名, -->
<c:forEach var="user" items="${users}">
${user.username }
</c:forEach>
<br><!-- Map集合的输出 -->
<c:forEach items="${map}" var="node">
对象:${node.key}, 姓名:${node.value.username}, 密码:${node.value.password}<br>
</c:forEach>
测试:localhost/springmvcdemo/testVM回车,返个视图到success.jsp中
访问成功!
name:xiongshaowen
name:xiongshaowen
User: User [id=0, username=熊少文, password=xiong]
徐会凤 熊少文
对象:user1, 姓名:徐会凤, 密码:xu
对象:user, 姓名:熊少文, 密码:xiong
三、@SessionAttributes
如果我们希望在多个请求之间共用某个模型属性数据,则可以在控制器类上标注一个 @SessionAttributes, Spring MVC 将把模型中对应的属性暂存到 HttpSession 的域中。
使用方法:
@SessionAttributes(value={"xxx"}, types={xxxx.class})
value:是通过键来指定放入HttpSession 的域中的值;
types:是通过类型指定放入HttpSession 的域中的值;
使用举例:
@SessionAttributes(types=User.class)
这个注解会将类中所有放入Request域中的User对象同时放进HttpSession的域空间中。
//@SessionAttributes(value= {"name","user","user1"})
@SessionAttributes(types= {User.class,String.class}) //这两个定法功能一样,这一行更简化了代码
@Controller
public class SeesionAttributesController {
@RequestMapping("/testSession")
public String testSessionAttributes(ModelMap model) {
//字符串
model.addAttribute("name","xiongshaowen");
//User对象
User user = new User();
user.setId(3);
user.setUsername("熊少文");
user.setPassword("1234546");
model.addAttribute("user",user);
return "session";
}
}
当去掉类上的注解@SessionAttribute后,sessionScope中没有对象
<body>
<h4>SessionAttribute</h4>
requestScope.name: ${requestScope.name }<br><br>
sessionScope.name: ${sessionScope.name }<br><br>
name: ${name } <br>
requestScope user:${requestScope.user }<br><br>
sessionScope user:${sessionScope.user}<br><br>
user: ${user}
</body>
效果1
requestScope.name: xiongshaowen
sessionScope.name: xiongshaowen
name: xiongshaowen
requestScope user:User [id=3, username=熊少文, password=1234546]
sessionScope user:User [id=3, username=熊少文, password=1234546]
user: User [id=3, username=熊少文, password=1234546]
效果2,没有注解@SessionAttributes
requestScope.name: xiongshaowen
sessionScope.name:
name: xiongshaowen
requestScope user:User [id=3, username=熊少文, password=1234546]
sessionScope user:
user: User [id=3, username=熊少文, password=1234546]
可以添加多个属性
@SessionAttributes(value={“user1”, “user2”}) 、
@SessionAttributes(types={User.class, Dept.class})
可以混合使用
@SessionAttributes(value={“user1”, “user2”},types={Dept.class})
注意: 该注解只能放在类的上面. 而不能修饰放方法。
四、@ModelAttribute
使用方式:
- 在方法定义上使用 @ModelAttribute 注解,Spring MVC在调用目标处理方法前,会先逐个调用在方法级上标注了@ModelAttribute 的方法。
- 在方法的参数前使用 @ModelAttribute 注解,主要用做和使用 @ModelAttribute 注解的方法中Map的Key匹配!
初步了解:
当同一个controller中有任意一个方法被@ModelAttribute注解标记,页面请求只要进入这个控制器,不管请求那个方法,均会先执行被@ModelAttribute标记的方法,所以我们可以用@ModelAttribute注解的方法做一些初始化操作。当同一个controller中有多个方法被@ModelAttribute注解标记,所有被@ModelAttribute标记的方法均会被执行,按先后顺序执行,然后再进入请求的方法。
例:只要被@ModelAttribute标记的方法,会在执行其它的方法之前先执行一次。
@Controller // 不象在spring中,那样要继承HttpServlet了
public class IndexController {
@ModelAttribute
private void testModelAttribute() {
System.out.println("@ModelAttribute这个注解标注的方法先执行了");
}
/*
* localhost:8081/springmvcdemo/indexid/100回车,控制台会打印ids====12,网页会是index.jsp内容
*/
@RequestMapping("/indexid/{id}")
public String indexid(@PathVariable("id") int ids) {
System.out.println("ids=====" + ids);
return "index";
}
}
测试:localhost:8081/springmvcdemo/index/1000回车,控制台打印如下信息
六月 19, 2025 8:46:26 上午 org.apache.catalina.startup.Catalina start
信息: Server startup in 19954 ms
@ModelAttribute这个注解标注的方法先执行了
例:应用场影中使用:
当我们修改用户信息时,有些敏感字段如名称,不能修改,往往在修改时,我们用表单提交,所以压根就没有值,如名称是null.若提交修改,会把数据库表中的username搞成null了,就不是我们想的了。
传统的处理方法
1.通过传来的id值,在数据据库中找到对应的记录,返回一个User对象
2.将过来的User对象的属性覆盖数据库中查出来User对象。
user2.setAge(user.getAge());
3.调用服务层的update方法更新到数据库
userService.update(user2);
利用@ModelAttribute注解标记方法
@Controller
public class IndexController {
@ModelAttribute
private void testModelAttribute(@RequestParam(value="id",required=false) Integer id,Model model) {
System.out.println("id=:"+id);
if(id !=null) { //Integer是一个类,不是int基本数据类型,可以用null来判断有没有内容
//去数据库取相应的记录,返回User对象
User user=new User();
//取出各字段的值,封到对象中去
user.setId(1);
user.setUsername("xiongshaowen");
user.setPassword("admin123");
System.out.println(user);
model.addAttribute("user",user);
}
}
@RequestMapping("/updateUser")
public String updateUser(User user) {
System.out.println("要修改的用户信息:" + user);
return "success";
}
@RequestMapping({ "/index", "/", " " }) // 为了测试方例,我只要输入项目名即可转到index.jsp页中
public String index() {
return "index";
}
-------------------------------------------------------------------------------------------
id=:1
User [id=1, username=xiongshaowen, password=admin123]
要修改的用户信息:User [id=1, username=xiongshaowen, password=123456]
index.jsp提交表单:注意不修改字段不要写上表单,如上文中所讲,不要写username表单项,一旦写上,就是它的内容(即便是空字符串)
<form action="${pageContext.request.contextPath}/updateUser" method="post">
<input type="hidden" name="id" value="1">
<input type="text" name="password" >
<input type="submit" value="提交">
</form>
综上所述:当我们这样处理了,名称就不会产生null,从而更新到数据库后,名称保持不变。
运行流程简单示意
1)执行目标方法之前先执行 @ModelAttribute 注解修饰的方法: 从数据库中取出对象, 把对象放入到了 Map 中。键为: user。
2)SpringMVC 从 Map 中取出 User 对象,合并Jsp页面中表单传过来的 User 对象的对应属性,如果有名称表单项,即使没有内容(为“”时,也是值),也会合并过来并不覆盖。
3) SpringMVC 把上述修改后的对象传入目标方法的参数.
注意: 在 @ModelAttribute 修饰的方法时, 放入到 Map 时的键(user)需要和目标方法入参类型(User)的第一个字母小写的字符串一致!
真正的内部执行过程复杂的多
-
确定一个 key,若目标方法的 POJO 类型的参数没有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名的第一个字母改小写后的名字,这个名字就是Key。 若目标方法中的参数使用了 @ModelAttribute 来修饰,则 key 为 @ModelAttribute 注解的 value 属性值。
-
SpringMVC 从 Map 中取出 User 对象, 合并Jsp页面中表单传过来的 User 对象的对应属性的内部细节:实际上
第一步
是将User对象(就是从数据库中取出的)放到了一个叫implicitModel 的Map中,所以它里头有Key(前面1来确定)和Value值(User对象)。
第二步
:SpringMVC执行目标方法之前会在implicitModel找Key,在若存在匹配的, 则取出来合并表单传过来的对象的相应属性后传入目标方法。如果在implicitModel 中不存在匹配的Key 对应, 则检查当前的Controller类上是否使用 @SessionAttributes 注解修饰。若使用了该注解, 且 @SessionAttributes 注解的HttpSession 域空间中找匹配的Key, 找到了则会从 HttpSession 的域中来获取 key 所对应的 value 值,在合并表单传过来的对象的相应属性后传入到目标方法的参数中,若Controller类没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的域空间中不包含匹配的Key, 则会通过反射来创建 POJO 类型的对象, 传入为目标方法的参数。 -
最后SpringMVC 会把 Key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中。
@Controller // 不象在spring中,那样要继承HttpServlet了
public class IndexController {
@ModelAttribute
private void testModelAttribute(@RequestParam(value="id",required=false) Integer id,Model model) {
System.out.println("id=:"+id);
if(id !=null) { //Integer是一个类,不是int基本数据类型,可以用null来判断有没有内容
//去数据库取相应的记录,返回User对象
User user=new User();
//取出各字段的值,封到对象中去
user.setId(1);
user.setUsername("xiongshaowen");
user.setPassword("admin123");
System.out.println(user);
model.addAttribute("aaa",user);
}
}
@RequestMapping("/updateUser")
public String updateUser(@ModelAttribute(name="aaa") User user) {
System.out.println("要修改的用户信息:" + user);
return "success";
}
SpringMVC的视图解析过程详解
fmt标签的方法(举国际化例)
我们可以看到拿到了试图解析器解析拿到ViewName和转发的URL。返回的结果对象为JstlView而不是XML配置的InternalResourceViewResolver默认对应的InternlResourceView原因也说了:
因为JSP页面使用fmt等JSTL数据格式化标签!SpringMVC会自动使用InternlResourceView的子类 — JstlView !!!
项目中使用JSTL,SpringMVC会把视图由InternalView转换为JstlView,这没问题。关键是这里还有一个知识点:如果要使用Jstl的fmt标签支持国际化(多语言界面),就需要在SpringMVC的配置文件中配置国际化资源文件才行。
玩一玩:
1、在src目录下新建i18n.properties(和配置文件里的basename一致),内容如下:
i18n.username=Username
i18n.password=Password
2、复制i18n.properties文件为i18n_zh_CN.properties(加后缀表示中文,这个后缀可以在ie浏览器里查),内容如下:中文自动识别
i18n.username=\u7528\u6237\u540D
i18n.password=\u5BC6\u7801
3、复制i18n.properties文件为i18n_en_US.properties,内容如下:
i18n.username=Username
i18n.password=Password
4、在sprinvmvc.xml文件中添加国际化配置文件信息,上面已经配置了。
5、在index.jsp视图文件中添加fmt标签的内容如下:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<form action="${pageContext.request.contextPath}/updateUser" method="post">
<fmt:message key="i18n.username"/> <input type="text" name="username" value=""><br><br>
<fmt:message key="i18n.password"/> <input type="text" name="password" >
<input type="submit" value="提交">
</form>
测试方法:
- 初次将IE浏览器首选语言设置为中文
2)将IE浏览器的首先语言优先级调整为英文优先。
看效果!
配置直接转发的页面
<mvc:view-controller path=""/>标签的作用:
对应WEB-INF目录下面的JSP页面,我们知道是不能直接使用URL访问到,WEB-INF是被保护的目录。需要通过转发的方式,而我们一般都是在控制器中做转发映射,对应一些我们不需要其他操作的JSP页面,我们可以使用<mvc:view-controller path=“”/>来配置,这样就可以不用再控制器中再去做转发映射。
<mvc:annotation-driven />作用-------这个在以后的开发中是必加项
有<mvc:view-controller path=""/>
这个配置后,正常的需要通过Controller类的方法的Url就会访问出错,解决方法加<mvc:annotation-driven />
配置
例:在控制类中,我去掉@RequestMapping({ “/index”, “/”, " " }) 标注的方法,这样我们再访问localhost:8081/springmvcdemo/index
,会出错处,那么理代码如下:
springmvc.xml----加如下两项
<mvc:view-controller path="/index" view-name="index"/>
<mvc:annotation-driven/>
此外:<mvc:view-controller/>
除了直接转发外,还可以重定向:
<mvc:view-controller path="/" view-name="redirect:/indexController的映射方法"/>
即如果当前路径是/ 则重定向到/indexController
例:如下配置后,我们localhost:8081/springmvcdemo/index
,会执行updateUser方法
<mvc:view-controller path="/index" view-name="redirect:/updateUser"/>
<mvc:annotation-driven/>
mvc-annotation-driven详说
<mvc:annotation-driven /> 其实是一种简写形式,完全可以手动配置替代这种简写形式,简写形式可以让初学都快速应用默认配置方案,也是以后开发的必配项。
<!-- 这个简化标签相当于 如下配置-->
<!-- HandlerMapping -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping ">
</bean>
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
<!-- HandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
</bean>
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter "></bean>
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
<!-- HadnlerExceptionResolvers -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver "></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver"></bean>
<bean class="org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver"></bean>
<mvc:annotation-driven /> 会自动注册:
- RequestMappingHandlerMapping,实现这个类,它会处理@RequestMapping 注解,并将其注册到请求映射表中。
- RequestMappingHandlerAdapter,实现这个类,则是处理请求的适配器,确定调用哪个类的哪个方法,并且构造方法参数,返回值。当配置了mvc:annotation-driven后,Spring就知道了我们启用注解驱动。然后Spring通过context:component-scan标签的配置,会自动为我们将扫描到的@Component,@Controller,@Service,@Repository等注解标记的组件注册到工厂中,来处理我们的请求。
- ExceptionHandlerExceptionResolver
- 还将提供以下支持:
支持使用 ConversionService 实例对表单参数进行类型转换;
支持使用 @NumberFormat annotation、@DateTimeFormat注解完成数据类型的格式化;
支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证;
支持使用 @RequestBody 和 @ResponseBody 注解;
读写XML的支持(JAXB),读写JSON的支持(Jackson),后面,我们处理响应ajax请求时,就使用到了对json的支持。
当使用mvc:view-controller标签时一定要加入mvc:annotation-driven,不然会使requestMapping失效。
后面,当为了处理静态资源问题而加入mvc:default-servlet-handler时,也一定要加入mvc:annotation-driven,不然requestMapping同样会失效。
后面,当使用自定义类型转换器的时候需要加上mvc:annotation-driven标签。
后面,对action写JUnit单元测试时,要从spring IOC容器中取DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,来完成测试,取的时候要知道是<mvc:annotation-driven />这一句注册的这两个bean进Spring的IOC容器的。
所以这个配置很重要,一般情况下,我们在项目中都会配置起这个标签!
SpringMVC自定义视图
自定义视图作用
:自己定义视图,视图继承view类或者abstractExcelView或者abstractPdfView,将内容以Excel或者PDF格式显示,还可以玩表格啊什么的。
这里需要apache的poi的jar包:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
两种方式
第一种
首选:定个类ExcelView.java,放到web层上。
//@Component("excelView") //第二种方式用到这个注解
public class ExcelView extends AbstractXlsView{
@Override
protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception {
String fileName="test.xlsx";
response.setCharacterEncoding("utf-8");
response.setContentType("application/ms-excel");//tomcat安装目录-conf--web.xml中查找到相关type类型
response.setHeader("Content-Disposition", "inline;filename="+new String(fileName.getBytes(),"UTF-8"));
List<User> users = (List<User>)model.get("users");
//创建一张sheet表
Sheet sheet = workbook.createSheet("用户信息表");
Row headRow=sheet.createRow(0);
headRow.createCell(0).setCellValue("id");
headRow.createCell(1).setCellValue("username");
headRow.createCell(2).setCellValue("password");
CellStyle cellStyle=workbook.createCellStyle();
cellStyle.setAlignment(HorizontalAlignment.LEFT);
Font font=workbook.createFont();
font.setColor(HSSFColorPredefined.RED.getIndex());
int rowNumber=1;
for(User user:users) {
Row row=sheet.createRow(rowNumber++);
Cell cell =row.createCell(0);
cell.setCellValue(user.getId());
row.createCell(1).setCellValue(user.getUsername());
row.createCell(2).setCellValue(user.getPassword());
}
OutputStream outputStream=response.getOutputStream();
workbook.write(outputStream);
outputStream.flush();
outputStream.close();
}
}
然后:控制类中定义一个方法,返回ModelAndView视图,该视图包含表格视图。
@RequestMapping("/ev")
public ModelAndView excelViewTest() {
Map<String,Object> map=new HashMap<>();
User user1=new User();
User user2=new User();
user1.setId(11);
user1.setUsername("zhangsan");
user1.setPassword("zhang");
user2.setId(22);
user2.setUsername("lisi");
user2.setPassword("li");
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
map.put("users", users);
ModelAndView mv = new ModelAndView(new ExcelView(),map);
return mv;
}
第二种方式
springmvc.xml,添加解析器,视IOC容器来解析ExcelView定义的视图,直接返回字符串形式名即可
<!--设置一个视图解析器,解析表格等文档性质视图,这里设置order优先级,随便设一个数都比上面的解析器优先级高,因为默认值是整数的最大值 -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="100"></property>
</bean>
首先:ExcelView.java类上要加注解@Component(“excelView”)
然后:控制类中,返回字符串形式的视图名,可由视图解析器来解析之。
@RequestMapping("/ev1")
public String excelViewTest2(Model model) {
User user1=new User();
User user2=new User();
user1.setId(11);
user1.setUsername("zhangsan");
user1.setPassword("zhang");
user2.setId(22);
user2.setUsername("lisi");
user2.setPassword("li");
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
model.addAttribute("users", users);
return "excelView"; //@Component("excelView")标注了在web层上的类ExcelView.java上
}
Controller类里的方法实现重定向和转发
这里主要就是用两个前缀:
redirect: 表示重定向!(在项目中常用)
forword:表示转发!(在项目中用的很少!)
springmvcREST风格的四种请求方式
什么是REST
(WebService两种方式,一是SOAP方式,二是REST方式。)
REST (REpresentation State Transfer) 描述了一个架构样式的网络系统,比如 web 应用程序。它首次出现在 2000 年 Roy Fielding 的博士论文中,他是 HTTP 规范的主要编写者之一。REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。
Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在请求之间的任何时间点重启,客户端不会得到通知。此外,无状态请求可以由任何可用服务器回答,这十分适合云计算之类的环境。客户端可以缓存数据以改进性能。
在服务器端,应用程序状态和功能可以分为各种资源。资源是一个有趣的概念实体,它向客户端公开。资源的例子有:应用程序对象、数据库记录、算法等等。每个资源都使用 URI (Universal Resource Identifier) 得到一个惟一的地址。所有资源都共享统一的界面,以便在客户端和服务器之间传输状态。使用的是标准的 HTTP 方法,比如 GET、PUT、POST 和 DELETE,还可能包括 HEADER 和 OPTIONS。Hypermedia 是应用程序状态的引擎,资源表示通过超链接互联。
另一个重要的 REST 原则是分层系统,这表示组件无法了解它与之交互的中间层以外的组件。通过将系统知识限制在单个层,可以限制整个系统的复杂性,促进了底层的独立性。
当 REST 架构的约束条件作为一个整体应用时,将生成一个可以扩展到大量客户端的应用程序。它还降低了客户端和服务器之间的交互延迟。统一界面简化了整个系统架构,改进了子系统之间交互的可见性。REST 简化了客户端和服务器的实现。
RESTful的实现:RESTful Web 服务与 RPC 样式的 Web 服务
了解了什么是REST,我们再看看RESTful的实现。最近,使用 RPC 样式架构构建的基于 SOAP 的 Web 服务成为实现 SOAP 最常用的方法。RPC 样式的 Web 服务客户端将一个装满数据的信封(包括方法和参数信息)通过 HTTP 发送到服务器。服务器打开信封并使用传入参数执行指定的方法。方法的结果打包到一个信封并作为响应发回客户端。客户端收到响应并打开信封。每个对象都有自己独特的方法以及仅公开一个 URI 的 RPC 样式 Web 服务,URI 表示单个端点。它忽略 HTTP 的大部分特性且仅支持 POST 方法。
由于轻量级以及通过 HTTP 直接传输数据的特性,Web 服务的 RESTful 方法已经成为最常见的替代方法。可以使用各种语言(比如 Java 程序、Perl、Ruby、Python、PHP 和 Javascript[包括 Ajax])实现客户端。RESTful Web 服务通常可以通过自动客户端或代表用户的应用程序访问。但是,这种服务的简便性让用户能够与之直接交互,使用它们的 Web 浏览器构建一个 GET URL 并读取返回的内容。
在 REST 样式的 Web 服务中,每个资源都有一个地址。资源本身都是方法调用的目标,方法列表对所有资源都是一样的。这些方法都是标准方法,包括 HTTP GET、POST、PUT、DELETE,还可能包括 HEADER 和 OPTIONS。
Springmvc四种REST风格请求
HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
它们分别对应四种基本操作:
1、GET -------获取资源
2、POST ---- 新建资源
3、PUT-------- 更新资源
4、DELETE—删除资源
REST:即 Representational State Transfer。(资源)表现层状态转化。
是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便, 所以正得到越来越多网站的采用。
我们可以通过rest风格占位符方式,利用@PathVariable注解将占位符的值赋给调用方法参数,实现结果:
/某路径/1 HTTP GET : 得到 id = 1 的 一条数据
/某路径/1 HTTP DELETE: 删除 id = 1的 一条数据
/某路径/1 HTTP PUT: 更新id = 1的 一条数据
/某路径 HTTP POST: 新增一条数据
实现方式(REST风格四种请求方式的调用):
一。我们通过@RequestMapping映射请求中的method参数实现四种请求方式的调用,以下为示例代码:
//REST风格请求,GET,POST,PUT,DELETE
@RequestMapping(value="/testGet",method=RequestMethod.GET)
public String testGet() {
System.out.println("GET");
return "redirect:/index";
}
@RequestMapping(value="/testPost",method=RequestMethod.POST)
public String testPost() {
System.out.println("POST");
return "redirect:/index";
}
@RequestMapping(value="/testPut",method=RequestMethod.PUT)
public String testPut() {
System.out.println("PUT");
return "redirect:/index";
}
@RequestMapping(value="/testDel",method=RequestMethod.DELETE)
public String testDel() {
System.out.println("DELETE");
return "redirect:/index";
}
二。将POST请求转化为put请求和delele请求,没有直接的put,delete请求
1.在web.xml文件中配置HiddenHttpMethodFilter过滤器:
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.在表单域中需要携带一个name值为_method,value值为put或者delete的参数,如下所示
<form action="${pageContext.request.contextPath}/testPut" method="post">
<input type="hidden" name="_method" value="put">
<input type="text" name="username" value=""><br><br>
<input type="text" name="password" >
<input type="submit" value="提交">
</form>
最后在Controller层调用即可。根据@RequestMapping的value值以及携带的参数、请求方式查找匹配函数。
测试:GET,POST不管,我只测试PUT,DELETE请求
先输入localhost:8081/springmvcdemo回车,然后进入index.jsp视图,输入:姓名,密码提交,控制台显示put字样,说明测试成功。
案例实现RESTful风格获取所有用户
spring,springmvc,hibernate整合参考
spring(springmvc,springboot)-hibernate(jpa,mybtis)-jsp整合
这里我新建一个项目sshweb,该项目是spring,springmvc,hibernate的整合后,的综合开发。
/user/1 HTTP GET : 得到 id = 1 的 一条数据
/users HTTP GET : 得到所有数据
/user/1 HTTP DELETE: 删除 id = 1的 一条数据
/user/1 HTTP PUT: 更新id = 1的 一条数据
/user HTTP POST: 新增一条数据
1、现在的浏览器只支持GET和POST请求,不支持PUT、DELETE请求,所以要在web.xml上配置拦截器把POST请求转PUT、DELETE请求:
<!-- 将POST请求转PUT DELETE的拦截器 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.springmvc配置文件中,配视图解析器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<mvc:annotation-driven></mvc:annotation-driven> <!-- 注解驱动,这个很重要,在mvc开发中是必要的设置 -->
<!-- spring,springmvc它们扫描的东西不能重合,springmvc只扫描控制类,它要映射到视图中去
而spring正好排除这两个项,初始化所有beans -->
<context:component-scan base-package="cn.ybzy.sshweb" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<!-- 视图解析器配置 bean-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
3、把Model数据模型编写好,再把基本的BaseDaoInterface接口和BaseDao类的最最基本的方法编写好
User,Department-用户部门多对一关系
public class User {
private int id;
private String username;
private String password;
private Department dpt; //部门
//get,set,toString,无参构造方法
}
public class Department {
private int id;
private String dptName;
//get,set,toString,无参构造方法
}
两个模型类的hibernate的映射到数据库中的文件,启动服务器后,自动在spring5数据库生成两个多对一关系的表(t_users,t_departments)
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.ybzy.sshweb.model.User" table="t_users">
<id name="id" type="int">
<column name="id" />
<generator class="native" />
</id>
<property name="username" type="java.lang.String">
<column name="username" />
</property>
<property name="password" type="java.lang.String">
<column name="password" />
</property>
<many-to-one name="dpt" class="cn.ybzy.sshweb.model.Department" fetch="join">
<column name="dpt_id" />
</many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="cn.ybzy.sshweb.model.Department" table="t_departments">
<id name="id" type="int">
<column name="id" />
<generator class="native" />
</id>
<property name="dptName" type="java.lang.String">
<column name="dpt_name" />
</property>
</class>
</hibernate-mapping>
Dao层
BaseDaoInterface.java,BaseDao.java,其它模型类的dao层类通过继承它俩,大大节少代码量。
public interface BaseDaoInterface<T> {
/**
* 新增数据库中的对应的数据表中
*/
public void add(T t);
/**
* 通过id值来删除对应的记录
* @param id
*/
public void delete(int id);
/**
* 传入一个User对象,通过对象的id来修改
*/
public void update(T t);
/**
* 通过id值来获取一条记录
* @param id
* @return
*/
public T getOne(int id);
}
public class BaseDao<T> {
public Class<T> getTClass(){
@SuppressWarnings("unchecked")
Class<T> tClass =(Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
return tClass;
}
@Autowired
private SessionFactory sessionFactory; //spring.xml中配了SessionFactory的bean
public Session getSession() {
return sessionFactory.getCurrentSession();
}
public void add(T t) {
getSession().save(t);
}
public void delete(int id) {
getSession().delete(getOne(id));
}
public void update(T t) {
getSession().update(t);
}
public T getOne(int id) {
return getSession().load(getTClass(),id);
}
}
UserDao,UserDaoImpl,DepartmentDao,DepartmentDaoImpl
-----------------------------------------------------------UserDao.java-----------------------------------
public interface UserDao extends BaseDaoInterface<User> {
/**
* 新增用户到数据库中的对应的数据表中
*/
//public void add(User user);
/**
* 通过id值来删除对应的记录
* @param id
*/
//public void delete(int id);
/**
* 传入一个User对象,通过对象的id来修改
*/
//public void update(User user);
/**
* 通过id值来获取一条记录
* @param id
* @return
*/
//public User getOne(int id);
/**
* 获取所有记录集合
* @return
*/
public List<User> getAll();
}
-----------------------------------------------------------UserDaoImpl.java-----------------------------------
@Repository("userDao") //注解式注册该类为IOC中的bean,应用者可用注解@Autowride自动装备
public class UserDaoImpl extends BaseDao<User> implements UserDao{
/*@Autowired
private SessionFactory sessionFactory; //spring.xml中配了SessionFactory的bean
public Session getSession() {
return sessionFactory.getCurrentSession();
}
@Override
public void add(User user) {
getSession().save(user);
}
@Override
public void delete(int id) {
getSession().delete(getOne(id));
}
@Override
public void update(User user) {
getSession().update(user);
}
@Override
public User getOne(int id) {
return getSession().load(User.class,id);
}
*/
//这个方法,要我们写一些具体代码量,所以做到BaseDao里是很麻烦的,所以保留这个方法
@SuppressWarnings("unchecked")
@Override
public List<User> getAll() {
String hql="from User";
return getSession().createQuery(hql).list();
}
}
-----------------------------------------------------------DepartmentDao.java-----------------------------------
public interface DepartmentDao extends BaseDaoInterface<Department> {
/**
* 获取所有记录集合
* @return
*/
public List<Department> getAll();
}
-----------------------------------------------------------DepartmentDaoImpl.java-----------------------------------
@Repository("departmentDao") //注解式注册该类为IOC中的bean,应用者可用注解@Autowride自动装备
public class DepartmentDaoImpl extends BaseDao<Department> implements DepartmentDao{
//这个方法,要我们写一些具体代码量,所以做到BaseDao里是很麻烦的,所以保留这个方法
@SuppressWarnings("unchecked")
@Override
public List<Department> getAll() {
String hql="from Department";
Session session=getSession();
Query<Department> query = session.createQuery(hql);
return query.list();
}
}
Service层
BaseService.java,BaseServcieInterface.java
public class BaseService<T> implements BaseServiceInterface<T> {
@Autowired
public BaseDao<T> baseDao;//因UserDao,DepartmentDao,已注入到IOC中,所以泛型类自动到了IOC中。
@Override
public void add(T t) {
baseDao.add(t);
}
@Override
public void delete(int id) {
baseDao.delete(id);
}
@Override
public void update(T t) {
baseDao.update(t);
}
@Override
public T getOne(int id) {
return baseDao.getOne(id);
}
}
public interface BaseServiceInterface<T> {
public void add(T t);
public void delete(int id);
public void update(T t); //通过传来的对象的id修改对象,不能直接修改id
public T getOne(int id);
}
UserService.java,UserServiceImpl.java,DepartmentService.java,DepartmentServiceImpl.java
-----------------------------------------------------------UserService.java-----------------------------------
public interface UserService extends BaseServiceInterface<User> {
//这里我们不用显示写出BaseServiceInterface接口中的方法,当类很多时,十分方便
public List<User> getAllUsers();
}
-----------------------------------------------------------UserServiceImpl.java-----------------------------------
@Service("userService")
public class UserServiceImpl extends BaseService<User> implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> getAllUsers() {
return userDao.getAll();
}
}
-----------------------------------------------------------DepartmentService.java-----------------------------------
public interface DepartmentService extends BaseServiceInterface<Department>{
public List<Department> getAllDepartment();
}
-----------------------------------------------------------DepartmentServiceImpl.java-----------------------------------
@Service("departmentService")
public class DepartmentServiceImpl extends BaseService<Department> implements DepartmentService {
@Autowired
private DepartmentDao departmentDao;
@Override
public List<Department> getAllDepartment() {
return departmentDao.getAll();
}
}
Controller层中,显示所有用户信息到users.jsp中。
@Controller
public class UserController {
// public UserController() {
// System.out.println("控制类的构造方法,看看启动时打印情况");
// }
@Autowired
private UserService userService;
/**
* rest风格
* url: xxxx/users 请求方法为get,获取所有用户
* @return
*/
@RequestMapping(value="/users",method=RequestMethod.GET)
public String getAllUsers(Model model) {
List<User> users = userService.getAllUsers();
model.addAttribute("users",users);
return "users"; //返回到users.jsp页面中
}
}
测试:localhost:8081/sshweb/users,回车,会发现出现懒加载异常,这是因为关联查询部门名称出现的,
最后环节发现LazyInitializationException:懒加载异常!这个异常,我们在Hibernate重点反复分析过原因,原因在Hibernate中为了提升数据库访问效率,我们对依赖的对象实行延时加载的策略,在这个期间session被关闭,在去访问数据,就会报这个异常!Web项目中解决方法就是要延缓session的关闭!
具体的做法:可以在web.xml中配置OpenSessionInViewFilter这里拦截器:
解决办法:web.xml中加一个过滤器
<!-- 解决查询用户信息的关联部门信息懒加载异常的过滤器 -->
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>