文章内容输出来源:拉勾教育Java高薪训练营;
在B / S架构中,系统标准的三层架构为:表现层 、业务层、持久层。三层架构中,每一层各司其职,共同协作实现对功能的支持与实现。
表现层
表现层也就是web层,负责接收客户端的请求(http / webservice等请求),响应客户端的结果。
表现层包括展示层和控制层:展示层负责将结果展示,控制层负责接收和响应请求。
表现层的设计基本都是使用MVC模型。MVC是表现层的设计模型,和其他层没有关系。
业务层
持久层就是service层,负责具体的业务逻辑处理,web层接收客户端的请求后转交给业务层处理。由于业务层是处理具体的业务逻辑,需要保证数据的一致性等,所以事务都是在业务层控制。
持久层
持久层就是dao层,负责数据的持久化。不负责具体的业务逻辑,只负责对数据的增、删、查、改。
MVC全称是Model View Controller,就是模型 - 视图 - 控制器的缩写。是一种用于设计Web应用程序表现层的模式。
Spring MVC全称是Spring Web MVC,是基于Java的实现MVC设计模式的请求驱动类型的轻量级Web框架,属于Spring FrameWork的后续产品。
Spring MVC已经成为目前最流行的MVC框架之一,并且随着Spring3.0的发布,全面超过Struts2成为最优秀的MVC框架。
servlet / struts开发需要实现接口,Spring MVC可以实现接口Controller,也提供了注解方式,只需要一个注解就可以(简便开发)。Spring MVC提供一整套注解,让一个简单的Java类成为处理请求的控制器,不需要实现任何接口。并且对Restful编程风格提供了支持。
本质 Spring MVC其实就是对Servlet的封装,简化了我们Servlet的开发。
<servlet>
<servlet-name>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>dispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
@Controller
@RequestMapping(value = "/demo")
public class DemoController {
/**
* SpringMVC 项目配置(三大核心组件):
* 1)配置前端控制器:org.springframework.web.servlet.DispatcherServlet(核心)
*
* dispatcherServlet
* org.springframework.web.servlet.DispatcherServlet
*
* contextConfigLocation
* classpath:springmvc.xml
*
*
*
* dispatcherServlet
* /
*
* 2)配置配置视图处理器:
*
*
*
*
* 3)配置映射处理器(不指定,将自动匹配合适的映射处理器):
*
*/
/**
* 数据返回方式一:model和view封装到对象中
* ModelAndView对象内部包含了:ModelMap 和 Object view两个属性,分别用于封装Model数据模型和View视图
* @return
*/
@RequestMapping(value = "/handler01")
public ModelAndView handler01() {
LocalDateTime now = LocalDateTime.now();
// 封装了Model和View视图
ModelAndView modelAndView = new ModelAndView();
// 封装返回结果到Model中
modelAndView.addObject("date", now);
// 设置返回的视图名称
modelAndView.setViewName("success");
return modelAndView;
}
/**
* 探究ModelMap / Model / Map之间的关系,为什么可以将数据存储到域并且被视图解析器读取到???
* class ModelMap extends LinkedHashMap 继承了LinkedHashMap,LinkedHashMap又实现了Map接口
* interface Model 只是一个接口
* interface Map 只是一个接口
*
* ModelMap / Model / Map 在SpringMVC注入的时候打印的都是org.springframework.validation.support.BindingAwareModelMap对象
* 探究 BindingAwareModelMap
* class BindingAwareModelMap extends ExtendedModelMap
* class ExtendedModelMap extends ModelMap implements Model
*
* 探究结果:BindingAwareModelMap继承了ModelMap类,实现了Model接口,最终封装数据的是BindingAwareModelMap
*
*/
/**
* 数据返回方式二:view视图路径直接return,使用ModelMap封装数据
* ModelMap对象:org.springframework.validation.support.BindingAwareModelMap
* @param modelMap
* @return
*/
@RequestMapping(value = "/handler11")
public String handler11(ModelMap modelMap) {
LocalDateTime now = LocalDateTime.now();
modelMap.addAttribute("date",now);
System.out.println("--------------" + modelMap.getClass());
return "success";
}
/**
* 数据返回方式三:view视图路径直接return,使用Model封装数据
* Model对象:org.springframework.validation.support.BindingAwareModelMap
* @param model
* @return
*/
@RequestMapping(value = "/handler12")
public String handler12(Model model) {
LocalDateTime now = LocalDateTime.now();
model.addAttribute("date",now);
System.out.println("--------------" + model.getClass());
return "success";
}
/**
* 数据返回方式四:view视图路径直接return,使用Map封装数据
* Map对象:org.springframework.validation.support.BindingAwareModelMap
* @param map
* @return
*/
@RequestMapping(value = "/handler13")
public String handler13(Map map) {
LocalDateTime now = LocalDateTime.now();
map.put("date",now);
System.out.println("--------------" + map.getClass());
return "success";
}
/**
* BindingAwareModelMap也可以封装数据,因为最终Spring就是通过这个对象将数据放入域中
* @param bindingAwareModelMap
* @return
*/
@RequestMapping(value = "/handler14")
public String handler14(BindingAwareModelMap bindingAwareModelMap) {
LocalDateTime now = LocalDateTime.now();
bindingAwareModelMap.put("date",now);
System.out.println("--------------" + bindingAwareModelMap.getClass());
return "success";
}
}
映射处理器
<context:component-scan base-package="com.zhichunqiu.controller" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
bean>
<mvc:annotation-driven />
<servlet>
<servlet-name>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:springmvc.xmlparam-value>
init-param>
servlet>
HandlerMapping(处理器控制器)
根据url查找对于的Handler和Interceptor,具体的Handler可以是类也可以是方法。
HandlerAdapter(处理器适配器)
由于Spring MVC中Handler可以是任意形式的只要能够处理请求即可。让合适的servlet处理请求这就是适配器的职责。
HandlerExceptionResolver(异常解析器)
用于处理Handler产生的异常信息。
ViewResolver(视图解析器)
RequsetToViewNameTranslator
LocalResolver
ThemeResolver
MultipartResolver
FlashMapManager
简单的基础数据类型:八种基础数据类型及其包装类型,参数类型推荐使用包装类类型,因为基础数据类型不可以为null。
Short / short 、Long / long 、Integer / int 、Float / float 、Double / double 、Char / char 、Boolean / boolean 、String
说明:对于布尔值类型的参数,参数值只能为trur / false / 1 / 0。
注意:绑定基础数据类型,只需要直接声明形参即可。建议请求参数和形参参数名称一致,如不一致使用@RequestParam
注解。
html
<fieldset>
<p>测试用例:SpringMVC 接收简单数据类型参数p>
<a href="/demo/handle03?ids=1">点击测试a>
fieldset>
java
@RequestMapping("/handle03")
public ModelAndView handle03(@RequestParam("ids") Integer id,Boolean flag) {
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
html
<fieldset>
<p>测试用例:SpringMVC接收pojo类型参数p>
<a href="/demo/handle04?id=1&name=zhangsan">点击测试a>
fieldset>
java
/*
* SpringMVC接收pojo类型参数 url:/demo/handle04?id=1&username=zhangsan
* 接收pojo类型参数,直接形参声明即可,类型就是Pojo的类型,形参名无所谓
* 但是要求传递的参数名必须和Pojo的属性名保持一致
*/
@RequestMapping("/handle04")
public ModelAndView handle04(User user) {
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
html
<fieldset>
<p>测试用例:SpringMVC接收pojo包装类型参数p>
<a href="/demo/handle05?user.id=1&user.name=zhangsan">点击测试a>
fieldset>
java
/*
* QueryVo中有User user 属性,需要将请求参数封装到queryVo中
* SpringMVC接收pojo包装类型参数 url:/demo/handle05?user.id=1&user.username=zhangsan
* 不管包装Pojo与否,它首先是一个pojo,那么就可以按照上述pojo的要求来
* 1、绑定时候直接形参声明即可
* 2、传参参数名和pojo属性保持一致,如果不能够定位数据项,那么通过属性名 + "." 的方式进一步锁定数据
*
*/
@RequestMapping("/handle05")
public ModelAndView handle05(QueryVo queryVo) {
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
html
<fieldset>
<p>测试用例:SpringMVC接收日期类型参数p>
<a href="/demo/handle06?birthday=2019-10-08">点击测试a>
fieldset>
java
/**
* 绑定日期类型参数
* 定义一个SpringMVC的类型转换器 接口,扩展实现接口接口,注册你的实现
* @param birthday
* @return
*/
@RequestMapping("/handle06")
public ModelAndView handle06(Date birthday) {
Date date = new Date();ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
/**
* Converter
* S : 是传递进来的数据类型
* T : 是处理后需要返回的数据类型
* @Author zhang yong jun
* @time 2020/6/11 11:25
*/
public class DateConverter implements Converter<String, Date> {
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date convert(String s) {
Date parse = null;
try {
parse = SDF.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
}
}
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionServiceBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.zhichunqiu.converter.DateConverter">bean>
set>
property>
bean>
Restful是一种web软件架构风格,它不是标准也不是协议,只是倡导url设计的时候使用资源定义以及资源操作的风格。
REST(Representational State Transfer)描述了一个架构样式的网络系统。它由Roy Fielding在2000年的博士论文中提出,Roy Fielding是HTTP规范的主要编写者之一。REST相比SOAP(Simple Object Access protocol,简单对象访问协议)和XML-RPC更加简单明了,REST更倾向于简单轻量的方法设计和实现。所以REST是一种设计风格。
结构清晰、符合标准、易于理解、方便扩展
资源(Resources): 网络上的一个实体,可以是一段文本、一张图片、一首歌曲、一种服务。可以用URI(统一资源定位符)指定它,每种资源对应一个特定的URI。要获得资源只需要访问这个资源的URI即可。
表现层(Representation): 把资源呈现出来的形式,叫做表现层。比如:文本用txt格式、HTML格式、XML格式、JSON格式表现。
状态转化(State Transfer): 每一个请求,就代表了客户端和服务器的一次交互。
HTTP协议是一个无状态协议,所有的状态都保存在服务器端。如果客户端想要操作服务器,就必须通过一些手段,让服务器端发生状态转化。具体在HTTP协议中有四个表示操作方式的动词:PUT / DELETE / GET / POST。
互联网所有的一切都是资源,要求URL中只有表示资源的名称,没有动词。
使用HTTP请求中的method方法put(更新) / delete(删除) / post(新增) / get(查询)来操作资源。但是由于安全性问题主要使用post和get。put和delete基本不使用。如果强制要使用put和delete,可以在请求中使用post并携带参数_method=put,服务端再配置请求方式过滤器,如果请求参数中有__method
就会转换请求方式org.springframework.web.filter.HiddenHttpMethodFilter
。
html
<form method="post" action="/demo/handle/15/lisi">
<input type="hidden" name="_method" value="put"/>
<input type="submit" value="提交rest_put请求"/>
form>
<form method="post" action="/demo/handle/15">
<input type="hidden" name="_method" value="delete"/>
<input type="submit" value="提交rest_delete请求"/>
form>
web.xml
<filter>
<filter-name>hiddenHttpMethodFilterfilter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
java
/*
* restful put /demo/handle/15/lisi
*/
@RequestMapping(value = "/handle/{id}/{name}",method = {RequestMethod.PUT})
public ModelAndView handlePut(@PathVariable("id") Integer id, @PathVariable("name") String username) {
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
/*
* restful delete /demo/handle/15
*/
@RequestMapping(value = "/handle/{id}",method = {RequestMethod.DELETE})
public ModelAndView handleDelete(@PathVariable("id") Integer id) {
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
JSON 语法是 JavaScript 对象表示语法的子集。
@ReqeustBody注解是Spring MVC对Json的支持,作用是将用户请求的Json格式数据解析转换为对应的参数对象。这个注解解析的是Post请求的body区域参数。
@ResponseBody注解是Spring MVC对Json的支持,作用是将Controller返回的对象通过转换器转换为指定的格式之后,写入到reqsponse对象的body区域。
注意 使用这个注解后返回结果不走视图处理器,而是直接将数据写入到输出流中响应给客户端。
pom引入jar
<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>
html
<div>
<h2>Ajax json交互h2>
<fieldset>
<input type="button" id="ajaxBtn" value="ajax提交"/>
fieldset>
div>
javascript
$(function () {
$("#ajaxBtn").bind("click",function () {
// 发送ajax请求
$.ajax({
url: '/demo/handle07',
type: 'POST',
data: '{"id":"1","name":"李四"}',
contentType: 'application/json;charset=utf-8',
dataType: 'json',
success: function (data) {
alert(data.name);
}
})
})
})
java
@RequestMapping("/handle07")
// 添加@ResponseBody之后,不再走视图解析器那个流程,而是等同于response直接输出数据
public @ResponseBody User handle07(@RequestBody User user) {
// 业务逻辑处理,修改name为张三丰
user.setName("张三丰");
return user;
}
拦截器需要实现HandlerInterceptor
接口,其中包含三个方法
preHandle
: 在Handler方法执行前执行。
postHandle
: 在Handler方法返回前执行。
afterCompletion
: 在Handler方法返回后执行。
public class MyInterceptor01 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor01 preHandle......");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
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>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.zhichunqiu.interceptor.MyInterceptor01">bean>
mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.zhichunqiu.interceptor.MyInterceptor02">bean>
mvc:interceptor>
mvc:interceptors>
引入jar
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.3.1version>
dependency>
配置springmvc.xml
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="2048000000" />
bean>
html
<div>
<h2>multipart 文件上传,写法一h2>
<fieldset>
<%--
1 method="post"
2 enctype="multipart/form-data"
3 type="file"
--%>
<form method="post" enctype="multipart/form-data" action="/demo/upload01">
<input type="file" name="uploadFile"/>
<input type="submit" value="上传"/>
form>
fieldset>
div>
java
/**
* 从文件上传的request中获取文件
*
* @param req
* @param resp
* @return
* @throws Exception
*/
@RequestMapping("/upload01")
public String fileUpload01(MultipartHttpServletRequest req, HttpServletResponse resp) throws Exception {
// 获取上传的文件
MultipartFile uploadFile = req.getFile("uploadFile");
// 获取上传文件的名称
String originalFilename = uploadFile.getOriginalFilename();
// 获取文件的流
InputStream inputStream = uploadFile.getInputStream();
// 获取磁盘路径, / 表示WEB-INF目录
String realPath = req.getServletContext().getRealPath("/upload") + "/";
System.out.println("磁盘目录为:" + realPath);
// 将文件输出到这个目录,这里文件没有重命名处理,但是生产中对文件需要重命名
uploadFile.transferTo(new File(realPath + originalFilename));
return "success";
}
html
<div>
<h2>multipart 文件上传,方式二h2>
<fieldset>
<%--
1 method="post"
2 enctype="multipart/form-data"
3 type="file"
--%>
<form method="post" enctype="multipart/form-data" action="/demo/upload02">
<input type="file" name="uploadFile"/>
<input type="submit" value="上传"/>
form>
fieldset>
div>
java
/**
* 直接定义MultipartFile对象
*
* @param multipartFile
* @param session
* @return
* @throws Exception
*/
@RequestMapping("/upload02")
public String fileUpload02(MultipartFile multipartFile, HttpSession session) throws Exception {
// 获取上传的文件名称
String originalFilename = multipartFile.getOriginalFilename();
// 获取磁盘路径, / 表示WEB-INF目录
String realPath = session.getServletContext().getRealPath("/upload") + "/" + originalFilename;
System.out.println("磁盘目录为:" + realPath);
// 将文件输出到这个目录,这里文件没有重命名处理,但是生产中对文件需要重命名
multipartFile.transferTo(new File(realPath));
return "success";
}
局部异常处理是在单个Controller中使用@ExceptionHandler
标记一个方法为这个Controller的局部异常处理Handler。可以定义多个异常处理器,分别处理不同类型的异常。
@ExceptionHandler(value = {IOException.class})
public ModelAndView methodException(Exception exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", exception.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView arithmeticException(Exception exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", exception.getMessage() + " / 局部异常处理器:arithmeticException");
modelAndView.setViewName("error");
return modelAndView;
}
全局异常处理是创建一个Class,并且使用注解@ControllerAdvice
标记这个Controller增强类。这样可以在类中定义多个异常处理器,分别处理不同的异常。
注意 如果同时定义了局部异常和全局异常,那么优先匹配的是局部异常。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView arithmeticException(Exception exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", exception.getMessage() + " / 全局异常处理器:arithmeticException");
modelAndView.setViewName("error");
return modelAndView;
}
}
对于重定向后的参数传递问题,一般都是重定向后拼接参数,这种方式效率不高,而且使用get方式对于参数有长度限制而且不安全。spring mvc在Controller的方法参数中提供了RedirectAttributes
参数用于解决重定向后的参数传递问题。传递后的参数使用@ModelAttribute
注解接收。
/**
* SpringMVC 重定向时参数传递的问题
*/
@RequestMapping("/handleRedirect")
public String handleRedirect(String name, RedirectAttributes redirectAttributes) {
// addFlashAttribute方法设置了一个flash类型属性,该属性会被暂存到session中,在跳转到页面之后该属性销毁
redirectAttributes.addFlashAttribute("name",name);
return "redirect:handleRedirect01";
}
/**
* 重定向后用@ModelAttribute注解取出属性:ModelAttribute
* @param name
* @return
*/
@RequestMapping("/handleRedirect01")
public ModelAndView handleRedirect01(@ModelAttribute("name") String name) {
LocalDateTime now = LocalDateTime.now();
// 封装了Model和View视图
ModelAndView modelAndView = new ModelAndView();
// 封装返回结果到Model中
modelAndView.addObject("date", now);
// 设置返回的视图名称
modelAndView.setViewName("success");
return modelAndView;
}