表现层
web层。负责接收客户端请求,向客户端返回相应结果。
业务层
service层。负责处理业务逻辑。表现层依赖业务层,但业务层不依赖表现层。
持久层
dao层。负责数据持久化。和数据库进行交互。
MVC:Model View Controller。是一种用于设计创建 Web 应用程序的表现层模式。
Model:模型。包括业务模型和数据模型。业务模型用于封装业务数据,数据模型用于处理业务数据。
View:视图。用于展示数据。一般依赖于模型数据创建。
Controller:控制器。用于处理程序逻辑。
每一层都编写自己的东西,不编写任何其他代码。分层是为了解耦,解耦是为了维护方便和分工协作。
Spring MVC:Spring Web MVC。是基于 Java 实现 MVC 设计模式的轻量级 web 框架。是 Spring FrameWork 的后续产品。本质可以认为是对 Servlet 的封装,简化了 Servlet 的开发。
组件名 | 描述 |
---|---|
HandlerMapping 处理器映射器 | 在请求到达后,查找请求相对应的 Handler(处理器)+ Interceptor(拦截器)。 标注了 @RequestMapping 的每个⽅法都可以看成是⼀个 Handler。 |
HandlerAdapter 处理器适配器 | 让固定的 Servlet 方法调用 Handler 来进行处理。 Servlet 的方法结构都是 doService(HttpServletRequest req,HttpServletResponse resp) 形式的。 Spirng MVC 的 Handler 可以是任意形式的,只要能处理请求即可。 |
HandlerExceptionResolver 处理程序异常解析器 | ⽤于处理 Handler 产⽣的异常情况。 根据异常设置 ModelAndView,之后交给渲染方法进行渲染。 |
ViewResolver 视图解析器 | Controller 层返回的 String 类型视图名 viewName 会被解析成 View 对象。找到渲染所用的模板 和 所用的技术 并填入参数。spring MVC 会默认配置一个 InternalResourceViewResolver 用于解析 JSP 视图。 |
RequestToViewNameTranslator 请求视图名称转换器 | 由于有的 Handler 没有设置 ViewName,ViewResolver 根据 ViewName 查询不到 View 。从请求中获取 viewName。 |
LocalResolver | 区域设置解析器 |
ThemeResolver | 主题解析器 |
MultipartResolver | 将普通请求包装成 MultipartHttpServletRequest 。使其拥有上传文件的功能。 |
FlashMapManager | 用于重定向时的参数传递。 |
http 协议:超文本传输协议。
原生 Servlet 接受一个整型的参数:
String ageStr = request.getParameter("age");
Integer age = Integer.parseInt(ageStr);
Spring MVC 对 Servlet 进行了封装,简化了 Servlet 的许多操作。
pom.xml
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
web.xml
<servlet>
<servlet-name>springMvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>springMvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
applicationContext.xml
<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
https://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="com.demo"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
bean>
beans>
@RequestMapping("/handle01")
// 返回 ModelAndView;Model 里面数数据模型;View 是需要跳转的页面地址
public ModelAndView handle01() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date", new Date());
modelAndView.setViewName("success");
return modelAndView;
}
// ModelMap;Model;Map;最终都会转换为==>BindingAwareModelMap
/**
* 继承体系如下:
* Model ——> ExtendedModelMap ——> BindingAwareModelMap
* Map ——> ModelMap ——> ExtendedModelMap ——> BindingAwareModelMap
*/
@RequestMapping("/handle11")
public String handle11(ModelMap modelMap) {
modelMap.addAttribute("date", new Date());
return "success";
}
@RequestMapping("/handle12")
public String handle12(Model model) {
model.addAttribute("date", new Date());
return "success";
}
@RequestMapping("/handle13")
public String handle13(Map<String, Object> map) {
map.put("date", new Date());
return "success";
}
@RequestMapping("/handle14")
public String handle14(ExtendedModelMap extendedModelMap) {
extendedModelMap.addAttribute("date", new Date());
return "success";
}
@RequestMapping("/handle15")
public String handle15(BindingAwareModelMap bindingAwareModelMap) {
bindingAwareModelMap.addAttribute("date", new Date());
return "success";
}
// 对八种基本数据类型的支持
// 除了 boolean 型的推荐用包装类 可以为 null
// 对于 boolean,Boolean 0 —— false;1 —— true;
// 对于 String 直接 传就行了
// 127.0.0.1:8080/demo/handle03?hello=127&s=60&id=1&uid=2&money=3&age=4&letter=0&flag=1
@RequestMapping("/handle03")
public String handle03(@RequestParam(value = "hello", required = false) Byte b, Short s, Integer id, Long uid, Double money, Float age, Character letter, Boolean flag) {
System.out.println(b);
System.out.println(s);
System.out.println(id);
System.out.println(uid);
System.out.println(money);
System.out.println(age);
System.out.println(letter);
System.out.println(flag);
return "success";
}
@RequestMapping("/handle02")
// 对原生 servlet 的支持
public String handle02(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
request.setAttribute("date", new Date());
return "success";
}
// 对 pojo 类型的支持
// 如果传两个对象 属性名相同的会同时赋值 属性名不同的会单独赋值
// 127.0.0.1:8080/demo/handle04?user.id=1&username=myName&otherAttribute=1233
@RequestMapping("/handle04")
public String handle04(User user, Substance substance) {
System.out.println(user.getId());
System.out.println(user.getUsername());
System.out.println(substance.getId());
System.out.println(substance.getUsername());
System.out.println(substance.getOtherAttribute());
return "success";
}
// 对 pojo 里面包含 pojo 的支持
// 127.0.0.1:8080/demo/handle05?id=1&username=myName&otherAttribute=1233&user.id=2&user.username=username&user.subUser.age=1001
@RequestMapping("/handle05")
public String handle05(Substance substance) {
System.out.println(substance);
return "success";
}
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>
<bean id="conversionServiceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.demo.converter.DateConverter"/>
set>
property>
bean>
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
// 完成字符串向日期的转换
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date parse = simpleDateFormat.parse(source);
return parse;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
@RequestMapping("/handle06")
public String handle06(Date birthday) {
System.out.println(birthday);
return "success";
}
// 对 rest 风格接口的支持
@RequestMapping(value = "/handle/{id}", method = RequestMethod.GET)
public String handleGet(@PathVariable Integer id) {
System.out.println("GET:" + id);
return "success";
}
@RequestMapping(value = "/handle/{id}", method = RequestMethod.POST)
public String handlePost(@PathVariable Integer id) {
System.out.println("POST:" + id);
return "success";
}
@RequestMapping(value = "/handle/{id}", method = RequestMethod.DELETE)
public String handleDelete(@PathVariable Integer id) {
System.out.println("DELETE:" + id);
return "success";
}
@RequestMapping(value = "/handle/{id}", method = RequestMethod.PUT)
public String handlePut(@PathVariable String id) {
System.out.println("PUT:" + id);
return "success";
}
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-coreartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.0version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-annotationsartifactId>
<version>2.9.0version>
dependency>
// 对 JSON 数据的支持
// 需要导入 jackson 的 pom
@RequestMapping("/handle07")
public @ResponseBody User handle07(@RequestBody User user) {
user.setUsername("张三丰");
return user;
}
从配置的角度来看,Servlet、Filter、Listener 是配置在 web.xml 中的。而 Interceptor 是配置在表现层自己的配置文件中。
public class MyInterceptor01 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在 handler 方法业务逻辑执行之前执行
// 返回值 返回值 boolean 代表是否放行,true 代表放行,false 代表中止
System.out.println("MyInterceptor01 preHandle......");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 在 handler 业务逻辑执行之后 尚未跳转页面时执行
System.out.println("MyInterceptor01 postHandle......");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 页面已经跳转渲染完毕之后执行
System.out.println("MyInterceptor01 afterCompletion......");
}
}
<mvc:interceptors>
<bean class="com.demo.interceptor.MyInterceptor01"/>
mvc:interceptors>
MyInterceptor01 preHandle......
MyInterceptor02 preHandle......
POST:1
MyInterceptor02 postHandle......
MyInterceptor01 postHandle......
MyInterceptor02 afterCompletion......
MyInterceptor01 afterCompletion......
pom.xml
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.1version>
dependency>
applicationContext.xml
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">bean>
@RequestMapping("/upload")
public String upload(MultipartFile uploadFile) {
String originalFilename = uploadFile.getOriginalFilename();
try {
uploadFile.transferTo(new File("D:\\learning\\spring-mvc", originalFilename));
} catch (IOException e) {
e.printStackTrace();
}
return "success";
}
@ControllerAdvice
public class GlobalExceptionResolver {
@ExceptionHandler(ArithmeticException.class)
public ModelAndView handleException(ArithmeticException exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", exception.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}
转发:url 不会变,参数也不会丢,一个请求。
重定向:url 会变,参数会丢失,需要重新携带参数。两个请求。
@RequestMapping("/handle99")
public String handle99(String name) {
// 利用 flash 的时候此处拿不到name;因为在页面跳转后值已经销毁了
System.out.println(name);
return "success";
}
@RequestMapping("/handleRedirect")
public String handleRedirect(String name, RedirectAttributes redirectAttributes) {
// return "redirect:handle99?name=" + name + "";
redirectAttributes.addFlashAttribute("name", name);
return "redirect:handle99";
}
利用 RedirectAttributes 设置一个 flash 类型的属性,该属性会暂时被存在 session 中,在页面跳转后进行销毁。
参考git:https://gitee.com/zhangyizhou/learning-spring-mvc-demo.git
1.自定义 mvc 项目:custom-mvc
1.spring mvc demo 项目:spring-mvc-demo