SpringMVC是一个基于MVC模式的Web框架,是Spring框架的一个模块。它以SpringloC容器为基础,并利用容器的特性来简化它的配置,所以SpringMVC和Spring可直接整合使用。SpringMVC对MVC流程进行了封装,屏蔽掉很多底层代码,让开发者可以更加轻松快捷的完成基于MVC模式的Web开发。
工作流程:
Spring核心容器模块
Commons-loggin日志
Spring AOP模块
Spring Web模块
Spring Web MVC模块
Servlet
步骤:
创建web项目
添加相关jar包
在src目录下添加springmvc.xml配置文件
<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的约束文件
修改web.xml文件
<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>springMvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
创建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" %>
$Title$
${msg}
在springmvc.xml中配置Controller
<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的基本使用中我们是以传统方式创建的控制器,它需要实现 Controller接口。传统风格的控制器不仅需要在配置文件中配置映射,而且只能编写一个处理方法,不够灵活。
使用基于注解的控制器具有以下两个优点:
@Controller Controller注解用于指定Bean对象为控制器
@RequesrMapping 用于将一个URI绑定到类上或类的方法中
和基本使用中完全相同
和基本使用中完全相同
基于注解创建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 {
/**
* 访问路径为http:localhost:8808:/spring_mvc_ann_demo/con/ann
* 如果类中未指定url,访问路径为http:localhost:8808:/spring_mvc_ann_demo/ann
* @return
*/
@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命名空间,开启注解扫描
<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>
【解决的两个异常】
在基于注解方式开发控制器时,需要添加
标签,它是启用MVC注解的钥匙。如果没有使用这个标签,而仅仅是使用
标签扫描并注册了相关的控制器,那么仅是@Controller @RequestMapping基本功能的注解可以使用除此以外的相关的注解并不能使用。
的作用是提供扩展功能的。
它的处理类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参数注入的优点
/**
* 可以将HttpServletRequest对象作为参数传入处理请求的方法中
* 从HttpServletRequest对象获取数据,再将其响应回index.jsp
* @param request
* @return
*/
@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表达式获取响应内容
/**
* 也可以将请求的参数直接作为参数传入处理请求的方法中
* @param user_name
* @param user_age
* @return
*/
@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表达式获取响应内容
RequestParam:将请求参数绑定到控制器的方法参数上。
/**
* value 用于指定请求中参数的名称
* required 某个参数是否为必填项,默认为true,必填
* defaultValue 如果设置该属性,required=false
* @param user_name
* @param user_age
* @return
*/
@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;
}
在SpringMVC请求参数注入中,如果有多请求参数的name相同,那么可以使用String或List集合来接收请求参数。如果使用的 List类型需要在该参数前添加@RequestParam注解,String[]则不需要。
创建一个addUsers.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
在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表达式获取响应内容
在SpringMVc的请求参数注入中,可以使用注入POJO方式来接收请求参数。
要求:请求参数的name必须与POJO的属性名相同。
创建和表单提交信息相应的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表达式获取响应内容
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" %>
Title
在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
在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" %>
Title
添加处理请求的方法
并没有遍历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;
}
在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" %>
Title
添加处理请求的方法
对于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;
}
【
在请求参数中如果含有中文,会出现乱码现象。
可通过修改Tomcat的server.xml配置文件,解决中文乱码。
<Connector port="8888" protocol="HTTP/1.1"
connectionTimeout="2000n"
redirectPort="8443"lURIEncoding="utf-8"/>
org.springframework.web.filter.CharacterEncodingFilter提供了过滤器,不用写,直接配置即可。
<filter>
<filter-name>encFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodeFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
在@RequestMapping 注解中如果并未指定处理请求类型,那么含有该注解的方法既可以处理GET类型请求也可以处理POST类型的请求。如果需要指定只能处理某种类型的请求,可以通过method属性指定请求类型。
@RequestMapping(value = "/addUsers7",method = RequestMethod.GET)
表示只能处理get方式请求。
//name属性绑定url
@GetMapping(name = "addUsers7")
表示只能处理post方式请求。
//name属性绑定url
@PostMapping(name = "addUsers7")
在SpringMVC中提供了13个视图解析器,用于支持不同的视图技术。视图解析器最大的特点是可以将控制器中处理请求的逻辑和视图中渲染实现解耦。InternalResourceViewResolver
是SpringMVC中默认的视图解析器,用来解析JSP视图。能将视图名映射为JSP文件。
修改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";
}
}
返回值不再是ModelAndView,响应的内容如何回送到浏览器?
在web目录下的jsp文件可以在地址栏直接访问;但实际环境中jsp文件应放在WEB-INF目录下,不允许通过地址栏直接访问;这时候,我们可以通过视图解析器设置前缀和后缀,以处理请求的方法跳转jsp页面。比如,通过上面的设置访问/page/login跳转到WEB-INF/jsp/login.jsp页面。
作用域:“数据共享的范围”,也就是说数据能够在多大的范围内有效。
在处理请求的方法中传入HttpServletRequest对象,调用setAttribute方法设置信息,在login.jsp通过EL表达式获取。
请求访问/page/login,跳转login.jsp,通过EL表达式获取该次请求中传递的参数信息。
HttpServletRequest对象是容器提供的,容器与控制器之间是紧耦合。
@RequestMapping("/login")
public String toLogin(HttpServletRequest request){
request.setAttribute("msg","001");
return "login";
}
SpringMVC会为Map接口注入BindingAwareModelMap对象。该对象是由Spring提供的一个实现了Map接口的对象。SpringMVC会把该对象中的数据放入到HttpServletRequest对象中,其目的是为了解除控制器与容器的耦合。
@RequestMapping("/login")
public String toLogin(HttpServletRequest request, Map<String,String> map){
map.put("msg","hello");
return "login";
}
【注意】
在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";
}
Session 作用域表示在当前会话中有效。在SpringMVc中对于Session作用域传值,只能使用HttpSession对象来实现。对于HttpSession对象的获取方式有两种:
@RequestMapping("/login")
public String toLogin(HttpServletRequest request, HttpSession session){
// HttpSession session = request.getSession();
session.setAttribute("msg","001");
return "login";
}
Application作用域表示在整个应用范围都有效。在SpringMvc中对于Application作用域传值,只能使用servletContext对象来实现。但是对于该对象的获取方式不能直接向方法中注入。
@RequestMapping("/login")
public String toLogin(HttpServletRequest request, HttpSession session){
// ServletContext servletContext = request.getServletContext();
ServletContext servletContext = session.getServletContext();
servletContext.setAttribute("msg","002");
return "login";
}
在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);
}
如果使用forward
关键字实现请求转发跳转时,是通过SpringMVC的DispatcherServlet组件来完成的。不会执行视图解析器,所以需要指定请求跳转页面的完整URI。
@RequestMapping("/login3")
public String showLogin2(Model model){
model.addAttribute("msg","003");
return "forward:/WEB-INF/jsp/login.jsp";
}
在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";
}
在SpringMVC需要使用redirect关键字实现重定向的跳转。在重定向跳转中是不经过视图解析器的。
@RequestMapping("/redirectLogin")
public String goRedirect(){
//redirect:/WEB-INF/jsp/login.jsp会报错
//重定向地址栏url会改变,所以/WEB-INF/jsp/login.jsp仍然是不被允许直接访问的
return "redirect:/page/login3";
}
过程:
访问/page/redirectLogin-----------重定向----------->/page/login3-------------请求转发------------>/WEB-INF/jsp/login.jsp
在SpringMVC中提供了用于处理文件上传的组件CommonsMultipartResolver(多部件解析器)。可以通过该组件很方便的实现文件上传。该组件的运行需要依赖于Apache的commons-fileupload 与commons-io包。
一般步骤:
创建普通项目
添加jar包
添加web框架
配置Tomcat
创建controller包
添加Spring配置文件
<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配置前端控制器
<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>springMvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
<%--请求方式必须是post--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
OK...
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 用于跳转到.jsp页面
*/
@Controller
@RequestMapping("/page")
public class PageController {
/**
* 跳转到singleFile.jsp
* @return
*/
@RequestMapping("/singleFile")
public String toSingleFile(){
return "singleFile";
}
/**
* 跳转到ok.jsp
* @return
*/
@RequestMapping("/ok")
public String toOK(){
return "ok";
}
}
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";
}
}
过程图示
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
/**
* 跳转到multiFile.jsp
* @return
*/
@RequestMapping("/multiFile")
public String toMultiFile(){
return "multiFile";
}
@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属性的值应当一致。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
File Download
<%--item 需要用EL表达式取files--%>
<%--显示文件名,点击请求下载文件--%>
<%--两条错误代码--%>
<%--${file}
--%>
<%--${file}
--%>
${file}
/**
* 1、跳转到文件下载的fileDownload.jsp页面
* 2、展示已有文件
* @return
*/
@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";
}
/**
* @param fileName 下载哪个文件
* @param request IO流、涉及路径转换
* @param response 将通过字符流输出到浏览器
*/
@RequestMapping("/download")
public void fileDownload(String fileName, HttpServletRequest request,HttpServletResponse response){
//下载文件需要设置的响应头
response.setHeader("Content-Disposition","attachment;filename="+fileName);
//从fileupload_war_exploded目录往下找
//这里提供的是要下载的文件所在的文件夹的路径,无法定位到具体文件,点击会下载一个位置大小的txt文件
//如果指定具体文件的目录,又无法实现下载文件的可选性
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();
}
}
response.setHeader("Content-Disposition","attachment;filename="+fileName);
过程图示
代码写错出现异常的解决过程:
${file}
写成了
${file}
导致点击下载文件时无法传递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中设置了
,原本直接可以访问的静态资源现在需要通过前端控制器去寻找图片资源,但是如此配置又会拦截图片等静态资源。
F:\envrionment\apache-tomcat-9.0.34\conf目录下有一个全局的web.xml文件,项目启动时这个全局的配置文件会和项目自身的配置文件合并生效。全局web.xml文件中有一个默认的DefaultServlet,在项目自身的web.xml中,通过对这个DefaultServlet进行配置(无需新建),来放行对静态资源的访问。删除out目录,重启Tomcat测试OK。
缺点:如果静态资源的类型很多,放行配置重复代码很多
<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>springMvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<servlet-mapping>
<servlet-name>defaultservlet-name>
<url-pattern>*.jpgurl-pattern>
servlet-mapping>
web-app>
在spring3.0.4以后的SpringMVc模块提供了静态资源映射器组件。通过mvc:resources标签配置静态资源映射器。
修改SpringMVC配置文件
<mvc:resources mapping="/image/**" location="/image/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
在SpringMVC的配置文件中配置
后,会在Spring MVC上下文中定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入 DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。
<mvc:default-servlet-handler/>
方式一和方式三本质相同。
如果静态资源放在web目录下,方式一和方式三有效;如果静态资源放在WEB-INF目录下,只能使用方式二。
创建项目添加jar包
创建Spring MVC配置文件
<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>
配置前端控制器
<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>springMvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
缺点:只能处理当前控制器内的异常。
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(){
/*String str = null;
str.length();*/
/*int i = 1/0;*/
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" %>
Get Users
getUsers...
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
error1
出错了。${msg}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
error2
未知异常。${msg}
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";
}
}
缺点:只能实现跳转,无法传递信息。
【注意】@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;
}
}
通过实现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;
}
}
Spring MVC的拦截器 (Interceptor) 与Servlet的过滤器 (Filter) 类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。
通过实现HandlerInterceptor接口自定义拦截器。
preHandle()
该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作。返回true表示继续向下执行,返回false表示中断后续操作。
postHandle()
该方法在控制器的处理请求方法执行之后、视图解析之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
afterCompletion()
该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
项目结构图
创建项目、添加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>
【注意】不拦截是指不对该请求做相关处理,而不是拒绝访问该请求。
全局拦截器是指可以拦截所有被控制器所处理URL。作用等同于/**
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");
}
}
<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>
如果全局拦截器和局部拦截器同时存在,方法的执行顺序:
Global preHandle —> preHandle —> postHandle —> Global postHandle —> aftercompletion —> Global afterCompletion
在拦截器的执行流程中,拦截器在前端控制器之后,它所拦截器都是控制器能够处理的请求,对于控制器不能处理的请求不会生效。
如果一个URL能够被多个拦截器所拦截,全局拦截器最先执行,其他拦截器根据配置文件中配置的上下顺序来决定执行顺序的。先配置谁,谁就先执行。
REST: Representational State Transfer(表象层状态转变),是一种设计风格。
传统风格:http://localhost:8888/user/addUsers?username=oldlu&userage=30
Restful风格:http://localhost:8888/user/addUsers/oldlu/30
【优点】
结构清晰、符合标准、易于理解、扩展方便
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+"")解决。
在SpringMVC中使用的是Jackson APIl实现对JSON格式数据处理。需要添加Jackson的jar包。
创建项目添加jar包
添加spring配置文件
<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>springMvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMvcservlet-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/"/>
在处理请求中的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" %>
Title
用户姓名:
用户年龄
创建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();
}
}
将响应结果转换成JSON格式数据时,需要使用@ResponseBody注解。
@ResponseBody
@ResponseBody注解的作用是将处理请求方法返回的对象通过转换器转换为JSON格式数据,同时写入到response对象的body区,通常用来返回JSON 数据。需要注意,在使用此注解之后不会经过视图解析器,而是直接将数据写入到输出流中,他的效果等同于通过response对象输出指定格式的数据。
如果处理请求方法返回的是String时,@ResponseBody注解不会进行JSON转换。响应的Content-Type为text/plain;charset=lSO-8859-1。
如果处理请求方法返回的是除了String类型以外的其他object类型时,@ResponseBody注解会进行JSON转换。响应的Content-Type为application/json。
修改控制器
返回值是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数据中文乱码问题
//返回值是String类型时
@RequestMapping(value="/addUsers",produces = MediaType.TEXT_PLAIN_VALUE+";charset=utf-8")
//返回值是其他类型时
@RequestMapping(value="/addUsers",produces=MediaType.APPLICATION_JSON_VALUE+";charset=utf-8")
同源策略是浏览器的一个安全功能,所谓的同源,指的是协议,域名,端口相同。浏览器处于安全方面的考虑,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
哪些不受同源策略限制:
在JavaScript的请求中当一个请求URL的协议、域名、端口三者之间任意一个与当前页面URL不同时即为跨域。浏览器执行JavaScript脚本时,会检查当前请求是否同源,如果不是同源中的资源,就不会被执行。
@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;
}
}
@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整合jar包依赖共计27个jar包。所有jar包位于springMVC\软件\ssmlib目录。
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;
创建项目,添加jar包
创建包,创建实体类与Mapper
添加连接数据的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Êä³öµ½¿ØÖÆ̨ ###
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Êä³öµ½ÈÕÖ¾Îļþ ###
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配置文件分解成三个配置文件,在不同的配置文件中配置不同的内容。
配置整合Mybatis
<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>
配置声明式事务管理
<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注解扫描
<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
<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
<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>contextConfigLocationparam-name>
<param-value>classpath:applicationContext-*.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<servlet>
<servlet-name>springMvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springMvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
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对象,因为父容器对子容器是可见的。
需求:实现用户登录业务,同时记录用户的登录日志。登录日志要求记录用户名、登录时间以及登录的IP地址。
创建业务层
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;
/**
* 用户登录
* @param users
* @return
*/
@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" %>
Title
${msg}
创建异常提示页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Error
出错了,请与管理员联系。
email:[email protected]
创建页面跳转控制器
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 关于为什么不在PageController加@RequestMapping注解?
* Tomcat启动默认访问index,url中没有/page。
*/
@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 {
/**
* 判断用户是否登录
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@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>
创建表
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 "index";
//关于为什么这里必须是重定向,且redirect:/page/index
return "redirect:/page/index";
}
}
配置切面
在applicationContext-service.xml中开启aop包注解扫描
<context:component-scan base-package="com.bjsxt.service,com.bjsxt.aop" />
在applicationContext-trans.xml中开启aspecyJ自动代理和登录操作的事务管理
<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中扫描注解。
使用Spring两个问题:
Maven能够很好的解决jar包管理的问题,通过给定坐标,自动导入jar包依赖。
将普通的jar项目改造为war项目
创建目录和相关文件
src—>main—>webapp—>WEB-INF—>web.xml
在pom.xml文件中添加
<groupId>com.bjsxtgroupId>
<artifactId>maven_ssm_demoartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
如果webapp目录不能直接新建jsp文件,在项目结构中通过modules添加web组件
Maven中央仓库是国外站点,下载构件较慢,可配置国内的阿里镜像或其他镜像地址。配置镜像地址时需要Maven的settings.xml配置文件。该配置文件需要从Apache下载的Maven中获取。最后在ldea中指定该配置文件路径。
Maven通过坐标管理jar包或插件。在中央仓库查找依赖构件的坐标,并将该坐标添加到Maven的pom.xml文件中。
Maven查找构件的顺序:先从本地仓库中查找,如果本地仓库没有则会去中央仓库下载并保存到本地仓库中。
中央仓库地址:
https://mvnrepository.com/
本地仓库默认路径为:
C:\Users\86150\.m2\repository
可以在settings.xml文件中指定本地仓库的路径
<localRepository>F:\.m2\repositorylocalRepository>
也可以在ldea中指定Maven的设置文件和本地仓库路径
3.1 配置依赖jar包的坐标
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.6version>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.12version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.12version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.12version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.48version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
<configuration>
<port>8080port>
<path>/path>
configuration>
plugin>
plugins>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.xmlinclude>
<include>**/*.propertiesinclude>
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中重新指定路径。
UsersMapper.java、UsersMapper.java等放在mapper包中,其他的.properties文件、.xml文件放在resources目录中。
创建查询用户到控制器
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" %>
show users
用户ID
用户名
密码
<%--注意这里的list也要使用EL表达式取--%>
${user.userid}
${user.username}
${user.userpwd}