简介
从前的网页程序是将业务代码嵌入到JSP页面中,耦合性较高。
后来将前后端的代码分离后,采用MVC架构,M:模型,负责数据模型的控制,V:视图,负责视图的展示,C:控制器,负责将数据模型放到相应的视图中渲染。
请求过程
┌─────────┐
↗│ 处理器映射│
2/ └─────────┘
/ 3
请求 1 ┌─────────────────┐ --------------------> ┌─────┐
----->│DispatcherServlet│ <--┌─────────────┐ 4 │控制器│
└─────────────────┘ │模型及逻辑视图名│ ---│ │
\ \ └─────────────┘ └─────┘
\ \5 ┌────────┐
6\ --->│视图解析器│
↘ └────────┘
响应 7 ┌────┐
<----------------------│视图 │
└────┘
过程:
- 请求:请求离开浏览器时,会带有用户所请求内容的信息,至少会包含请求的URL。(还可能带有如表单信息)
- DispatcherServlet:将请求发送给Spring MVC控制器(controller)
配置DispatcherServlet
可继承AbstractAnnotationConfigDispatcherServletInitializer
实现。
AbstractAnnotationConfigDispatcherServletInitializer
| |
创建 创建
| |
v v
DispatcherServlet ContextLoaderListener
| |
加载 加载
| |
v V
应用上下文 应用上下文
(查找@Configuration注解的类, (查找@Configuration注解的类,
加载包含Web组件的bean。 加载应用中的其它bean。
如控制器,视图解析器,处理器映射等) 如自定义的业务所需的bean)
控制器
控制器注解:
@Controller
是基于@Component
的注解,效果是一样的。但在MVC中使用@Component
在表意上可能会差一些。
映射注解:
@RequestMapping
指定了要处理请求的路径。
可以放在类上,或方法上。放在类上设定映射的路径的话,其方法还需要此注解指定请求的方式。
同一个方法或类路径的映射可以有多个。
返回值-视图名:
返回的字符串,将会被Spring MVC解读为要渲染的视图名称。DispatcherServlet
会要求视图解析器将这个逻辑名称解析为实际的视图。
InternalResourceViewResolver
能识别返回的字符前缀带有redirect:
或forward:
,分别是重定向,前往。
模型参数:
参数中的传递数据的模型有3种:Model
,ModelMap
,ModelAndView
。
其关系如下。
public class ExtendedModelMap extends ModelMap implements Model{}
ModelAndView
相比于其它而言,可以设置渲染的视图名称。
模型调用addAttribute()
方法不指定key的时候,key会根据对象的类型推断确定。
可以是Map
类型做模型。
甚至可以直接返回列表,当处理器返回对象或集合时,这个值会被放到模型中,模型的key会根据其类型推断得出。而逻辑视图的名词会根据请求路径推断得出。
当视图是JSP时,模型数据会作为请求属性放到请求中。
模型
可以对模型的属性添加注解实现校验。还需要在控制器传递参数前加@Valid
注解,并后紧接着
附加参数Errors
获取异常。
Java校验注解:@AssertFalse, @AssertTrue, @DecimalMax, @DecimalMin, @Digits, @Future, @Max, @Min, @NotNull, @Null, @Past, @Pattern, @Size。
示例
- 基本MVC程序
- 接受请求的输入
-
- 路径参数
-
- 表单参数
基本MVC程序
xml配置DispatcherServlet
项目结构
.
├── build.gradle
└── src
└── main
├── java
│ └── com
│ └── yww
│ ├── HomeController.java
│ └── Message.java
├── resources
│ └── application.properties
└── webapp
├── index.jsp
└── WEB-INF
├── applicationContext.xml
├── dispatcher-servlet.xml
├── jsp
│ └── home.jsp
└── web.xml
代码
build.gradle
plugins {
id 'java'
id 'war'
}
group = 'com.yww'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8
ext{
springVersion = '5.2.0.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
compile "org.springframework:spring-core:$springVersion"
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework:spring-beans:$springVersion"
compile "org.springframework:spring-expression:$springVersion"
compile "org.springframework:spring-aop:$springVersion"
compile "org.springframework:spring-aspects:$springVersion"
compile "org.springframework:spring-web:$springVersion"
compile "org.springframework:spring-webmvc:$springVersion"
testCompile "junit:junit:4.12"
}
web.xml
:配置DispatcherServlet
及其映射路径,ContextLoaderListener
(加载bean的任意命名的xml文件),bean的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>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!-- <url-pattern>*.form</url-pattern>-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
dispatcher-servlet.xml
:命名为xxx-servlet.xml格式的文件会被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"
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.yww"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
applicationContext.xml
:配置了ContextLoaderListener
后,可以加载bean的任意命名的xml文件,无需xxx-servlet.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">
<bean id="msg1" class="com.yww.Message">
<property name="msg" value="mvc - xml config"/>
</bean>
</beans>
HomeController.java
package com.yww;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.beans.factory.annotation.Autowired;
@Controller
@RequestMapping("/home")
public class HomeController {
@Autowired
public Message msg1;
@RequestMapping(method = RequestMethod.GET)
public String printHome(Model model){
String str1 = msg1.getMsg();
model.addAttribute("msg", "this is a page! gradle " + str1);
return "home";
}
}
Message.java
package com.yww;
public class Message {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = "this mssage is : " + msg;
}
}
index.jsp
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
home.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>${msg}<h1>
</body>
</html>
启动
- Project Structe -> Artifacts -> + -> WebApplication:Exploded -> From Modules… -> OK
- Project Structe -> Artifacts -> + -> WebApplication:Archive -> For ‘xxx:war exploded’ -> OK
- Edit Configurations… -> + -> Tomcat Server -> local -> Deployment -> + Artifact… -> OK
- run(shift + F10)
java配置DispatcherServlet
项目结构
.
├── build.gradle
└── src
└── main
├── java
│ └── com
│ └── yww
│ ├── config
│ │ ├── MyWebAppInitializer.java
│ │ ├── RootConfig.java
│ │ └── WebConfig.java
│ ├── HomeController.java
│ └── Message.java
├── resources
│ └── application.properties
└── webapp
├── index.jsp
└── WEB-INF
├── jsp
│ └── home.jsp
└── web.xml
代码
build.gradle
:不同于之前的,java配置DispatcherServlet,需要添加servlet-api
库。
plugins {
id 'java'
id 'war'
}
group = 'com.yww'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8
ext{
springVersion = '5.2.0.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
compile "org.springframework:spring-core:$springVersion"
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework:spring-beans:$springVersion"
compile "org.springframework:spring-expression:$springVersion"
compile "org.springframework:spring-aop:$springVersion"
compile "org.springframework:spring-aspects:$springVersion"
compile "org.springframework:spring-web:$springVersion"
compile "org.springframework:spring-webmvc:$springVersion"
testCompile "junit:junit:4.12"
providedCompile 'javax.servlet:javax.servlet-api:4.0.1'
}
web.xml
:什么也没有配置,全使用java配置了。
<?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">
</web-app>
MyWebAppInitializer.java
package com.yww.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{ RootConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() { // 指定配置类
return new Class[]{ WebConfig.class };
}
@Override
protected String[] getServletMappings() { // 将DispatcherServlet映射到"/"
return new String[]{ "/" };
}
}
WebConfig.java
package com.yww.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc // 启用Spring MVC
//@ComponentScan("com.yww")
@ComponentScan // 启用组件扫描,只会扫描当前包下的配置
public class WebConfig extends WebMvcConfigurationSupport {
// 配置JSP视图解析器
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
// 配置静态资源的处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
configurer.enable();
}
}
RootConfig.java
package com.yww.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan(
basePackages = {"com.yww"},
excludeFilters = {@Filter(type= FilterType.ANNOTATION, value = EnableWebMvc.class)}
)
public class RootConfig {
}
HomeController.java
(同上)
Message.java
(同上)
index.jsp
(同上)
home.jsp
(同上)
接受请求的输入
Spring MVC允许以多种方式将客户端中的数据传送到控制器的处理器方法中:
- 查询参数(Query Parameter)
- 表单参数(Form Parameter)
- 路径变量(Path Variable)
项目结构
.
├── build.gradle
└── src
└── main
├── java
│ └── com
│ └── yww
│ ├── dao
│ │ └── User.java
│ ├── FormParamController.java
│ ├── PathParamController.java
│ └── QueryParamController.java
├── resources
│ └── application.properties
└── webapp
├── index.jsp
└── WEB-INF
├── applicationContext.xml
├── dispatcher-servlet.xml
├── jsp
│ ├── form.jsp
│ └── home.jsp
└── web.xml
代码
User.java
package com.yww.dao;
public class User {
private int id;
private String name;
private String passwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
QueryParamController.java
package com.yww;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class QueryParamController {
@RequestMapping(value = "/query_param", method = RequestMethod.GET)
public String printHome(
Model model,
@RequestParam int id,
@RequestParam(value = "name", defaultValue = "yww") String username,
@RequestParam(value = "count", defaultValue = "10") long count // 尽管defaultValue属性给定的时String类型的值,但当绑定到方法的count参数时,会自动转换为long类型。
){
String str1 = " query - id:" + id + " username:" + username + " count:" + count;
model.addAttribute("msg", "this is a page! " + str1);
return "home";
}
}
PathParamController.java
package com.yww;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class PathParamController {
@RequestMapping(value = "/path_param/{id}", method = RequestMethod.GET)
public String printHome(
Model model,
@PathVariable("id") int id
){
String str1 = " path - id:" + id;
model.addAttribute("msg", "this is a page! " + str1);
return "home";
}
}
FormParamController.java
package com.yww;
import com.yww.dao.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.springframework.web.bind.annotation.RequestMethod.*;
@Controller
public class FormParamController {
@RequestMapping(value = "/form_param", method = GET)
public String showForm(){
return "form";
}
@RequestMapping(value = "/form_param", method = POST)
public String postForm(
Model model,
User user
){
model.addAttribute("msg", "this is a page! form : " + user.getName());
return "home";
}
}
form.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post">
id : <input type="text" name="id" /> </br>
name : <input type="text" name="name" /> </br>
password : <input type="text" name="passwd" /> </br>
<input type="submit" value="login" />
</form>
</body>
</html>
启动
查询参数(Query Parameter):http://localhost:8080/mvc_param/query_param?id=1001&name=megumin&count=7
路径变量(Path Variable):http://localhost:8080/mvc_param/path_param/1001
表单参数(Form Parameter):http://localhost:8080/mvc_param/form_param
PS
注:xml配置文件
命名为xxx-servlet.xml格式的文件会被spring mvc自动加载(如:dispatcher-servlet.xml)。
配置了ContextLoaderListener
后,可以加载bean的任意命名的xml文件,无需xxx-servlet.xml格式(如:applicationContext.xml)。
错误:class file for javax.servlet.ServletException not found
java配置DispatcherServlet
,需要在build.gradle
添加库:
`providedCompile 'javax.servlet:javax.servlet-api:4.0.1'`
注:表单提交路径
<form>
标签中没有设置action属性的话,当表单提交时,它会提交到与展现时相同的url路径上。