《精通SpringMVC4》
链接:https://pan.baidu.com/s/1vmaSmQ1TOenWGE7HdeQe2g
提取码:vwk3
别人的读书笔记https://blog.csdn.net/qq_36807862/article/details/81290041
1.5.2
@SpringBootApplication 注解,如果看一下这个注解的代码的话,就会发现它实际上组合了3 个其他的注解,也就是@Configuration(表明这个类将会处理Spring常规配置,比如bean的声明)、@EnableAutoConfiguration 和@ComponentScan(告诉Spring去哪里查找Spring组件,服务,控制器等,默认扫描当前包以及包下面所有子包)
1.6如果你之前搭建过Spring MVC 应用,那么可能已经习惯于编写相关的XML 文件或Java注解配置类。
一般来讲,初始的步骤如下所示:
- 初始化Spring MVC 的DispatcherServlet;
- 搭建转码过滤器,保证客户端请求进行正确地转码;
- 搭建视图解析器(view resolver),告诉Spring 去哪里查找视图,以及它们是使用哪种方言编写的(JSP、Thymeleaf 模板等);
- 配置静态资源的位置(CSS、JS);
- 配置所支持的地域以及资源bundle;
- 配置multipart 解析器,保证文件上传能够正常工作;
- 将Tomcat 或Jetty 包含进来,从而能够在Web 服务器上运行我们的应用;
- 建立错误页面(如404)。
//在完成所有Bean的实例化后,Spring又加载了一系列策略方法,用于SpringMVC。
protected void initStrategies(ApplicationContext context) {
//文件类型解析器
initMultipartResolver(context);
//为了让web应用程序支持国际化,必须识别每个用户的首选区域,并根据这个区域显示内容。
//LocaleResolver解析器主要解决国际化的问题。
initLocaleResolver(context);
//主题样式解析器,
initThemeResolver(context);
//处理器映射解析器(非常重要)
initHandlerMappings(context);
//handlerAdapter解析器
initHandlerAdapters(context);
//异常解析器
initHandlerExceptionResolvers(context);
//通过提供的 Servlet实例来初始化RequestToViewNameTranslator
initRequestToViewNameTranslator(context);
//视图解析器,
initViewResolvers(context);
initFlashMapManager(context);
}
关于快速搭建web项目,Springboot帮我们做的事情:SpringBoot 是带有一定倾向性的Spring项目配置器,SpringBoot仅仅是基于常见的使用场景,帮助我们对应用进行配置,不过我们可以在任意的地方覆盖这些默认值,并声明自己的配置。
1.6 .1分发器和multipart解析器
为什么我们已经拥有了分发器servlet和multipart解析器?
通过查看DispatcherServletAutoConfiguration这个配置类我们就了解了SpringBoot已经帮我们配置了dispatcherServlet和multipartResolver。
DispatcherServletAutoConfiguration这是一个典型的SpringBoot配置类
- 与其他的Spring 配置类相同,它使用了@Configuration 注解;
- 般会通过@Order 注解来声明优先等级,可以看到DispatcherServletAutoConfiguration需要优先进行配置;
- 其中也可以包含一些提示信息,如@AutoConfigureAfter 或@AutoConfigureBefore,从而进一步细化配置处理的顺序;
- 它 还 支 持 在 特 定 的 条 件下启用某项功能。通过使用@ConditionalOnClass(DispatcherServlet.class) 这个特殊的配置, 能够确保我们的类路径下包含DispatcherServlet,这能够很好地表明Spring MVC 位于类路径中,用户当前希望将其启动起来。
这个文件中还包含了Spring MVC 分发器Servlet 和multipart 解析器的典型配置。整个Spring MVC 配置被拆分到了多个文件之中。org.springframework.boot.autoconfigure.web包下面都是Spring web 相关的配置,还有读取配置的类xxxProperties
1.6.2 视图解析器、静态资源以及区域配置
另一个密切相关的配置类是WebMvcAutoConfiguration,他声明了视图解析器,地域解析器(localeresolver),以及静态资源的位置。
视图解析器:
@Configuration
@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
}
配置很普通,很简单,其中使用了配置属性,允许用户自定义。它的意思就是说“将会在用户的application.properties 文件中查找两个变量,这两个变量的名字是spring.mvc.view.prefix=" ", spring.mvc.view.suffix=" ".在配置中只需两行代码就能将视图解析器搭建起来了,这是非常便利的。
在这个文件中还声明了静态资源和地域的配置:
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
public LocaleResolver localeResolver() {...}
public void addResourceHandlers(ResourceHandlerRegistry registry) {...}
}
1.7 错误与转吗配置
org.springframework.boot.autoconfigure.web.servlet.error包
ErrorMvcAutoConfiguration:这个类帮我们做了简单的错误处理
- 定义了一个bean,即DefaultErrorAttributes,它通过特定的属性暴露了有用的错误信息,这些属性包括状态、错误码和相关的栈跟踪信息。
- 定义了一个BasicErrorController bean,这是一个MVC 控制器,负责展现我们所看到的错误页面。
- 允许我们将Spring Boot 的whitelabel 错误页面设置为无效,这需要将配置文件application.properties 中error.whitelable.enabled 设置为false。
- 我们还可以借助模板引擎提供自己的错误页面。例如,它的名字是error.html,ErrorTemplateMissingCondition 条件会对此进行检查。
自动配置实在是太强了!!!
Spring MVC的解析器中
我们重点关注两个,initHandlerMappings和initHandlerAdapters。
1.8 嵌入式Servlet容器(Tomcat)的配置
默认情况下,SpringBoot在打包和运行应用时会使用Tomcat嵌入式API(Tomcat embedded API):嵌入式servlet容器的配置类包含了三个不同的配置,那一个处于激活状态取决于类路径下哪些内容是可用的。因此,服务器可以很容易地进行替换,只需将spring-boot-starter-tomcat JAR 依赖移除掉,并将其替换为Jetty或Undertow 对应的依赖即可。
配置servlet容器时的不同选项:ServerProperties
1.8.1HTTP 端口 server.port= -1(禁用HTTP协议)/0(在随机端口启动应用)
1.8.2 SSL配置
1.8.3 其他配置
在配置中我们可以简单的通过@Bean元素添加java web元素,比如servlet,Filter,listlistener。SpringBoot可以很方便的与Servlet配合使用。
除此之外,SpringBoot还为我们内置了3项内容:
- 在JackSonAutoConfiguration中,声明Jackson进行JSON序列化。**注意Jackson使用默认时区,反序列化将会出现日期问题,配置成东八区只需要添加配置spring.jackson.time-zone=GMT+8
- 在HttpMessageConvertersAutoConfiguration中声明了默认的HttpMessageConverter
- 在JmxAutoConfiguration中声明了JMX功能(Java Management Extension java管理扩展),添加Spring-boot-starter-actuator
第二章、精通MVC架构
2.1MVC架构:将数据和表现层解耦,模型和视图解耦,相同的数据跨多个视图重用
- 模型:包含了应用中所需的各种展现数据。
- 视图:由数据的多种表述所组成,它将会展现给用户。以不同的方式来查看数据
- 控制器:将会处理用户的操作,它是连接模型和视图的桥梁。
2.2 对MVC的质疑及其最佳实践
2.2.1 贫血的领域模型
DDD(Domain Driver Design,领域驱动设计):核心理念是将面向对象的范式应用到领域对象之中。违背这一原则的话就被称为贫血的领域模型
贫血的领域模型通常来讲会具有如下的症状:
- 模型是由简单老式的Java 对象(plain old Java object,POJO)所构成的,只有getter 和setter 方法;
- 所有业务逻辑都是在服务层处理的;
- 对模型的校验会在本模型外部进行,例如在控制器中。
DDD实践需要将领域从应用逻辑中分离出来,DDD规则所涉及的非常多...
避免领域贫血的途径如下:
- 服务层适合进行应用级别的抽象(如事务处理),而不是业务逻辑;
- 领域对象应该始终处于合法的状态。通过校验器(validator)或JSR-303 的校验注解,让校验过程在表单对象中进行;
- 将输入转换成有意义的领域对象;
- 将数据层按照Repository 的方式来实现,Repository 中会包含领域查询(例如参考Spring Data 规范);
- 将领域逻辑与底层的持久化框架解耦;
- 尽可能使用实际的对象,例如操作FirstName 类而不是操作String。
2.3 Spring MVC
- 模型-由Spring MVC 的Model 或者ModelView简单封装的Map,与数据层交互-推荐使用springData库
- 控制层-@Controller处理,响应http请求;控制器响应有两种方式:
- 在web响应中直接写入内容(比如RESTful应用,在响应中直接暴露模型的JSON或者XML描述);
- 将应用路由一个视图并将属性注入到该视图中。(模型传递到视图中,由模板引擎进行渲染,并写入相应之中)
2.4 使用Thymeleaf模板引擎
- 添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>2.1.7.RELEASE</version> </dependency>
添加依赖就能启动Thymleaf模板引擎了
- 添加一个页面,将其放到src/main/resources/templates目录中
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <html lang="en"> <head> <meta charset="UTF-8"> <title>Hello My Thymeleaf</title> </head> <body> <span th:text="|Hello thymeleaf|">Hello html</span> </body> </html>
html xmlns属性可以在文档中定义多个命名空间
- 修改Controller层
@Controller public class HelloController { @RequestMapping("/") /** * @ResponseBody 注解将被拦截,转化为JSON * 移除@ResponseBody,再返回字符串就会告诉SpringMVC要将这个字符串映射为视图名称,而不是在响应中直接描述模型 * */ // @ResponseBody public String hello() { return "resultPage"; } }
结果:
2.5 Spring MVC架构
跟踪浏览器所发送的HTTP 请求的行程以及它是如何从服务器端得到响应的。了解整个SpringMVC的核心流程
参考:Spring MVC的核心流程(步骤):https://www.cnblogs.com/Jones-dd/p/8831013.html
2.5.1DispatcherServlet
- DispatcherServlet是一个典型的HttpServlet 类,它会将HTTP 请求分发给HandlerMapping 。
- HandlerMapping 会将资源(URL)与控制器关联起来。
- 控制器上对应的方法(也就是带有@RequestMapping 注解的方法)将会被调用。在这个方法中,控制器会设置模型数据并将视图名称返回给分发器。
- 然后,DispatcherServlet 将会查询ViewResolver 接口,从而得到对应视图的实现。
- 在样例中,ThymeleafAutoConfiguration 将会为我们搭建视图解析器。通过查看ThymeleafProperties 类,可以知道视图的默认前缀是“classpath:/templates/”,后缀是“.html”。这就意味着,假设视图名为resultPage,那么视图解析器将会在类路径的templates 目录下查找名为resultPage.html 的文件。
- 在我们的应用中,ViewResolver 接口是静态的,但是更为高级的实现能够根据请求的头信息或用户的地域信息,返回不同的结果。
- 视图最终将会被渲染,其结果会写入到响应之中。
2.5.2 将数据传递给视图
修改resultPage.html让其展现来自模型中的信息
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello My Thymeleaf</title>
</head>
<body>
<span th:text="|Hello thymeleaf|">Hello html</span>
<span th:text="${message}">Hello html</span>
</body>
</html>
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloController {
@RequestMapping("/")
public String hello(Model model) {
model.addAttribute("message","hello from Controller!");
return "resultPage";
}
}
很多对象都可以注入到控制器的方法之中,例如HttpRequest、HttpResponse、Locale、TimeZone 和Principal,其中Principal 代表了一个认证过的用户。完整的对象列表可以在Spring文档中查阅
2.6 Spring 表达式语言--"${}"语法(Spring Expression Language 表达式语言)
https://www.cnblogs.com/xdp-gacl/p/3938361.html
SpEL不局限于在视图中使用,可以在Spring 框架的各种地方使用,比如使用@Value注解往bean中注入属性时也可使用SpEL
从请求参数中获取数据
- @RequestParam将参数添加到URL中,请求参数位于URL的?字符之后,使用&符号进行分割。@RequestParam接收的参数是来自requestHeader中,即请求头。通常用于GET请求,像POST、DELETE等其它类型的请求也可以使用。
- 带占位符的 URL 是 Spring3.0 新增的功能,该功能在SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义@PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中:URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到操作方法的入参中。
- 注解@RequestBody接收的参数是来自requestBody中,即请求体。一般用于处理非
Content-Type: application/x-www-form-urlencoded
编码格式的数据,比如:application/json
、application/xml
等类型的数据。通常用于接收POST、DELETE等类型的请求数据,GET类型也可以适用。
2.7 结束Hello Word 开始获取Tweet--无法访问tweet的开发者中心
2.8 Spring MVC的stream流和lambda表达式
Java8 中每个集合都会有一个默认的stream()方法,能实现函数式风格的操作。这些操作可以是中间操作,返回一个流,也可以是终止操作,返回一个值
常用中间操作:
- map:为列表中的每个元素都应用某个方法,并返回结果所组成的列表
- filter:返回匹配断言的所有元素
- reduce:借助一个操作和累加器,将一个列表聚合到单个值上
collect 方法允许我们调用一个终止操作。Collectors 类是一组终止操作,它会将结果放到列表、集合或Map 之中,允许进行分组(grouping)、连接(joining)等操作。
lambda表达式用到只包含一个方法的接口之中(匿名内部类的一种,主要用于替换广泛使用的匿名内部类,比如Thread类,Runnable接口),λ表达式的目标类型是“函数接口(functional interface)”
lambda表达式的组成
函数式接口:
2.9 使用WebJars实现质感设计
- 在Controller中实现请求重定向:RedirectAttributes 是一个Spring 的模型,专门用于redirect 场景下传送值
- Redirect/Forward,只需要在返回的字符串上添加"redirect:"或者"forward:"前缀即可
- 重定向(URL变化)/转发(URL不变化)
第三章、处理表单和复杂的URL映射
3.1 表单
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
}
对SpringMVC进行自定义配置,可以实现WebMvcConfigurer 接口,或者扩展WebMvcConfigurerAdapter 类
3.2 校验:
javax.validation.constraints包中的注解
hibernate-validator的jar包中:org.hibernate.validator.constraints中的注解
3.3 国际化
SpringMVC拦截器(Interceptor) 可以类比为Servlet的过滤器,拦截器在Spring应用的上下文中,以bean的形式配置,允许进行自定义的预处理,跳过处理器的执行,以及执行自定义的事后处理;而过滤器和servlet一起在web.xml中配置(现在也可以使用java 进行配置@WebFilter)
第四章、文件上传与错误处理
4.1 上传文件
用户提交的文件将会以MultipartFile 接口的形式注入到控制器中,这个接口提供了多个方法,用来获取文件的名称、大小及其内容。
使用@ConfigurationProperties(prefix=" ")读取用户配置
@ModelAttribute创建模型属性,并且可以通过相同的注解将模型属性注入到Controller当中
@Controller
//将参数定义为会话属性
@SessionAttributes("picturePath")
public class PictureUploadController {
@ModelAttribute("picturePath")
public Resource picturePath() {
return anonymousPicture;
}
@RequestMapping(value = "/uploadedPicture")
public void getUploadedPicture(HttpServletResponse response, @
ModelAttribute("picturePath") Path picturePath) throws IOException {
response.setHeader("Content-Type",
URLConnection.guessContentTypeFromName(picturePath.toString()));
Files.copy(picturePath, response.getOutputStream());
}
}
问题在于模型属性在不同的请求间会进行重置,因此我们需要将picturePath参数定义为会话属性。可以通过在控制器类上添加@SessionAttributes("picturePath")
4.3 将基本信息放入到会话中
HTTP 会话(session)是用来在请求之间存储信息的一种方式。HTTP 是无状态的协议,这就意味着没有办法将同一用户的两请求关联起来。Servlet 容器最常用的办法是为每个用户关联一个名为JSESSIONID 的cookie。这个cookie将会通过请求头信息进行传输,借助这项技术允许用户将任意的对象存储在Map 中,也就是名为HttpSession 的抽象形式。这样的会话一般情况下会在用户关闭或切换Web浏览器,或者预定时间内用户没有活跃的动作时失效。
在Spring 中,将内容放到会话中的另外一种流行方式就是为bean 添加@Scope("session")注解。这样就能将会话bean注入到控制器中,其他的Spring组件可以为其设值,或者从中检索值;
借助SpringMVC将数据存储到会话中所需要做的工作非常简单:
//创建一个会话bean
@Component
@Scope(value = "session",proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart {
...
}
/**
* @description: 注入一个会话bean
**/
@Controller
public class ShoppingCartController {
private ShoppingCart shoppingCart;
@Autowired
public ShoppingCartController(ShoppingCart shoppingCart) {
this.shoppingCart = shoppingCart;
}
}
关于域注入和构造函数注入地讨论http://olivergierke.de/2013/11/why-field-injection-is-evil/
@Scope(value = "session",proxyMode = ScopedProxyMode.TARGET_CLASS)
proxyMode有三个可用的参数(NO-不创建任何代理;TARGET_CLASS-使用CGLib代理;INTERFACES-使用JDK代理)
当我们将一些内容注入到长期存活的组件中时,例如单例bean;代理的好处就体现出来了。因为注入只会发生一次,bean创建之后,对被注入的bean的后续调用不一定能反应真实状态。
会话bean的实际状态存储在会话之中,并没有直接反应在bean上。这就反映了Spring为什么要创建代理:他需要拦截对bean方法的调用,并监听它的变化。通过这种方式,bean状态的存储和获取就对底层的HTTP会话完全透明了。
对于会话bean,我们必须要使用代理模式。CGLib 代理会对字节码进行instrument 操作,能够用在任意的类上,而JDK 的方式可能会更加轻量级,但是需要你实现一个接口。
会话bean 最好能够实现Serializable接口,因为HTTP会话能够将任意的bean存储在内存之中。
4.4自定义错误页面
处理错误的更高级方式就是定义自己的ErrorController 实现类,这个控制器负责在全局处理所有的异常。可以参考ErrorMvcAutoConfiguration 类和BasicErrorController,这是默认的实现。
4.5 使用矩阵变量进行URL映射
矩阵变量可以映射为URL中不同的对象类型
- Map<String, List<?>>:这将会处理多个变量的多个值。
- Map<String, ?>:这会处理一个变量只有一个值的场景。
- List<?>:如果我们只对一个变量感兴趣,并且变量的名称可以配置的话,可以使用这种方式。
- someUrl/param?var1=value1&var2=value2
- 我们可以使用如下的矩阵变量,替换之前的参数:someUrl/param;var1=value1;var2=value2
- 它还支持将每个参数设置为列表:someUrl/param;var1=value1,value2;var2=value3,value4
默认情况下SpringMVC会移除URL中封号之后的字符,我们需要先关闭这种默认行为
在WebConfiguration类中添加配置
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
然后使用@MatrixVariable(pathVar="ByCriteria") Map<String, List<String>> filterParams 接收参数。