MVC的核心思想就是业务数据抽取同业务数据呈现相分离
view:是视图层,为用户提供UI,重点关注数据的呈现。
model:模型层,是业务数据的信息表示,关注支撑业务的信息构成,通常是多个业务实体的组合。
controller:是控制层,通过调用业务逻辑产生合适的数据(model),同时将数据传递给视图层用于呈现。
- MVC是一种架构模式,是程序分成,分工合作,既相互独立,有协同工作。
- MVC是一种思考方式,模型层思考为用户展现什么,在视图层思考如何布局,在控制层思考调用那些业务逻辑。
控制器:负责业务数据的抽取
视图模板:负责页面呈现
前端控制器:负责分发调度
用户通过http协议发送请求到前端控制器,前端控制器根据用户的请求到controller处理数据,返回处理结果到前端控制器,前端控制器将数据分发给业务视图,由业务视图呈现最终的用户页面,返回给前端控制器,在呈现给用户。
SpringMVC的基本概念
名称 | 作用 |
---|---|
DispatcherServlet | 前端控制器,分发到合适的controller来生产我们需要的model model通过DispatcherServlet来传递给我们的View |
Controller | 就是MVC中的C,调用业务逻辑生成model的地方 |
HandlerAdapter | 是DispatcherServlet中得一个类,是Controller的一个表现形式 DispatcherServlet实际调用的Controller其实是以handler(Handler是DispatcherServlet调用Controller的中间过渡对象)形式出现的HandlerAdapter(适配器模式),就是把各种不同的Handler适配成DispatcherServlet可以使用的Handler |
HandlerInterceptor | 拦截器,该接口提供after、postHandle、preHandle三个方法,调用controller前后使用 |
HandlerMapping | 前端控制器与controller映射关系的类 |
HandlerExecutionChain | preHandle->Controller method->postHandle->afterCompletion的执行链 |
ModelAndView | model的具体表现,有model和map两种处理方式 |
viewResolver | 视图解析器,决定需要用哪个视图来进行视图的呈现 |
view | 响应页面的呈现 |
SpringMVC的动态概念
1.用户发送请求request
2.DispatcherServlet拦截request 就需要找到一个Controller 如何找?
3.DispatcherServlet就把功能代理给了HandlerMapping
4.HandlerMapping根据配置 通过annotation找到Controller也找到了HandlerInterceptor
形成了一个HandlerExcutionChain 作为一个Handler或者是HandlerAdapter返回给DispatcherServlet
5.DispatcherServlet调用这个Handler或者是HandlerAdapter(找到Controller,Controller目的:生成ModelAndView) 生成ModelAndView 并返回给DispatcherServlet
6.DispatcherServlet调用ViewResolver(视图解析器)方法 返回View对象给DispatcherServlet
7.DispatcherServlet调用ModelAndView作为模型数据传递给我们的View完成了呈现 返回给DispatcherServlet
8.DispatcherServlet再把视图响应给用户
注意事项
1.DispatcherServlet,HandlerMaping 只需要配置
2.Handler/HandlerAdapter忽略掉
3.Controller需要自己写
4.HandlerInterceptor(Controller调用前后干点啥)是一个接口,需要就写不需要就不用实现
5.ModelAndView:只是一个类,也可以使用Map把需要的放进去就好了
6.ViewResolver和HandlerMapping差不多只需要知道使用那种类型就可以了
7.view:不需要管具体的呈现。只需要管是哪一个就好了
eg:一个简单的登录页面,服务器使用jetty
maven配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lmr.springmvc</groupId>
<artifactId>SpringMVC</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>SpringMVC</name>
<url>http://maven.apache.org</url>
<properties>
<commons-lang.version>2.6</commons-lang.version>
<slf4j.version>1.7.6</slf4j.version>
<spring.version>4.1.3.RELEASE</spring.version>
<jackson.version>2.5.4</jackson.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>${commons-lang.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.2.2.v20140723</version>
<configuration>
<httpConnector>
<port>9090</port>
</httpConnector>
<webAppConfig>
<!--浏览器网址是否需要输入包名-->
<contextPath>/${project.artifactId}</contextPath>
<!-- <contextPath>/</contextPath> -->
</webAppConfig>
</configuration>
</plugin>
</plugins>
</build>
</project>
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Spring MVC</display-name>
<!-- 配置Spring框架配置的XML -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext*.xml</param-value>
</context-param>
<!-- DispatcherServlet, Spring MVC的核心 -->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- DispatcherServlet对应的上下文配置, 默认为/WEB-INF/$servlet-name$-servlet.xml -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc-dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<!-- mvc-dispatcher拦截所有的请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
mvc-dispatcher-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" 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:annotation-config />
<!-- DispatcherServlet上下文, 只管理@Controller类型的bean, 忽略其他型的bean, 如@Service -->
<context:component-scan base-package="com.lmr.springmvc">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- HandlerMapping, 无需配置, Spring MVC可以默认启动。 DefaultAnnotationHandlerMapping annotation-driven HandlerMapping -->
<!-- 扩充了注解驱动,可以将请求参数绑定到控制器参数 -->
<mvc:annotation-driven />
<!-- 配置ViewResolver。 可以用多个ViewResolver。 使用order属性排序。InternalResourceViewResolver放在最后。 -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
<entry key="htm" value="text/html" />
</map>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<!-- 视图渲染,并给返回的路径添加前缀和后缀 -->
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
applicationContext.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
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:annotation-config></context:annotation-config>
<context:component-scan base-package="com.lmr.springmvc">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
</beans>
Controller
1.基本配置
如果不基于注解:该类需要继承 CommandController
如果基于注解:在类名前加上 @controller
并使用@RequestMapping(”/xxxxx请求路径”)来指定请求路径
@Controller
@RequestMapping("/login")
public class LoginController {
//方法级别的RequestMapping, 限制并缩小了URL路径匹配,同类级别的标签协同工作,最终确定拦截到的URL由那个方法处理
@RequestMapping("/view")
public String login() {
//视图渲染,根目录下/login.jsp
return "login";
}
}
2.含有请求参数,使用Model传递参数到前台页面,处理/login/result1?id=123&pw=456 形式的URL
@RequestMapping(value = "/result1", method = RequestMethod.GET)
public String login1(@RequestParam("id") String id, @RequestParam("pw") String pw, Model model) {
System.out.println("login1 " + id + " - - " + pw);
LoginBean loginbean = new LoginBean(id, pw);
// model.addAttribute("loginbean", loginbean);
// 生成名字的规则是使用对象的类名的小写模式来作model名字。当这个model是集合或数组的时候,使用集合的第一个元素的类名加s来作model的名字。
model.addAttribute(loginbean);
return "loginsuccess";
}
3.含有请求参数,使用Map传递参数到前台页面
@RequestMapping(value = "/result2", method = RequestMethod.GET)
public String login2(@RequestParam("id") String id, @RequestParam("pw") String pw, Map<String, Object> model) {
System.out.println("login2 " + id + " - - " + pw);
LoginBean loginbean = new LoginBean(id, pw);
model.put("loginbean", loginbean);
return "loginsuccess";
}
4.使用Restful风格的URL ,须使用@PathVariable注解获取参数,处理/login/result3/123 形式的URL
@RequestMapping(value = "/result3/{id}/", method = RequestMethod.GET)
public String login3(@PathVariable("id") String id, Map<String, Object> model) {
System.out.println("login3 " + id);
LoginBean loginbean = new LoginBean(id, "159258357456");
model.put("loginbean", loginbean);
return "loginsuccess";
}
5.使用传统的HttpServletRequest,并用其获取参数,写入资源
@RequestMapping(value = "/result4")
public String login4(HttpServletRequest request) throws UnsupportedEncodingException {
String id = request.getParameter("id");
String pw = request.getParameter("pw");
System.out.println("login4 " + id + " - - " + pw);
LoginBean loginbean = new LoginBean(id, pw);
request.setAttribute("loginbean", loginbean);
request.setAttribute("msg", "这是Controller的msg");
request.getSession().setAttribute("user", id);
return "loginsuccess";
}
6.使用ModelAndView作为返回值,在ModelAndView中可配置参数,视图等
@RequestMapping(value = "/result5")
public ModelAndView login5(@RequestParam("id") String id, @RequestParam("pw") String pw) {
System.out.println("login5 " + id + " - - " + pw);
LoginBean loginbean = new LoginBean(id, pw);
ModelAndView mv=new ModelAndView();
mv.addObject(loginbean);
mv.setViewName("loginsuccess");
return mv;
}
7.value值可为空,可附加params属性,处理/score?add 形式的URL
@Controller
@RequestMapping("/score")
public class ScoreController {
@RequestMapping(value="", method=RequestMethod.GET, params="add" )
public String edit(){
return "score/add";
}
}
8.使用Bean类来获取前台页面提交的参数,前提是要配置相应的Bean类,而且Bean类中的参数名要与前台页面标签中name属性的相同
//SoreBean类
public class ScoreBean {
private String sname;
private String sid;
private String sgrade;
private int chinese;
private int math;
private int english;
public ScoreBean() {
super();
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getSgrade() {
return sgrade;
}
public void setSgrade(String sgrade) {
this.sgrade = sgrade;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
public int getEnglish() {
return english;
}
public void setEnglish(int english) {
this.english = english;
}
@Override
public String toString() {
return "ScoreBean [sname=" + sname + ", sid=" + sid + ", sgrade=" + sgrade + ", chinese=" + chinese + ", math="
+ math + ", english=" + english + "]";
}
}
<!--前台页面-->
<body>
<form action="<%= request.getContextPath()%>/score/save1" method="post">
姓名:<input id="sname" name="sname" type="text"> <br> <br>
学号:<input id="sid" name="sid" type="text"> <br> <br>
班级:<input id="sgrade" name="sgrade" type="text"> <br> <br>
语文:<input id="chinese" name="chinese" type="text"> <br> <br>
数学:<input id="math" name="math" type="text"> <br> <br>
英语:<input id="english" name="english" type="text"> <br> <br>
<button type="submit">添加</button>
<button type="reset">重置</button>
</form>
</body>
@RequestMapping(value="/save", method=RequestMethod.POST)
public String save(ScoreBean scoreBean,Model model){
System.out.println(scoreBean.toString());
model.addAttribute(scoreBean);
return "score/result";
}
9.也可使用变量名直接获取前台页面的值,前提是该变量名要与前台页面标签中name属性的相同
@RequestMapping(value="/save1", method=RequestMethod.POST)
public String save1(Map<String, Object> model,String sname, String sid, String sgrade, int chinese, int math, int english){
System.out.println(sname+" - - "+sid+" - - "+sgrade+" - - "+chinese+" - - "+math+" - - "+english);
model.put("sname", sname);
model.put("sid", sid);
model.put("sgrade", sgrade);
model.put("chinese", chinese);
model.put("math", math);
model.put("english", english);
// return "score/result";
//转发forward 重定向redirect
return "forward:/score/save2";
}
SpringMVC使用ViewResovler来处理含义相同格式不同(json、xml、html)的数据,ViewResolver将最终数据格式根据需要转化为JSPView或者JsonView
在mvc-dispatcher-servlet.xml中配置ViewResolver,引入org.springframework.web.servlet.view.json.MappingJackson2JsonView
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
<entry key="htm" value="text/html" />
</map>
</property>
<property name="defaultViews">
<list>
<!-- JSON View -->
<bean
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView">
</bean>
</list>
</property>
<property name="ignoreAcceptHeader" value="true" />
</bean>
1.使用@ResponseBody方式返回JSON数据
@RequestMapping(value="/json1", method=RequestMethod.GET)
public @ResponseBody List<ScoreBean> list1(){
return scoreService.query();
}
@Service("scoreService")
public class ScoreService {
public List<ScoreBean> query(){
List<ScoreBean> scorelist=new ArrayList<>();
for(int i=0;i<10;i++){
ScoreBean scoreBean=new ScoreBean();
Random rand=new Random();
scoreBean.setSid((char)(rand.nextInt(26)+97)+"");
scoreBean.setSname((char)(rand.nextInt(26)+97)+"");
scoreBean.setSgrade((char)(rand.nextInt(26)+97)+"");
scoreBean.setChinese(rand.nextInt(100));
scoreBean.setMath(rand.nextInt(100));
scoreBean.setEnglish(rand.nextInt(100));
scorelist.add(scoreBean);
}
return scorelist;
}
}
结果:返回一组随机生成的scorelist的JSON数据
[{"sname":"r","sid":"h","sgrade":"q","chinese":14,"math":39,"english":41},
{"sname":"l","sid":"i","sgrade":"i","chinese":68,"math":26,"english":14},
{"sname":"s","sid":"v","sgrade":"p","chinese":4,"math":19,"english":46},
{"sname":"k","sid":"r","sgrade":"x","chinese":90,"math":74,"english":21},
{"sname":"e","sid":"c","sgrade":"x","chinese":32,"math":47,"english":9},
{"sname":"b","sid":"r","sgrade":"s","chinese":91,"math":97,"english":78},
{"sname":"y","sid":"w","sgrade":"q","chinese":81,"math":76,"english":77},
{"sname":"q","sid":"z","sgrade":"c","chinese":38,"math":39,"english":78},
{"sname":"j","sid":"i","sgrade":"n","chinese":47,"math":97,"english":87},
{"sname":"x","sid":"x","sgrade":"l","chinese":46,"math":22,"english":47}]
2.使用ResponseEntity方式返回JSON数据
@RequestMapping(value="/json2", method=RequestMethod.GET)
public ResponseEntity<List<ScoreBean>> list2(){
return new ResponseEntity<List<ScoreBean>>(scoreService.query(), HttpStatus.OK);
}
文件上传
在mvc-dispatcher-servlet.xml中配置multipartResolver
<!-- maxUploadSize:最大上传大小(200*1024*1024即200M);defaultEncoding:默认编码;resolveLazily:是否延迟加载文件解析(若启用可推迟文件解析,以便捕获文件大小异常) -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="209715200" />
<property name="defaultEncoding" value="UTF-8" />
<property name="resolveLazily" value="true" />
</bean>
前端页面中文件上传的form表单必须的属性:enctype=”multipart/form-data”
<body>
<center>
<form action="<%=request.getContextPath() %>/file/load1" method="post" enctype="multipart/form-data">
<input type="file" id="fileload" name="fileload">
<input type="submit" value="上传">
</form>
</center>
</body>
1.使用@RequestParam(“fileload”)MultipartFile file 请求参数,获取前端页面传来的文件
@RequestMapping(value="/load", method=RequestMethod.POST)
public String load(@RequestParam("fileload")MultipartFile file,Model model) throws IOException{
System.out.println(file.getOriginalFilename());
FileUtils.copyInputStreamToFile(file.getInputStream(), new File("D:\\Text\\", System.currentTimeMillis()+ file.getOriginalFilename()));
model.addAttribute("filename", file.getOriginalFilename());
return "file/result";
}
2.使用MultipartHttpServletRequest获取前端页面传来的文件
@RequestMapping(value="/load1", method=RequestMethod.POST)
public String load1(MultipartHttpServletRequest multipartRequest,Model model) throws IOException{
Map<String,MultipartFile> filemap=multipartRequest.getFileMap();
for(Entry<String, MultipartFile> en:filemap.entrySet()){
System.out.println(en.getKey()+" - - "+en.getValue().getOriginalFilename());
FileUtils.copyInputStreamToFile(en.getValue().getInputStream(), new File("D:\\Text\\", System.currentTimeMillis()+ en.getValue().getOriginalFilename()));
}
return "file/result";
}