SpringMVC
服务器端分为三层架构:表现层、业务层、持久层。
SpringMVC是表现层的框架。
MVC设计模型:
- model模型
- view视图
- controller控制器
SpringMVC和Struts2的优劣区别:
共同点:
- 他们是表现层框架,都是基于MVC模型写的
- 底层都离不开原始ServletAPI
- 处理请求的机制都是一个核心控制器
区别:
- Spring MVC的入口是Servlet,而Struts2是Filter
- SpringMVC是基于方法设计的,而Struts2是基于类,Struts2每次执行都会创建一个动作类。所以SpringMVC会比Struts2稍微快一些。
- SpringMVC使用更加简洁,同时支持JSR303,处理ajax的请求更方便
快速入门
- 创建前端控制器DispatcherServlet
- 加载springmvc.xml配置文件
- xml中创建了视图解析器对象
其中涉及注释:
@Controller
@RequestMapping
用于建立请求URL和处理请求方法之间的对应关系- 属性path:响应请求,也可以用value,当只有这一个时可不写
- 属性method:可选择请求方式,底层是一个枚举
- 属性params:可以指定必须传入值的属性(可加值)
- 属性headers:用于指定限制请求消息头的条件
涉及的标签:
<mvc:annotation-driven/>
:开启SpringMVC框架注解的支持
web.xml:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>dispatcherServlet</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>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern><!--所有的请求都会经过Servlet-->
</servlet-mapping>
</web-app>
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"
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
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="cn.huangyy"></context:component-scan>
<!--视图解析器对象-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property><!--前缀-->
<property name="suffix" value=".jsp"></property><!--扩展名-->
</bean>
<!--开启SpringMVC框架注解的支持-->
<mvc:annotation-driven/>
</beans>
详细图解:
在springMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。
使用<mvc:annotation-driven/>
可自动加载RequestMappingHandlerMapping(处理器映射器)和RequestMappingHandlerAdapter(处理器适配器)。
请求参数的绑定
绑定机制:
- 表单提交的数据都是k=v格式的
- SpringMVC的参数绑定过程是把表单提交的参数,作为控制器中方法的参数进行绑定的
- 要求:提交表单的name和参数名称是相同的
支持的数据类型:
- 基本数据类型和字符串类型
- 实体类型(JavaBean)
- 集合类型(List、Map集合等)
基本数据类型和字符串类型
- 提交表单的name和参数名称是相同的
- 区分大小写
<a href="param/testParam?username=hehe&password=1234">请求参数绑定</a>
@RequestMapping("/testParam")
public String testParam(String username,String password){
System.out.println("输出了!");
System.out.println("用户名:"+username);
System.out.println("密码:"+password);
return "success";
}
实体类型
- 提交表单的name和JavaBean中的属性名称需要一致
- 如果一个JavaBean类中包含其他的引用类型,那么表单的name属性需要编写成:对象.属性 如:address.name
<form action="param/saveAccount" method="post">
用户名:<input type="text" name="username" ><br>
密码:<input type="text" name="password" ><br>
金额:<input type="text" name="money" ><br>
<input type="submit" value="提交"><br>
</form>
@RequestMapping("/saveAccount")
public String testSaveAccount(Account account){
System.out.println("输出了!");
System.out.println(account);
return "success";
}
若有引用,则可:
姓名:<input type="text" name="user.uname" ><br>
年龄:<input type="text" name="user.age" ><br>
乱码过滤器的配置:
在web.xml中:
<filter>
<filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
集合类型
若account中有以下两个类型
private List<User> list;
private Map<String,User> map;
姓名:<input type="text" name="list[0].uname" ><br>
年龄:<input type="text" name="list[0].age" ><br>
姓名:<input type="text" name="map['one'].uname" ><br>
年龄:<input type="text" name="map['one'].age" ><br>
自定义类型转换器
MVC内部有进行数据类型转换,但并不是所有都能转
如自定义一个String转Date
写一个类继承Converter:
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
if (s==null){
throw new RuntimeException("传入参数为空!");
}
DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
try {
return df.parse(s);
} catch (Exception e) {
throw new RuntimeException("数据类型转换出现异常!");
}
}
}
在springmvc.xml配置文件中配置:
<!--配置自定义类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="cn.huangyy.utils.StringToDateConverter"/>
</set>
</property>
</bean>
<!--开启SpringMVC框架注解的支持-->
<mvc:annotation-driven conversion-service="conversionService"/>
获取Servlet原生的API
只需要在参数中写入HttpServletRequest和HttpServletResponse即可
常用注解:
@RequestParam
把请求中制定名称的参数给控制器中的形参赋值。
属性
- value:请求参数中的名称。也是name属性
- required:默认为true,则必须传一个
<a href="anno/testRequestParam?name=haha">RequestParam</a>
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam("name") String username){
System.out.println("完成了");
System.out.println(username);
return "success";
}
@RequestBody
用于获取请求体的内容,直接使用得到的是k=v&k=v这样结构的数据,get请求方式不适用
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String body){
System.out.println("完成了");
System.out.println(body);
return "success";
}
@PathVaribale
用于绑定url占位符。例如url中/delete/{id},这个{id}就是url占位符
属性:
- value:用于指定url占位符名称
- required:是否必须提供占位符
@RequestMapping("/testPathVaribale/{sid}")
public String testPathVaribale(@PathVariable(value = "sid") String id){
System.out.println("完成了");
System.out.println(id);
return "success";
}
<a href="anno/testPathVaribale/10">testPathVaribale</a>
@RequestHeader
用于获取请求消息头。
@RequestMapping("/testRequestHeader")
public String testRequestHeader (@RequestHeader("Accept") String header){
System.out.println("完成了");
System.out.println(header);
return "success";
}
@CookieValue
用于把制定cookie名称的值传入控制器方法参数
@RequestMapping("/testCookieValue")
public String testCookieValue (@CookieValue("JSESSIONID") String cookieValue){
System.out.println("完成了");
System.out.println(cookieValue);
return "success";
}
@ModelAttribute
- 出现在方法上,表示当前方法会在控制器的方法执行之前先执行。他可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。
- 出现在参数上,获取制定的数据给参数赋值。
属性value:用于获取数据的key。key可以是POJO的属性名称,也可以是map结构的key
出现在方法上:
@RequestMapping("/testModelAttribute")
public String testModelAttribute (){
System.out.println("testModelAttribute方法执行了");
return "success";
}
@ModelAttribute
public void showUser(){
System.out.println("showUser方法执行了");
}
可以用在提前用其中的属性查询数据库,如有名字年龄生日,可拿名字先查,当要用到时,倘若没传,则用数据库中查询到的
@RequestMapping("/testModelAttribute")
public String testModelAttribute (User user){
System.out.println("testModelAttribute方法执行了");
System.out.println(user);
return "success";
}
@ModelAttribute
public User showUser(String uname){
System.out.println("showUser方法执行了");
//通过名字查询数据库(模拟)
User user=new User();
user.setUname(uname);
user.setAge(20);
user.setDate(new Date());
return user;
}
属性上的用法,倘若方法没有返回值,则:
@RequestMapping("/testModelAttribute")
public String testModelAttribute (@ModelAttribute("abc") User user){
System.out.println("testModelAttribute方法执行了");
System.out.println(user);
return "success";
}
@ModelAttribute
public void showUser(String uname, Map<String,User> map){
System.out.println("showUser方法执行了");
//通过名字查询数据库(模拟)
User user=new User();
user.setUname(uname);
user.setAge(20);
user.setDate(new Date());
map.put("abc",user);
}
@SessionAttribute
用于多次执行控制器方法间的参数共享。
@RequestMapping("/testSessionAttribute")
public String testSessionAttribute (Model model){
System.out.println("testSessionAttribute完成了");
//底层会存入request域中
model.addAttribute("msg","帅帅");
return "success";
}
在类上加@SessionAttributes会存入session中,传入的参数是一个数组
@SessionAttributes("msg")
public class AnnoController {
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>入门成功</h3>
${sessionScope}
</body>
</html>
额外:ModelMap的get可取值,SessionStatus的setComplete可删除
响应后的返回值
返回值为String
@RequestMapping("/testString")
public String testString(Model model){
System.out.println("testString执行了。。");
User user=new User();
user.setUsername("zhangsan");
user.setPassword("123456");
user.setAge(23);
model.addAttribute("user",user);
return "success";
}
可在jsp中用EL表达式获取
返回值为void
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("testVoid执行了。。");
response.setCharacterEncoding("UTF-8");
/*转发:request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);*/
/*重定向:response.sendRedirect(request.getContextPath()+"/index.jsp");*/
response.setContentType("text/html;charset=UTF-8");
response.getWriter().print("hello");
return;
}
返回值为ModelAndView
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
//创建ModelAndView对象
ModelAndView mv=new ModelAndView();
System.out.println("testModelAndView执行了。。");
User user=new User();
user.setUsername("zhangsan");
user.setPassword("123456");
user.setAge(23);
//把user对象传入mv对象中,也会把user对象存入到request域中
mv.addObject("user",user);
//跳转到哪个界面
mv.setViewName("success");
return mv;
}
使用forward和redirect进行页面跳转
@RequestMapping("/testForwardOrRedirect")
public String testForwardOrRedirect(){
System.out.println("testForwardOrRedirect执行了。。");
/*转发:return "forward:/WEB-INF/pages/success.jsp";*/
return "redirect:/index.jsp";//重定向
}
响应json数据过滤静态资源
需在springmvc.xml中配置:
<!--告诉前端控制器,以后哪些静态资源不拦截,一般是js、图片、css-->
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/images/**" location="/image/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<script src="js/jquery-3.5.1.min.js"></script>
<script>
$(function (){
$("#btn").click(function (){
$.ajax({
url:"user/testJson",
contentType:"application/json;charset=UTF-8",
data:'{"username":"hehe","password":"123","age":30}',
dataType:"json",
type:"post",
success:function (data){
}
});
});
});
</script>
@RequestMapping("/testJson")
public void testJson(@RequestBody String body){
System.out.println("testJson执行了。。");
System.out.println(body);
}
封装到对象当中
@RequestMapping("/testJson")
public @ResponseBody User testJson(@RequestBody User user){
System.out.println("testJson执行了。。");
System.out.println(user);
//模拟查数据库
user.setUsername("haha");
user.setAge(23);
//做响应
return user;
}
success:function (data){
/*可接受到响应回来的数据*/
alert(data);
alert(data.username);
alert(data.age)
}
文件上传
要求:
- form表单的enctype(表单请求正文的类型)取值必须是:multipart/form-data
- method属性的取值必须是Post
- 提供一个文件选择域
需使用Commons-fileupload组件实现文件上传,需导入fileupload和io的jar包。
<form action="user/fileupload1" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上传"/>
</form>
@RequestMapping("/fileupload1")
public String fileupload1(HttpServletRequest request) throws Exception {
System.out.println("文件上传...");
//使用fileupload组件完成文件上传
//上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
//判断该路径是否存在
File file=new File(path);
if (!file.exists()){
file.mkdirs();
}
//解析request对象,获取上传文件项
DiskFileItemFactory factory=new DiskFileItemFactory();
ServletFileUpload upload=new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
//解析request
List<FileItem> items = upload.parseRequest(request);
for (FileItem item : items) {
//进行判断,当前item对象是否是上传文件项
if (item.isFormField()){
//说明是普通表单项
}else {
//是上传文件项
String filename = item.getName();
//改唯一名称
String uuid = UUID.randomUUID().toString().replace("-", "");
filename=uuid+"_"+filename;
item.write(new File(path,filename));
item.delete();
}
}
return "success";
}
SpringMVC的文件上传
springMVC框架文件上传的原理:
前端控制器接收到request后会传到配置文件解析器中,解析器会进行解析返回一个upload,前端控制器会将upload传到一个方法中,参数为MultipartFile upload,这个upload必须和input标签中name的值一致
在springmvc中配置文件解析器:
<!--配置文件解析器对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--文件最大大小-->
<property name="maxUploadSize" value="10485760">
<property name="defaultEncoding" value="UTF-8"></property></property>
</bean>
方法的改动:
@RequestMapping("/fileupload2")
public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
System.out.println("文件上传...");
//上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
//判断该路径是否存在
File file=new File(path);
if (!file.exists()){
file.mkdirs();
}
//获取文件上传的名称
String filename = upload.getOriginalFilename();
//改唯一名称
String uuid = UUID.randomUUID().toString().replace("-", "");
filename=uuid+"_"+filename;
//完成文件上传
upload.transferTo(new File(path,filename));
return "success";
}
跨服务器的文件上传
会先将图片上传至应用服务器,服务器再传到图片服务器,当需要图片时,会直接去图片服务器找。
需要用到jersey的core和client jar包
需要创建一个新的服务器,要改端口号
@RequestMapping("/fileupload3")
public String fileupload3(MultipartFile upload) throws Exception {
System.out.println("文件上传...");
String path="http://localhost:9090/ch04_upload/";
//获取上传文件的名称
String filename = upload.getOriginalFilename();
String encodingFilename= URLEncoder.encode(filename,"utf-8");
//改唯一名称
String uuid = UUID.randomUUID().toString().replace("-", "");
filename=uuid+"_"+filename;
//创建客户端对象
Client client = Client.create();
//和图片服务器进行连接
WebResource webResource = client.resource(path + encodingFilename);
//上传文件
webResource.put(upload.getBytes());
return "success";
}
SpringMVC异常处理
配置一个异常处理器处理异常
- 编写一个自定义异常的类(做提示信息)
- 编写异常处理器
- 配置异常处理器(跳转到提示页面)
自定义异常类:
public class SysException extends Exception{
private String message;
public SysException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
编写异常处理器
public class SysExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
//获取异常对象
SysException ex=null;
if (e instanceof SysException){
ex= (SysException) e;
}else {
ex=new SysException("系统正在维护。。。");
}
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject("errorMsg",ex.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}
配置异常处理器
<bean id="sysExceptionResolver" class="cn.huangyy.exception.SysExceptionResolver"/>
拦截器
过滤器和拦截器的区别
- 过滤器是servvlet规范中的一部分,任何javaweb工程都可以使用
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
- 过滤器在url-pattern中配置/*之后又,会对所有要访问的资源拦截
- 拦截器只会拦截访问的控制器方法,如果访问的是jsp、html、css、image或者js是不会进行拦截的
自定义拦截器,要求必须实现:HandlerInterceptor接口
步骤:
- 编写拦截器类,实现HandlerInterceptor接口
- 配置拦截器
public class MyInterceptor1 implements HandlerInterceptor {
//预处理,controller方法执行前
//return true 放行,执行下一个拦截器,如果没有,执行方法
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器执行了...前");
return true;
}
//后处理方法
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器执行了...后");
}
//success页面执行后,该方法会执行
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器执行了...最终");
}
}
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--要拦截的具体方法-->
<mvc:mapping path="/user/*"/>
<!--不拦截的方法-->
<!--<mvc:exclude-mapping path=""/>-->
<!--配置拦截器对象-->
<bean class="cn.huangyy.interceptor.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
再配一个也一样