MVC架构模式简介
1、MVC是Model、View和Controller的缩写,分别代表 web应用程序中的3种职责。
- 模型:用于存储数据以及处理用户请求的业务逻辑。
- 视图:向控制器提交数据,显示模型中的数据。
- 控制器:根据视图提出的请求判断将请求和数据交给哪个模型处理,将处理后的有关结果交给哪个视图更新显示。
2、基于servlet的MVC模式
- 模型:一个或多个JavaBean对象,用于存储数据(实体模型,由JavaBean类创建)和处理业务逻辑(业务模型,由一般的Java类创建)。
- 视图:一个或多个JSP页面,向控制器提交数据和为模型提供数据显示,JSP页面主要使用HTML标记和JavaBean标记来显示数据。
- 控制器:一个或多个Servlet对象,根据视图提交的请求进行控制,即将请求转发给处理业务逻辑的JavaBean,并将处理结果存放到实体模型JavaBean中,输出给视图显示。
SpringMVC简介
1、什么是SpringMVC
SpringMVC是一个基于MVC模式的Web框架,是Spring框架的一个模块。它以SpringloC容器为基础,并利用容器的特性来简化它的配置,所以SpringMVC和Spring可直接整合使用。SpringMVC对MVC流程进行了封装,屏蔽掉很多底层代码,让开发者可以更加轻松快捷的完成基于MVC模式的Web开发。
2、SpringMVC中重要的组件
- DispatcherServlet:前端控制器,接受所有请求
- HandlerMapping:处理器映射器,根据配置的映射规则,找到对应的处理器
- HandlerAdapter:处理器适配器,执行处理器中处理请求的方法
- ViewResolver:视图解析器,定位视图。
3、SpringMVC工作流程
工作流程:
- 客户端请求提交到DispatcherServlet
- 由DispatcherServlet 控制器通过HandlerMapping,找到处理请求的Controller
- DispatcherServlet将请求提交到Controller
- Controller调用HandlerAdapter执行处理请求的方法
- 业务逻辑处理后返回ModelAndView
- DispatcherServlet通过ViewResolver视图解析器,找到ModelAndView指定的视图
- 视图负责渲染并将结果显示到客户端
SpringMVC的基本使用
1、jar包依赖
Spring核心容器模块
- spring-beans-5.2.7.RELEASE.jar
- spring-context-5.2.7.RELEASE.jar
- spring-core-5.2.7.RELEASE.jar
- spring-expression-5.2.7.RELEASE.jar
Commons-loggin日志
Spring AOP模块
- spring-aop-5.2.7.RELEASE.jar
Spring Web模块
- spring-web-5.2.7.RELEASE.jar
Spring Web MVC模块
- spring-webmvc-5.2.7.RELEASE.jar
Servlet
2、搭建环境
步骤:
-
创建web项目 -
添加相关jar包 -
在src目录下添加springmvc.xml配置文件 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
-
添加配置文件中beans的约束文件
3、配置前端控制器
修改web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<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-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
4、创建Controller
创建Controller,需要实现Controller接口
package com.bjsxt.web.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DemoController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("msg","hello spring mvc");
return modelAndView;
}
}
在index.jsp中通过EL表达式获取响应内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
${msg}
</body>
</html>
在springmvc.xml中配置Controller
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="/demo" class="com.bjsxt.web.controller.DemoController"></bean>
</beans>
SpringMVC基于注解开发
在SpringMVC的基本使用中我们是以传统方式创建的控制器,它需要实现 Controller接口。传统风格的控制器不仅需要在配置文件中配置映射,而且只能编写一个处理方法,不够灵活。
使用基于注解的控制器具有以下两个优点:
- 在基于注解的控制器类中可以编写多个处理方法,进而可以处理多个请求,这就允许将相关的操作编写在同一个控制器类中,从而减少控制器类的数量,方便以后的维护。
- 基于注解的控制器不需要在配置文件中部署映射,仅需要使用@RequestMapping 注解就可以将一个URI绑定到类或方法上。
1、注解介绍
2、搭建环境
和基本使用中完全相同
3、配置前端控制器
和基本使用中完全相同
4、创建Controller
基于注解创建Controller
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/con")
public class AnnController {
@RequestMapping("/ann")
public ModelAndView abc(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("msg","spring mvc ann");
return modelAndView;
}
}
在index.jsp中通过EL表达式获取响应内容
修改spring-mvc-ann.xml文件,添加context命名空间,开启注解扫描
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="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.xsd">
<context:component-scan base-package="com.bjsxt.web.controller"/>
</beans>
【解决的两个异常】
- XML文件错误:web.xml中漏写springmvc配置文件的名称路径
- JDK17无法解析某个class文件:换回JDK1.8
5、配置注解驱动
在基于注解方式开发控制器时,需要添加<mvc:annotation-driven> 标签,它是启用MVC注解的钥匙。如果没有使用这个标签,而仅仅是使用<context:component-scan/> 标签扫描并注册了相关的控制器,那么仅是@Controller @RequestMapping基本功能的注解可以使用除此以外的相关的注解并不能使用。
<mvc:annotation-driven/> 的作用是提供扩展功能的。
它的处理类AnnotationDrivenBeanDefinitionParser会注册很多基于注解开发时所用到的Bean对象到容器中。其中包含RequestMappingHandlerMapping、RequestMappingHandlerAdapter与ExceptionHandlerExceptionResolver 三个bean。
-
在spring-mvc-ann.xml中添加mvc命名空间 xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="...
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd"
-
配置注解驱动 <mvc:annotation-driven/>
获取请求参数
在Servlet中我们通过request.getParameter(name)方法获取请求参数。该方式存在两个问题:
- 请求参数较多时会出现代码冗余的显现
- 与容器紧耦合
在SpringMVC中可以使用HttpServletRequest对象获取请求数据,同时还提供了参数注入的方式用于获取请求数据。
SpringMVC参数注入的优点
- 简化参数接收形式(不需要调用任何方法。需要什么参数,就在控制器方法中提供什么参数)
- 参数类型不需要自己转换了,如果类型不符会抛出400异常
- 可将参数自动封装为对象
- 如果没有该参数对应的数据,可为该参数指定默认值
1、通过HttpServletRequest对象获取请求数据
@RequestMapping("/getData")
public ModelAndView getParameter(HttpServletRequest request){
ModelAndView modelAndView = new ModelAndView();
String username = request.getParameter("name");
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("username",username);
return modelAndView;
}
在index.jsp中通过EL表达式获取响应内容
2、通过参数注入获取请求数据
2.1 注入多参数
2.1.1 不使用注解注入多参数
@RequestMapping("/addUsers")
public ModelAndView addUsers(String user_name,int user_age){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("name",user_name);
modelAndView.addObject("age",user_age);
return modelAndView;
}
在index.jsp中通过EL表达式获取响应内容
2.1.2 @RequestParam注解
RequestParam:将请求参数绑定到控制器的方法参数上。
- value:参数名
- required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错
- defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false;如果没有传该参数,就使用默认值
@RequestMapping("/addUsers2")
public ModelAndView addUsers2(@RequestParam(value = "name") String user_name,@RequestParam("age") int user_age){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("name",user_name);
modelAndView.addObject("age",user_age);
return modelAndView;
}
2.3 注入集合参数
在SpringMVC请求参数注入中,如果有多请求参数的name相同,那么可以使用String或List集合来接收请求参数。如果使用的 List类型需要在该参数前添加@RequestParam注解,String[]则不需要。
创建一个addUsers.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="addUsers3">
用户名:<input type="text" name="username"><br>
爱好:<br>
体育:<input type="checkbox" name="hobby" value="sport">
音乐:<input type="checkbox" name="hobby" value="music">
艺术:<input type="checkbox" name="hobby" value="art"><br>
<input type="submit" value="ok">
</form>
</body>
</html>
在Controller类中添加处理请求的方法
@RequestMapping("/addUsers3")
public ModelAndView addUsers3(String username,String[] hobby){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("name",username);
String temp = "";
for(String str:hobby){
temp += str+" ";
}
modelAndView.addObject("hobby",temp);
return modelAndView;
}
或者
@RequestMapping("/addUsers3")
public ModelAndView addUsers3(String username,@RequestParam(value="hobby") List<String> hobby){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("name",username);
String temp = "";
for(String str:hobby){
temp += str+" ";
}
modelAndView.addObject("hobby",temp);
return modelAndView;
}
在index.jsp中通过EL表达式获取响应内容
2.4 注入对象参数
在SpringMVc的请求参数注入中,可以使用注入POJO方式来接收请求参数。
要求:请求参数的name必须与POJO的属性名相同。
2.4.1 注入单个对象
创建和表单提交信息相应的pojo类
【注意】表单中单个爱好的属性名是hobby,尽管逻辑上Users类中对应的属性应该是hobbies,但是这么写会报错,必须按照上面的要求:name和属性名一致才行。猜测原因可能是注入对象的方式无法使用@RequestParam指定参数名?
package com.bjsxt.poji;
import java.util.List;
public class Users {
private String username;
private List<String> hobby;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
}
在Controller类中添加处理请求的方法
@RequestMapping("/addUsers4")
public ModelAndView addUsers4(Users user){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("name",user.getUsername());
String temp = "";
for(String str: user.getHobby()){
temp += str+" ";
}
modelAndView.addObject("hobby",temp);
return modelAndView;
}
在index.jsp中通过EL表达式获取响应内容
2.4.2 注入关联对象
SpringMVC可以根据对象的关联关系实现请求参数的注入。
创建一个新的实体类
package com.bjsxt.poji;
public class Address {
private String phonenumber;
private String postcode;
public String getPhonenumber() {
return phonenumber;
}
public void setPhonenumber(String phonenumber) {
this.phonenumber = phonenumber;
}
public String getPostcode() {
return postcode;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
@Override
public String toString() {
return "Address{" +
"phonenumber='" + phonenumber + '\'' +
", postcode='" + postcode + '\'' +
'}';
}
}
修改Users类,添加address属性,添加get/set方法
package com.bjsxt.poji;
import java.util.List;
public class Users {
private String username;
private List<String> hobby;
private Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "Users{" +
"username='" + username + '\'' +
", hobby=" + hobby +
'}';
}
}
创建addUsers2.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="addUsers5">
用户名:<input type="text" name="username"><br>
爱好:<br>
体育<input type="checkbox" name="hobby" value="sport">
音乐<input type="checkbox" name="hobby" value="music">
艺术<input type="checkbox" name="hobby" value="art"><br>
地址:<br>
电话号码:<input type="text" name="address.phonenumber">
邮编:<input type="text" name="address.postcode"><br>
<input type="submit" value="ok">
</form>
</body>
</html>
在Controller中添加处理请求的方法
@RequestMapping("/addUsers5")
public ModelAndView addUsers5(Users user){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("user",user.toString());
modelAndView.addObject("address",user.getAddress().toString());
return modelAndView;
}
在index.jsp中通过EL表达式获取响应内容:user 、address
2.4.3 向List中注入对象
在Users类中添加一个泛型为Address的集合类型的属性
private List<Address> addressList;
public List<Address> getAddressList() {
return addressList;
}
public void setAddressList(List<Address> addressList) {
this.addressList = addressList;
}
新建addUsers3.jsp
addressList[0].phonenumber,先集合、再到元素address对象、最后到其属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="addUsers6">
用户名:<input type="text" name="username"><br>
爱好:<br>
体育<input type="checkbox" name="hobby" value="sport">
音乐<input type="checkbox" name="hobby" value="music">
艺术<input type="checkbox" name="hobby" value="art"><br>
地址:<br>
电话号码:<input type="text" name="addressList[0].phonenumber">
邮编:<input type="text" name="addressList[0].postcode"><br>
电话号码:<input type="text" name="addressList[1].phonenumber">
邮编:<input type="text" name="addressList[1].postcode"><br>
<input type="submit" value="ok">
</form>
</body>
</html>
添加处理请求的方法
并没有遍历AddressList集合!是不是可以说会自动跨过集合这层直接读取元素?
@RequestMapping("/addUsers6")
public ModelAndView addUsers6(Users user){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("user",user.toString());
modelAndView.addObject("address",user.getAddressList());
return modelAndView;
}
2.4.4 向Map中注入对象
在Users类中添加一个泛型为Address的Map类型的属性,生成get/set方法
private Map<String,Address> addressMap;
public Map<String, Address> getAddressMap() {
return addressMap;
}
public void setAddressMap(Map<String, Address> addressMap) {
this.addressMap = addressMap;
}
新建addUsers4.jsp
addressMap[‘one’].phonenumber,Map的key、key对应的value就是Address对象、再到其属性
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="addUsers7">
用户名:<input type="text" name="username"><br>
爱好:<br>
体育<input type="checkbox" name="hobby" value="sport">
音乐<input type="checkbox" name="hobby" value="music">
艺术<input type="checkbox" name="hobby" value="art"><br>
地址:<br>
电话号码:<input type="text" name="addressMap['one'].phonenumber">
邮编:<input type="text" name="addressMap['one'].postcode"><br>
电话号码:<input type="text" name="addressMap['two'].phonenumber">
邮编:<input type="text" name="addressMap['two'].postcode"><br>
<input type="submit" value="ok">
</form>
</body>
</html>
添加处理请求的方法
对于Map,先获取EntrySet对象
@RequestMapping("/addUsers7")
public ModelAndView addUsers7(Users user){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/index.jsp");
modelAndView.addObject("user",user.toString());
Set<Map.Entry<String, Address>> set = user.getAddressMap().entrySet();
modelAndView.addObject("address",set);
return modelAndView;
}
【
3、字符编码过滤器
在请求参数中如果含有中文,会出现乱码现象。
3.1 Get请求含有中文乱码解决方案
可通过修改Tomcat的server.xml配置文件,解决中文乱码。
<Connector port="8888" protocol="HTTP/1.1"
connectionTimeout="2000n"
redirectPort="8443"lURIEncoding="utf-8"/>
3.2 Post请求含有中文乱码解决方案
org.springframework.web.filter.CharacterEncodingFilter提供了过滤器,不用写,直接配置即可。
<filter>
<filter-name>encFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
指定处理请求类型
1、@RequestMapping
在@RequestMapping 注解中如果并未指定处理请求类型,那么含有该注解的方法既可以处理GET类型请求也可以处理POST类型的请求。如果需要指定只能处理某种类型的请求,可以通过method属性指定请求类型。
@RequestMapping(value = "/addUsers7",method = RequestMethod.GET)
2、@GetMapping
表示只能处理get方式请求。
@GetMapping(name = "addUsers7")
3、@PostMapping
表示只能处理post方式请求。
@PostMapping(name = "addUsers7")
SpringMVC处理响应
1、配置视图解析器
在SpringMVC中提供了13个视图解析器,用于支持不同的视图技术。视图解析器最大的特点是可以将控制器中处理请求的逻辑和视图中渲染实现解耦。InternalResourceViewResolver 是SpringMVC中默认的视图解析器,用来解析JSP视图。能将视图名映射为JSP文件。
1.1 简单使用
修改spring-mvc-ann.xml文件
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
创建新的Controller
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/page")
public class PageController {
@RequestMapping("/login")
public String toLogin(){
return "login";
}
}
1.2 视图解析器的作用:
- 预先设置要跳转的页面前缀和后缀
- Controller中处理请求的方法返回值类型不再是ModelAndView,变成了String
- 可以很方便地指定要跳转的页面
返回值不再是ModelAndView,响应的内容如何回送到浏览器?
在web目录下的jsp文件可以在地址栏直接访问;但实际环境中jsp文件应放在WEB-INF目录下,不允许通过地址栏直接访问;这时候,我们可以通过视图解析器设置前缀和后缀,以处理请求的方法跳转jsp页面。比如,通过上面的设置访问/page/login跳转到WEB-INF/jsp/login.jsp页面。
2、Spring MVC作用域传值
作用域:“数据共享的范围”,也就是说数据能够在多大的范围内有效。
2.1 Request作用域传值
2.1.1 使用原生的HttpServletRequest
在处理请求的方法中传入HttpServletRequest对象,调用setAttribute方法设置信息,在login.jsp通过EL表达式获取。
请求访问/page/login,跳转login.jsp,通过EL表达式获取该次请求中传递的参数信息。
HttpServletRequest对象是容器提供的,容器与控制器之间是紧耦合。
@RequestMapping("/login")
public String toLogin(HttpServletRequest request){
request.setAttribute("msg","001");
return "login";
}
2.1.2 使用Map集合
SpringMVC会为Map接口注入BindingAwareModelMap对象。该对象是由Spring提供的一个实现了Map接口的对象。SpringMVC会把该对象中的数据放入到HttpServletRequest对象中,其目的是为了解除控制器与容器的耦合。
@RequestMapping("/login")
public String toLogin(HttpServletRequest request, Map<String,String> map){
map.put("msg","hello");
return "login";
}
【注意】
- 这种方式作用域仍然是一次请求中,因为map是放在HttpServletRequest对象中的;
- 通过EL表达式取msg时,会自动定位map对象从中取key为msg对应的值,不需要我们指定具体是哪个map,是不是可以认为它本身就具有一层拆封的能力?
2.1.3 使用Model接口
在SpringMVC中提供了一个Model类型的接口,该接口定义了传递数据的基本行为。如果在处理请求的方法中指定了Model类型的参数,那么SpringMVC会注入一个BindingAwareModelMap对象,并通过该对象把数据放入到HttpServletRequest对象中。
@RequestMapping("/login")
public String toLogin(HttpServletRequest request, Map<String,String> map, Model model){
model.addAttribute("msg","001");
return "login";
}
2.2 Session作用域传值
Session 作用域表示在当前会话中有效。在SpringMVc中对于Session作用域传值,只能使用HttpSession对象来实现。对于HttpSession对象的获取方式有两种:
- 通过参数注入方式获取 HttpSession对象
- 通过注入HttpServletRequest对象,并通过该对象获取HttpSession
@RequestMapping("/login")
public String toLogin(HttpServletRequest request, HttpSession session){
session.setAttribute("msg","001");
return "login";
}
2.3 Application作用域传值
Application作用域表示在整个应用范围都有效。在SpringMvc中对于Application作用域传值,只能使用servletContext对象来实现。但是对于该对象的获取方式不能直接向方法中注入。
- 通过HttpServletRequest对象获取
- 通过HttpSession对象获取
@RequestMapping("/login")
public String toLogin(HttpServletRequest request, HttpSession session){
ServletContext servletContext = session.getServletContext();
servletContext.setAttribute("msg","002");
return "login";
}
3、Spring MVC的响应方式
3.1 请求转发
3.1.1 使用Servlet API
在SpringMVC中可以使用HttpServletRequest对象实现请求转发。
@RequestMapping("/login2")
public void showLogin(HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setAttribute("msg","and one");
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
}
3.1.2 使用forward关键字
如果使用forward 关键字实现请求转发跳转时,是通过SpringMVC的DispatcherServlet组件来完成的。不会执行视图解析器,所以需要指定请求跳转页面的完整URI。
@RequestMapping("/login3")
public String showLogin2(Model model){
model.addAttribute("msg","003");
return "forward:/WEB-INF/jsp/login.jsp";
}
3.1.3 使用视图解析器
在SpringMVC的视图解析器中使用的是请求转发方式来实现页面跳转。可以在配置视图解析器时指定视图的前置与后缀。
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
@RequestMapping("/login4")
public String showLogin3(Model model){
model.addAttribute("msg","003");
return "login";
}
3.2 重定向
3.2.1 使用Redirect关键字
在SpringMVC需要使用redirect关键字实现重定向的跳转。在重定向跳转中是不经过视图解析器的。
@RequestMapping("/redirectLogin")
public String goRedirect(){
return "redirect:/page/login3";
}
过程:
访问/page/redirectLogin-----------重定向----------->/page/login3-------------请求转发------------>/WEB-INF/jsp/login.jsp
文件上传与下载
1、文件上传
在SpringMVC中提供了用于处理文件上传的组件CommonsMultipartResolver(多部件解析器)。可以通过该组件很方便的实现文件上传。该组件的运行需要依赖于Apache的commons-fileupload 与commons-io包。
1.1 搭建环境
一般步骤:
-
创建普通项目 -
添加jar包 -
添加web框架 -
配置Tomcat -
创建controller包 -
添加Spring配置文件 <?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="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.xsd http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.bjsxt.web.controller"/>
<mvc:annotation-driven/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
<property name="maxUploadSize" value="10485760"/>
<property name="maxUploadSizePerFile" value="1024"/>
</bean>
</beans>
-
修改web.xml配置前端控制器 <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
1.2 单文件上传
1.2.1 修改项目的访问路径
1.2.2 创建文件上传页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--请求方式必须是post--%>
<form action="/file/singleFile" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"><br>
文件上传:<input type="file" name="file"><br>
<input type="submit" value="ok">
</form>
</body>
</html>
1.2.3 创建成功页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<p>
OK...
</p>
</body>
</html>
1.2.3 创建页面跳转的控制器
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/page")
public class PageController {
@RequestMapping("/singleFile")
public String toSingleFile(){
return "singleFile";
}
@RequestMapping("/ok")
public String toOK(){
return "ok";
}
}
1.2.4 创建实现文件上传的控制器
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@Controller
@RequestMapping("/file")
public class FileUploadController {
@RequestMapping("/singleFile")
public String singleFileUpload(@RequestParam("file") MultipartFile files, String username, HttpServletRequest request){
String filename = UUID.randomUUID().toString()+files.getOriginalFilename().substring(files.getOriginalFilename().lastIndexOf("."));
String realPath = request.getServletContext().getRealPath("/fileUpload");
File file = new File(realPath,filename);
try {
files.transferTo(file);
} catch (IOException e) {
e.printStackTrace();
}
return "redirect:/page/ok";
}
}
1.3 单文件上传总结
- 实现文件上传的表单,提交方式为post,编码格式enctype=multipart/form-data
- 控制器分离,一个用于实现文件上传,一个用于跳转jsp页面
- 实现文件上传的控制器中,FileUploadController类和singleFileUpload()方法都映射应当和表单请求的路径对应
- singleFileUpload()方法中的参数名files,最好和表单中上传文件的input标签中name属性的值一致,否则用@RequestParam指定
- singleFileUpload()方法的参数MultipartFile files,包含文件名、大小等信息
- singleFileUpload()方法需要HttpServletRequest对象来获取ServletContext对象,获取绝对路径
- 为避免文件名相似或重复,使用UUID工具类的randomUUID()方法和subString()方法生成一个不易重复的文件名+扩展名
- files.transferTo()自动读取和写入文件
- 成功上传的文件位于 /out/artifacts/fileupload_war_exploded/fileUpload目录,而不是/WEB-INF/fileUpload
过程图示
1.4 实现多文件上传
1.4.1 创建多文件上传页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/file/multiFile" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"><br>
<%--文件上传name属性的值必须一样--%>
文件上传1:<input type="file" name="file"><br>
文件上传2:<input type="file" name="file"><br>
<input type="submit" value="ok">
</form>
</body>
</html>
1.4.2 添加页面跳转方法
@RequestMapping("/multiFile")
public String toMultiFile(){
return "multiFile";
}
1.4.3 创建实现多文件上传的方法
@RequestMapping("/multiFile")
public String multiFileUpload(MultipartFile file[],String username, HttpServletRequest request){
for(MultipartFile temp:file){
String fileName = UUID.randomUUID().toString()+temp.getOriginalFilename().substring(temp.getOriginalFilename().lastIndexOf("."));
String realPath = request.getServletContext().getRealPath("/fileUpload");
File aFile = new File(realPath,fileName);
try{
temp.transferTo(aFile);
}catch(Exception e){
e.printStackTrace();
}
}
return "redirect:/page/ok";
}
总结
传入multiFileUpload()方法的MultipartFile参数是一个数组,数组中的每个元素就是一个MultipartFile对象。数组的名称和上传文件的input标签name属性的值应当一致。
2、文件下载
2.1 添加jstl标签库jar包
2.2 创建下载页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>File Download</title>
</head>
<body>
<%--item 需要用EL表达式取files--%>
<c:forEach items="${files}" var="file">
<%--显示文件名,点击请求下载文件--%>
<%--两条错误代码--%>
<%--<a href="/file/download?fileName="+${file}>${file}</a><br>--%>
<%--<a href="/file/download?fileName="${file}>${file}</a><br>--%>
<a href="/file/download?fileName=${file}">${file}</a><br>
</c:forEach>
</body>
</html>
2.3 添加跳转方法
@RequestMapping("/fileDownload")
public String toFileDownload(Model model, HttpServletRequest request){
String realPath = request.getServletContext().getRealPath("/fileUpload");
File file = new File(realPath);
String[] arr = file.list();
model.addAttribute("files",arr);
return "fileDownload";
}
2.4 添加下载文件的方法
@RequestMapping("/download")
public void fileDownload(String fileName, HttpServletRequest request,HttpServletResponse response){
response.setHeader("Content-Disposition","attachment;filename="+fileName);
String realPath = request.getServletContext().getRealPath("/fileUpload");
File file = new File(realPath,fileName);
try {
ServletOutputStream os = response.getOutputStream();
os.write(FileUtils.readFileToByteArray(file));
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
2.5 总结
- new File(realPath).list()的返回值是包含文件名的String数组
- 在fileDownload.jsp页面中通过c:forEach标签展示文件名,使用超链接点击请求下载,同时传递要下载文件的文件名
- 设置响应头
response.setHeader("Content-Disposition","attachment;filename="+fileName); - 通过路径和文件名定位到文件,将其通过FileUtils读取为为字节数组
- 获取响应的字节输出流输出到浏览器
过程图示
代码写错出现异常的解决过程:
<a href="/file/download?fileName=${file}">${file}</a>
写成了
<a href="/file/download?fileName="${file}>${file}</a>
导致点击下载文件时无法传递fileName,通过浏览器审查发现响应头信息中attachment;filename=为空,有想到可能是fileName没有传递过去,但是看到Tomcat提示信息:getRealPath()方法中的参数/fileUpload是一个目录而不是文件,思考方向就一直固定在目录上,通过尝试:fileUpload、/fileUpload、/fileUpload/、/fileUpload/xxx.jpg四种方式发现只有最后一种方式可行,所以根本原因还是没有定位到要下载的文件,就算这样处理不论点哪个文件都会下载一个固定的文件,显然不合理。最后,终于发现在fileDownload.jsp中传递fileName的超链接的访问地址有问题,原来是将${file} 当作字符串拼接了!!!
静态资源映射
当在DispatcherServlet的url-pattern中配置拦截“/”时,除了.jsp不会拦截以外,其他所有的请求都会经过前端控制器进行匹配,此时静态资源,例如 .css、.js、.jpg…就会被前端控制器拦截,导致不能访问,出现404问题。
在web目录下新建image目录,copy一个图片,在WEB-INF/jsp目录下新建一个jsp文件展示该图片,添加一个跳转该页面的方法,测试发现图片无法显示。
原因:web.xml中设置了<url-pattern>/</url-pattern> ,原本直接可以访问的静态资源现在需要通过前端控制器去寻找图片资源,但是如此配置又会拦截图片等静态资源。
1、通过DefaultServlet处理静态资源
F:\envrionment\apache-tomcat-9.0.34\conf目录下有一个全局的web.xml文件,项目启动时这个全局的配置文件会和项目自身的配置文件合并生效。全局web.xml文件中有一个默认的DefaultServlet,在项目自身的web.xml中,通过对这个DefaultServlet进行配置(无需新建),来放行对静态资源的访问。删除out目录,重启Tomcat测试OK。
缺点:如果静态资源的类型很多,放行配置重复代码很多
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
</web-app>
2、通过静态资源映射器处理静态资源
在spring3.0.4以后的SpringMVc模块提供了静态资源映射器组件。通过mvc:resources标签配置静态资源映射器。
修改SpringMVC配置文件
<mvc:resources mapping="/image/**" location="/image/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
3、通过default-servlet-handler处理静态资源
在SpringMVC的配置文件中配置<mvc:default-servlet-handler/> 后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入 DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。
<mvc:default-servlet-handler/>
4、对比三种方式
方式一和方式三本质相同。
如果静态资源放在web目录下,方式一和方式三有效;如果静态资源放在WEB-INF目录下,只能使用方式二。
SpringMVC的异常处理
1、搭建环境
-
创建项目添加jar包 -
创建Spring MVC配置文件 <?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="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.xsd http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.bjsxt.web.controller"/>
<mvc:annotation-driven/>
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
-
配置前端控制器 <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2、使用@ExceptionHandler注解处理异常
缺点:只能处理当前控制器内的异常。
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/user")
public class UsersController {
@RequestMapping("/getUsers")
public String getUsers(){
int[] arr = new int[1];
arr[2] = 2;
return "getUsers";
}
@ExceptionHandler({java.lang.NullPointerException.class,java.lang.ArithmeticException.class})
public String nullPointerExceptionHandle(Exception e, Model model){
model.addAttribute("msg",e);
return "error1";
}
@ExceptionHandler({java.lang.Exception.class})
public String exceptionHandle(Exception e, Model model){
model.addAttribute("msg",e);
return "error2";
}
}
创建相关页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Get Users</title>
</head>
<body>
getUsers...
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>error1</title>
</head>
<body>
出错了。${msg}
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>error2</title>
</head>
<body>
未知异常。${msg}
</body>
</html>
3、使用@ControllerAdvice和@ExceptionHandler处理异常
GlobalExceptionHandler所在的包应当能够被注解扫描。
package com.bjsxt.web.controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({java.lang.NullPointerException.class,java.lang.ArithmeticException.class})
public String nullPointerExceptionHandle(Exception e, Model model){
model.addAttribute("msg",e);
return "error1";
}
@ExceptionHandler({java.lang.Exception.class})
public String exceptionHandle(Exception e, Model model){
model.addAttribute("msg",e);
return "error2";
}
}
4、使用SimpleMappingExceptionResolver处理异常
缺点:只能实现跳转,无法传递信息。
【注意】@Configuration @Bean
package com.bjsxt.web.controller;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.Properties;
@Configuration
public class GlobalExceptionHandler2 {
@Bean
public SimpleMappingExceptionResolver getResolver(){
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.setProperty("java.lang.NullPointerException","error1");
properties.setProperty("java.lang.ArithmeticException","error2");
resolver.setExceptionMappings(properties);
return resolver;
}
}
5、自定义HandlerExceptionResolve处理器处理异常
通过实现HandlerExceptionResolver接口自定义异常处理器,既可以跳转页面,也可以传递信息。
【注意】仍然有@Configuration注解。
package com.bjsxt.web.controller;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Configuration
public class GlobalExceptionHandler3 implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView modelAndView = new ModelAndView();
if(e instanceof NullPointerException){
modelAndView.setViewName("error1");
}
if(e instanceof ArithmeticException){
modelAndView.setViewName("error2");
}
modelAndView.addObject("msg",e);
return modelAndView;
}
}
SpringMVC拦截器
1、拦截器简介
1.1 什么是拦截器
Spring MVC的拦截器 (Interceptor) 与Servlet的过滤器 (Filter) 类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
1.2 执行流程
1.3 拦截器与过滤器的区别
- 拦截器是SpringMVC组件,而过滤器是Servlet 组件
- 拦截器不依赖容器,过滤器依赖容器
- 拦截器只能对控制器请求起作用,而过滤器则可以对所有的请求起作用
- 拦截器可以获取IOC容器中的各个bean,而过滤器就不太方便
2、定义拦截器
通过实现HandlerInterceptor接口自定义拦截器。
2.1 方法介绍
-
preHandle() 该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作。返回true表示继续向下执行,返回false表示中断后续操作。 -
postHandle() 该方法在控制器的处理请求方法执行之后、视图解析之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。 -
afterCompletion() 该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
2.2 拦截器的使用
项目结构图
-
创建项目、添加jar包等步骤重复,省略 -
创建拦截器,实现HandlerInterceptor接口的MyInterceptor拦截器 package com.bjsxt.interceptor;
import org.springframework.ui.Model;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...处理请求方法前");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...处理请求方法执行之后、视图解析之前");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...处理请求方法执行完成后");
}
}
-
在spring-mvc.xml中配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/user/getUsers"/>
<bean class="com.bjsxt.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
【注意】不拦截是指不对该请求做相关处理,而不是拒绝访问该请求。
3、定义全局拦截器
全局拦截器是指可以拦截所有被控制器所处理URL。作用等同于/**
3.1 创建全局拦截器
package com.bjsxt.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GlobalInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("GlobalInterceptor preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("GlobalInterceptor postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("GlobalInterceptor afterCompletion");
}
}
3.2 在spring-mvc.xml中配置全局拦截器
<mvc:interceptors>
<bean class="com.bjsxt.interceptor.GlobalInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/user/getUsers"/>
<bean class="com.bjsxt.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3.3 说明
-
如果全局拦截器和局部拦截器同时存在,方法的执行顺序: Global preHandle —> preHandle —> postHandle —> Global postHandle —> aftercompletion —> Global afterCompletion -
在拦截器的执行流程中,拦截器在前端控制器之后,它所拦截器都是控制器能够处理的请求,对于控制器不能处理的请求不会生效。 -
如果一个URL能够被多个拦截器所拦截,全局拦截器最先执行,其他拦截器根据配置文件中配置的上下顺序来决定执行顺序的。先配置谁,谁就先执行。
SpringMVC对Restful风格的支持
1、Restful简介
REST: Representational State Transfer(表象层状态转变),是一种设计风格。
传统风格:http://localhost:8888/user/addUsers?username=oldlu&userage=30 Restful风格:http://localhost:8888/user/addUsers/oldlu/30
【优点】 结构清晰、符合标准、易于理解、扩展方便
2、处理 Restful风格的请求
- { }占位符
- @PathVaribale 将URI中占位符参数绑定到控制器处理方法的入参中
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/page")
public class PageController {
@RequestMapping("/{jsp}/aaa/{id}")
public String toPage(@PathVariable(value="jsp") String page, @PathVariable String id, Model model){
model.addAttribute("msg",id+"");
return page;
}
}
【异常信息】java.lang.Integer cannot be cast to java.lang.String
@PathVariable注解已经将浏览器地址栏传递的String类型的id转为Integer类型。但在jsp页面中通过EL表达式获取时,一直提示类型转换失败!!model.addAttribute(“msg”,id+"")解决。
JSON数据处理
在SpringMVC中使用的是Jackson APIl实现对JSON格式数据处理。需要添加Jackson的jar包。
- jackson-annotations-2.11.0.jar
- jackson-core-2.11.0.jar
- jackson-databind-2.11.0.jar
1、搭建环境
-
创建项目添加jar包 -
添加spring配置文件 <?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="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.xsd http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
</beans>
-
修改web.xml配置前端控制器
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
-
在src目录下创建com.bjsxt.web.controller包 -
修改sprin配置文件,开启直接扫描,配置注解驱动,配置视图解析器
<context:component-scan base-package="com.bjsxt.web.controller"/>
<mvc:annotation-driven/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
-
在WEB-INF/js目录下添加Jquery.js -
修改spring配置文件,配置静态资源映射器
<mvc:resources mapping="/js/**" location="/WEB-INF/js/"/>
2、处理请求中的JSON格式数据
在处理请求中的JSON格式数据时需要使用@RequestBody 注解。
@RequestBody RequestBody注解可以将JSON格式的数据转为Java对象。但是要求content-type 不是默认的application/x-www-form-urlcoded编码的内容。一般情况下来说常用其来处理application/json类型。
-
创建Users实体类 package com.bjsxt.pojo;
public class Users {
private String username;
private int userage;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
@Override
public String toString() {
return "Users{" +
"username='" + username + '\'' +
", userage=" + userage +
'}';
}
}
-
添加addusers.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="/js/jquery.js"></script>
<script>
$(function(){
$("#button").click(function(){
var name = $("#username").val();
var age = $("#userage").val();
var obj = {
username:name,
userage:age
};
var user = JSON.stringify(obj);
$.ajax({
url:"/user/addUsers",
contentType:"application/json",
type:"post",
data:user,
success:function(res){
alert(res)
}
});
});
});
</script>
</head>
<body>
用户姓名:<input type="text" id="username"><br>
用户年龄<input type="text" id="userage"><br>
<input type="button" value="ok" id="button">
</body>
</html>
-
创建PageController package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/page")
public class PageController {
@RequestMapping("/{page}")
public String showPage(@PathVariable String page){
return page;
}
}
-
创建UsersController package com.bjsxt.web.controller;
import com.bjsxt.pojo.Users;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Controller
@RequestMapping("/user")
public class UsersController {
@RequestMapping("/addUsers")
public void addUsers(@RequestBody Users users, HttpServletResponse response) throws IOException {
System.out.println(users);
PrintWriter writer = response.getWriter();
writer.print("ok");
writer.flush();
writer.close();
}
}
3、处理响应中的json格式数据
将响应结果转换成JSON格式数据时,需要使用@ResponseBody注解。
@ResponseBody
@ResponseBody注解的作用是将处理请求方法返回的对象通过转换器转换为JSON格式数据,同时写入到response对象的body区,通常用来返回JSON 数据。需要注意,在使用此注解之后不会经过视图解析器,而是直接将数据写入到输出流中,他的效果等同于通过response对象输出指定格式的数据。
-
修改控制器 返回值是String类型 package com.bjsxt.web.controller;
import com.bjsxt.pojo.Users;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
@Controller
@RequestMapping("/user")
public class UsersController {
@RequestMapping(value = "/addUsers",produces = MediaType.TEXT_PLAIN_VALUE+";charset=utf-8")
@ResponseBody
public String addUsers(@RequestBody Users users) throws IOException {
System.out.println(users);
return "ok";
}
}
返回值是其他类型 package com.bjsxt.web.controller;
import com.bjsxt.pojo.Users;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.IOException;
@Controller
@RequestMapping("/user")
public class UsersController {
@RequestMapping(value = "/addUsers",produces = MediaType.APPLICATION_JSON_VALUE+";charset=utf-8")
@ResponseBody
public Users addUsers(@RequestBody Users users) throws IOException {
System.out.println(users);
return users;
}
}
-
解决响应中JSON数据中文乱码问题
@RequestMapping(value="/addUsers",produces = MediaType.TEXT_PLAIN_VALUE+";charset=utf-8")
@RequestMapping(value="/addUsers",produces=MediaType.APPLICATION_JSON_VALUE+";charset=utf-8")
SpringMVC解决跨域请求问题
1、什么是同源策略
同源策略是浏览器的一个安全功能,所谓的同源,指的是协议,域名,端口相同。浏览器处于安全方面的考虑,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
哪些不受同源策略限制:
- 页面中的链接,重定向以及表单提交是不会受到同源策略限制的
- 跨域资源的引入是可以的。如嵌入到页面中的scrpit img link href frame等
2、什么是跨域请求
在JavaScript的请求中当一个请求URL的协议、域名、端口三者之间任意一个与当前页面URL不同时即为跨域。浏览器执行JavaScript脚本时,会检查当前请求是否同源,如果不是同源中的资源,就不会被执行。
3、通过@CrossOrigin解决跨域
- CrossOrigin:注解用于处理跨域请求访问。是SpringMVC4.2版本中推出的注解
- origins:允许可访问的域列表,String类型的数组
- maxAge:准备响应前的缓存持续的最大时间(以秒为单位)
@Controller
@RequestMapping("/user")
public class UsersController {
@RequestMapping(value = "/addUsers",produces = MediaType.APPLICATION_JSON_VALUE+";charset=utf-8")
@ResponseBody
@CrossOrigin(origins = "http://localhost:8888")
public Users addUsers(@RequestBody Users users) throws IOException {
System.out.println(users);
return users;
}
}
SpringMVC常用注解
- @Controller
- @RequestMapping
- @GetMapping
- @PostMapping
- @PathVariable,restful风格
- @RequestParam
- @RequestBody
- @ResponseBody
- @RequestHeader
- @CookieValue
@Controller
@RequestMapping("/page")
public class PageController {
@RequestMapping("/{page}")
public String showPage(@PathVariable String page, @RequestHeader("Accept-Language") String language, @CookieValue("JSESSIONID") String value){
System.out.println(language);
System.out.println(value);
return page;
}
}
SSM框架整合
1、SSM框架整合介绍
1.1 整合步骤
- 创建项目,添加jar包
- 创建包,创建实体与Mapper
- 添加连接数据库的properties文件
- 添加Log4j配置文件
- 添加Spring配置文件
- 添加SpringMVC配置文件
- 修改web.xml文件
1.2 jar包依赖
ssm整合jar包依赖共计27个jar包。所有jar包位于springMVC\软件\ssmlib目录。
2、搭建环境
2.1 创建表
CREATE TABLE `users` (
`userid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`userpwd` varchar(30) NOT NULL,
PRIMARY KEY (`userid`),
UNIQUE KEY `username_uk` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.2 创建项目及结构
-
创建项目,添加jar包 -
创建包,创建实体类与Mapper
2.3 配置SSM整合
-
添加连接数据的properties文件 jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?useSSL=false
jdbc.username=root
jdbc.password=1615
-
添加log4j配置文件 log4j.rootLogger=info,console
### appender.consoleê?3?μ?????ì¨ ###
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=<%d> %5p (%F:%L) [%t] (%c) - %m%n
log4j.appender.console.Target=System.out
### appender.logfileê?3?μ?è??????t ###
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=SysLog.log
log4j.appender.logfile.MaxFileSize=500KB
log4j.appender.logfile.MaxBackupIndex=7
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=<%d> %p (%F:%L) [%t] %c - %m%n
-
添加Spring配置文件 在SSM框架整合时为了能够清晰的对Spring 配置项进行管理,我们可以采用“分而治之”的方式来定义Spring配置文件。将Spring配置文件分解成三个配置文件,在不同的配置文件中配置不同的内容。
- applicationContext-dao.xml 该配置文件的作用是用来配置与Mybatis框架整合
- applicationContext-trans.xml 该配置文件的作用是用来配置Spring声明式事务管理
- applicationContext-service.xml 该配置文件的作用是用来配置注解扫描以及其他的Spring配置
-
配置整合Mybatis <?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="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.xsd">
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.bjsxt.pojo"/>
</bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer" >
<property name="basePackage" value="com.bjsxt.mapper"/>
</bean>
</beans>
-
配置声明式事务管理 <?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="modify*" propagation="REQUIRED"/>
<tx:method name="drop*" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="myPointCut" expression="execution(* com.bjsxt.service.*.*(..))"/>
<aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut"/>
</aop:config>
</beans>
-
开启Spring注解扫描 <?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="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.xsd">
<context:component-scan base-package="com.bjsxt.service" />
</beans>
-
配置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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.bjsxt.web.controller"/>
<mvc:annotation-driven/>
<bean id="servlet" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:resources mapping="/js/**" location="/WEB-INF/js/"/>
<mvc:resources mapping="/img/**" location="/WEB-INF/img/"/>
<mvc:resources mapping="/css/**" location="/WEB-INF/css/"/>
</beans>
-
配置web.xml <?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3、Spring 与 SpringMVC父子容器问题
Spring的ContextLoaderListener会创建Spring的IoC容器,标记为A容器;
SpringMVC的DispatcherServlet会创建SpringMVC的IoC容器,标记为a容器;
SpringMVC会将A容器设置为其父容器。
父容器对子容器可见,所以在子容器中可以访问父容器的Bean对象;而子容器对父容器不可见。
注解扫描时需要注意
-
如果在Spring MVC配置文件中扫描所有注解,声明式事务会失效 因为Spring声明式事务管理的切面位于A容器,a容器扫描注解得到的Bean对象对A容器不可见,所以事务管理器无法对B容器中的Bean对象实现事务控制。 -
如果在Spring配置文件中扫描所有注解,无法找到控制器报404异常 此时控制器对象位于A容器,但HandleMapping在根据URI查找控制器时,只会去a容器中查找,所以会出现404异常。
正确使用方式
在Spring MVC配置文件中扫描@Controller注解,在Spring配置文件中扫描除Controller 以外的其他注解。在Spring MVC的控制器可以注入SpringloC容器中的Bean对象,因为父容器对子容器是可见的。
4、实现用户登录
需求:实现用户登录业务,同时记录用户的登录日志。登录日志要求记录用户名、登录时间以及登录的IP地址。
4.1 用户登录
创建业务层
package com.bjsxt.service;
import com.bjsxt.pojo.Users;
public interface UsersService {
Users userLogin(Users users);
}
package com.bjsxt.service.impl;
import com.bjsxt.exception.UserNotFoundException;
import com.bjsxt.mapper.UsersMapper;
import com.bjsxt.pojo.Users;
import com.bjsxt.pojo.UsersExample;
import com.bjsxt.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UsersServiceImpl implements UsersService {
@Autowired
private UsersMapper usersMapper;
@Override
public Users userLogin(Users users) {
UsersExample usersExample = new UsersExample();
UsersExample.Criteria criteria = usersExample.createCriteria();
criteria.andUsernameEqualTo(users.getUsername());
criteria.andUserpwdEqualTo(users.getUserpwd());
List<Users> list = usersMapper.selectByExample(usersExample);
if(list == null || list.size() <= 0){
throw new UserNotFoundException("用户名或密码有误");
}
return list.get(0);
}
}
创建自定义异常
package com.bjsxt.exception;
public class UserNotFoundException extends RuntimeException{
public UserNotFoundException() {
super();
}
public UserNotFoundException(String message) {
super(message);
}
public UserNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
创建用户登录控制器
package com.bjsxt.web.controller;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/user")
public class UsersController {
@Autowired
private UsersService usersService;
public String userLogin(Users users, HttpSession session){
Users user = this.usersService.userLogin(users);
session.setAttribute("user",user);
return "redirect:/page/index";
}
}
全局异常处理器
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionController {
@ExceptionHandler({com.bjsxt.exception.UserNotFoundException.class})
public String userNotFoundExceptionHandle(Exception e, Model model){
model.addAttribute("msg",e.getMessage());
return "login";
}
@ExceptionHandler({java.lang.Exception.class})
public String exceptionHandle(Exception e){
return "redirect:/page/error";
}
}
创建用户登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
<form action="/user/userLogin" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="text" name="userpwd"><br>
<input type="submit" value="ok">
</form>
</body>
</html>
创建异常提示页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Error</title>
</head>
<body>
出错了,请与管理员联系。
email:1615669687@qq.com
</body>
</html>
创建页面跳转控制器
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class PageController {
@RequestMapping("/")
public String showIndex(){
return "index";
}
@RequestMapping("/page/{page}")
public String showPage(@PathVariable String page){
return page;
}
}
创建拦截器——判断用户是否登录
package com.bjsxt.interceptor;
import com.bjsxt.pojo.Users;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
Users user = (Users)session.getAttribute("user");
if(user == null || user.getUsername().length() <= 0){
response.sendRedirect("/page/login");
return false;
}
return true;
}
}
配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/page/login"/>
<mvc:exclude-mapping path="/user/userLogin"/>
<mvc:exclude-mapping path="/page/error"/>
<bean class="com.bjsxt.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
4.2 记录用户登录日志
-
创建表 CREATE TABLE `logs` (
`logid` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(30) DEFAULT NULL,
`ip` varchar(30) DEFAULT NULL,
`logtime` date DEFAULT NULL,
PRIMARY KEY (`logid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
创建实体类与Mapper -
创建日志记录切面 package com.bjsxt.aop;
import com.bjsxt.mapper.LogsMapper;
import com.bjsxt.pojo.Logs;
import com.bjsxt.pojo.UsersExt;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
@Aspect
@Component
public class UsersLoginLogAOP {
@Autowired
private LogsMapper logsMapper;
@Pointcut("execution(* com.bjsxt.service.UsersService.userLogin(..))")
public void myPointCut(){}
@AfterReturning("myPointCut()")
public void userLoginLog(JoinPoint joinPoint){
Object[] objs = joinPoint.getArgs();
UsersExt user = (UsersExt)objs[0];
Logs logs = new Logs();
logs.setUsername(user.getUsername());
logs.setLogtime(new Date());
logs.setIp(user.getIp());
this.logsMapper.insertSelective(logs);
}
}
-
创建Users扩展实体类 package com.bjsxt.pojo;
public class UsersExt extends Users{
private String ip;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
}
-
修改控制器 package com.bjsxt.web.controller;
import com.bjsxt.pojo.Users;
import com.bjsxt.pojo.UsersExt;
import com.bjsxt.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/user")
public class UsersController {
@Autowired
private UsersService usersService;
@RequestMapping("/userLogin")
public String userLogin(UsersExt users, HttpSession session, HttpServletRequest request){
String ip = request.getRemoteAddr();
users.setIp(ip);
Users user = this.usersService.userLogin(users);
session.setAttribute("user",user);
return "redirect:/page/index";
}
}
-
配置切面
-
在applicationContext-service.xml中开启aop包注解扫描
<context:component-scan base-package="com.bjsxt.service,com.bjsxt.aop" />
-
在applicationContext-trans.xml中开启aspecyJ自动代理和登录操作的事务管理 <?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="modify*" propagation="REQUIRED"/>
<tx:method name="drop*" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="userLogin" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="myPointCut" expression="execution(* com.bjsxt.service.*.*(..))"/>
<aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut"/>
</aop:config>
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
【一个异常】提示找不到Logs LogsMapper类
原因:对于aop包,不应在spring-mvc.xml中扫描注解。
基于Maven搭建SSM整合
使用Spring两个问题:
Maven能够很好的解决jar包管理的问题,通过给定坐标,自动导入jar包依赖。
1、创建Maven项目
将普通的jar项目改造为war项目
-
创建目录和相关文件 src—>main—>webapp—>WEB-INF—>web.xml -
在pom.xml文件中添加 <groupId>com.bjsxt</groupId>
<artifactId>maven_ssm_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
-
如果webapp目录不能直接新建jsp文件,在项目结构中通过modules添加web组件
2、配置镜像地址
Maven中央仓库是国外站点,下载构件较慢,可配置国内的阿里镜像或其他镜像地址。配置镜像地址时需要Maven的settings.xml配置文件。该配置文件需要从Apache下载的Maven中获取。最后在ldea中指定该配置文件路径。
3、添加jar包
Maven通过坐标管理jar包或插件。在中央仓库查找依赖构件的坐标,并将该坐标添加到Maven的pom.xml文件中。
Maven查找构件的顺序:先从本地仓库中查找,如果本地仓库没有则会去中央仓库下载并保存到本地仓库中。
中央仓库地址:
https://mvnrepository.com/
本地仓库默认路径为:
C:\Users\86150\.m2\repository
可以在settings.xml文件中指定本地仓库的路径
<localRepository>F:\.m2\repository</localRepository>
也可以在ldea中指定Maven的设置文件和本地仓库路径
3.1 配置依赖jar包的坐标
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
</dependencies>
4、配置Tomcat插件
5、配置资源拷贝路径
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
配置Tomcat插件显示异常
Cannot resolve plugin org.apache.tomcat.maven:tomcat7-maven-plugin
【解决】使用version标签添加版本号
为什么要配置资源拷贝路径?
【原因】Maven默认从resources目录加载配置文件,但UsersMapper.xml、UsersMapper.java等映射配置文件位于com.bjsxt.mapper包下,为了加载全部的配置文件,配置java和resources两个资源拷贝目录。
启动Tomcat插件直接报错,process terminated,意思是本地仓库都默认路径拒绝Maven访问
【解决】将repository目录和settings.xml文件复制到F盘,在IDEA中重新指定路径。
6、配置框架配置文件
UsersMapper.java、UsersMapper.java等放在mapper包中,其他的.properties文件、.xml文件放在resources目录中。
7、实现用户查询
-
创建查询用户到控制器 package com.bjsxt.web.controller;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UsersService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("/user")
public class UsersController {
@Autowired
private UsersService usersService;
@RequestMapping("/findUsers")
public String findUsersAll(Model model){
List<Users> list = this.usersService.findUsersAll();
model.addAttribute("list",list);
return "showUsers";
}
}
-
创建展示用户信息的页面 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>show users</title>
</head>
<body>
<table border="1px" align="center">
<tr>
<th>用户ID</th>
<th>用户名</th>
<th>密码</th>
</tr>
<%--注意这里的list也要使用EL表达式取--%>
<c:forEach items="${list}" var="user">
<tr>
<td>${user.userid}</td>
<td>${user.username}</td>
<td>${user.userpwd}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
|