首页 > 其他分享 >《Spring Boot+Vue全栈开发实战》读书笔记

《Spring Boot+Vue全栈开发实战》读书笔记

时间:2022-12-12 17:37:42浏览次数:79  
标签:Vue 读书笔记 spring 配置 Boot class Spring public


写在前面

  • 嗯,回家处理一些事,所以离职了,之前的公司用开源技术封装了一套自己的低代码平台,所以之前学的spring Boot之类的东西都忘了很多,蹭回家的闲暇时间复习下。
  • 笔记整体以 Spring Boot+Vue全栈开发实战一书为方向,中间穿插一些其他视频(原书作者的视频)的知识点。
  • 嗯,生活加油,这段时间好好休养,笔记在更新中…整装待发 ^ _ ^,加油生活…

我年青时以为金钱至上,而今年事已迈,发现果真如此 —王尔德


使用XML配置搭建SSM项目

​代码详见:https://github.com/LIRUILONGS/SSM-XML.git​

  • ​新建一个maven工程,构造SSM目录结构​
  • 《Spring Boot+Vue全栈开发实战》读书笔记_spring boot


  • ​添加依赖,构建配置文件​
  • 《Spring Boot+Vue全栈开发实战》读书笔记_bc_02

  • SpringMVC是Spring的子容器,所以SpringMVC子容器可以访问Spring父容器,反之则不行。所以Spring的配置文件扫描除了Controller的bean,SpringMVC扫描controller的东西。
  • 《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_03



使用 Java配置类搭建SSM项目

​代码详见:https://github.com/LIRUILONGS/SSM-java.git​

  • @Configuration 注解表示这是一个配置类,在我们这里,这个配置的作用类似于 applicationContext.xml
  • @ComponentScan 注解表示配置包扫描,里边的属性和 xml 配置中的属性都是一一对应的,useDefaultFilters 表示使用默认的过滤器,然后又除去 Controller 注解,即在 Spring 容器中扫描除了 Controller 之外的其他所有 Bean 。
  • 使用 Java 代码去代替 web.xml 文件,这里会用到 WebApplicationInitializer ,WebInit 的作用类似于 web.xml,这个类需要实现 WebApplicationInitializer 接口,并实现接口中的方法,当项目启动时,onStartup 方法会被自动执行,我们可以在这个方法中做一些项目初始化操作,例如加载 SpringMVC 容器,添加过滤器,添加 Listener、添加 Servlet 等。具体定义如下:

​注意​​​:
由于我们在 WebInit 中只是添加了 SpringMVC 的配置,这样项目在启动时只会去加载 SpringMVC 容器,而不会去加载 Spring 容器,如果一定要加载 Spring 容器,需要我们修改 SpringMVC 的配置,在 SpringMVC 配置的包扫描中也去扫描 @Configuration 注解,进而加载 Spring 容器,还有一种方案可以解决这个问题,就是直接在项目中舍弃 Spring 配置,直接将所有配置放到 SpringMVC 的配置中来完成,这个在 SSM 整合时是没有问题的,在实际开发中,较多采用第二种方案,第二种方案,SpringMVC 的配置如下:

  • 静态资源过滤:重写 addResourceHandlers 方法,在这个方法中配置静态资源过滤,这里我将静态资源放在 resources 目录下,所以资源位置是 classpath:/ ,当然,资源也可以放在 webapp 目录下,此时只需要修改配置中的资源位置即可。如果采用 Java 来配置 SSM 环境,一般来说,可以不必使用 webapp 目录,除非要使用 JSP 做页面模板,否则可以忽略 webapp 目录。
  • 视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
@Configuration
@ComponentScan(basePackages = "org.javaboy")
public class SpringMVCConfig extends WebMvcConfigurationSupport {
@Override
protected void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/jsp/", ".jsp");
}
}
  • 路径映射:控制器的作用仅仅只是一个跳转,就像上面小节中的控制器,里边没有任何业务逻辑,像这种情况,可以不用定义方法,可以直接通过路径映射来实现页面访问。如果在 XML 中配置路径映射
<mvc:view-controller path="/hello" view-name="hello" status-code="200"/>

这行配置,表示如果用户访问 /hello 这个路径,则直接将名为 hello 的视图返回给用户,并且响应码为 200,这个配置就可以替代 Controller 中的方法。

@Configuration
@ComponentScan(basePackages = "org.javaboy")
public class SpringMVCConfig extends WebMvcConfigurationSupport {
@Override
protected void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello3").setViewName("hello");
}
}
  • JSON 配置SpringMVC 可以接收JSON 参数,也可以返回 JSON 参数,这一切依赖于 HttpMessageConverter。

HttpMessageConverter 可以将一个 JSON 字符串转为 对象,也可以将一个对象转为 JSON 字符串,实际上它的底层还是依赖于具体的 JSON 库。
所有的 JSON 库要在 SpringMVC 中自动返回或者接收 JSON,都必须提供和自己相关的 HttpMessageConverter 。
SpringMVC 中,默认提供了 Jackson 和 gson 的 HttpMessageConverter ,分别是:MappingJackson2HttpMessageConverter 和 GsonHttpMessageConverter 。
正因为如此,我们在 SpringMVC 中,如果要使用 JSON ,对于 jackson 和 gson 我们只需要添加依赖,加完依赖就可以直接使用了。具体的配置是在 AllEncompassingFormHttpMessageConverter 类中完成的。
如果开发者使用了 fastjson,那么默认情况下,SpringMVC 并没有提供 fastjson 的 HttpMessageConverter ,这个需要我们自己提供,如果是在 XML 配置中,fastjson 除了加依赖,还要显式配置 HttpMessageConverter,如下:

<mvc:annotation-driven>
<mvc:message-converters>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

第1章Spring Boot入门

  • 提供一个快速的Spring项目搭建渠道
  • 开箱即用,很少的Spring 配置就能运行一个Java EE项目。
  • 提供了生产级的服务监控方案。
  • 内嵌服务器,可以快速部署。
  • 提供了一系列非功能性的通用配置。
  • 纯Java配置,没有代码生成,也不需要XML配置。

第2章 Spring Boot基础配置

工程创建的三种方式:

  1. 在线创建
  2. 通过 IDE 来创建(IntelliJ IDEA、STS)
  3. 通过改造一个普通的 Maven 工程来实现

2.1不使用spring-boot-starter-parent

​spring-boot-starter-parent​​主要提供了如下默认配置:

  • Java版本默认使用1.8.编码格式
  • 默认使用UTF-8.
  • 提供Dependency Management进行项目依赖的版本管理。
  • 默认的资源过滤与插件配置

2.2 @Spring BootApplication.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
....

@Spring BootApplication 是一个组合注解:

  • @SpringBootConfiguration原来就是一个@Configuration,所以@Spring BootConfiguration的功能就是表明这是一个配置类。开发者可以在这个类中配置Bean。从这个角度来讲,这个类所扮演的角色有点类似于Spring中applicationContext.xml文件的角色。
  • 第二个注解@EnableAutoConfiguration表示开启自动化配置。 Spring Boot中的自动化配置是非侵入式的,在任意时刻,开发者都可以使用自定义配置代替自动化配置中的某一个配置。
  • 第三个注解@ComponentScan完成包扫描,也是Spring中的功能。由于@ComponentScan注解默认扫描的类都位于当前类所在包的下面,因此建议在实际项目开发中把项目启动类放在根包。

2.3定制banner

  • Spring Boot项目在启动时会打印一个banne
  • 定制网站:http://patorjk.com/software/taag
@SpringBootApplication
public class SpringBootDemoApplication {

public static void main(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootDemoApplication.class);
builder.bannerMode(Banner.Mode.OFF).run(args);
}
}

2.4 Web容器配置

2.4.1 Tomcat配置
##配置了Web容器的端口号。
server.port=8081
##配置了当项目出错时跳转去的页面。
server.error.path=/error
##配置了session失效时间, 30m表示30分钟,如果不写单位,默认单位是秒。由于Tomcat中配置session过期时间以分钟为单位,因此这里单位如果是秒的话,该时间会被转换为一个不超过所配置秒数的最大分钟数,例如这里配置了119,默认单位为秒,则实际session过期时间为1分钟。
server.servlet.session.timeout=30m
##表示项目名称,不配置时默认为/,如果配置了,就要在访问路径中加上配置的路径。
server.servlet.context-path=/
##表示配置Tomcat请求编码。
server.tomcat.uri-encoding=utf-8
##表示Tomcat最大线程数。
server.tomcat.threads.max=500
##是一个存放Tomcat运行日志和临时文件的目录,若不配置,则默认使用系统的临时目录。
server.tomcat.basedir=/home/sang/tmp
##
HTTPS的配置:

《Spring Boot+Vue全栈开发实战》读书笔记_java_04

## 密匙文件
server.ssl.key-store=sang.p12
## 密匙别名
server.ssl.key-alias=tomcathttps
## 就是在cmd命令执行过程中输入的密码
server.ssl.key-store-password=123456

Spring Boot不支持同时在配置中启动​​HTTP​​​和​​HTTPS​​,这个时候可以配置请求重定向,将HTTP请求重定向为HTTPS请求。配置方式如下:

@Configuration
public class TomcatConfig {
/*
* @return
* @Description : TODO 配置一个 TomcatServletWebServerFactory 的Bean,
* @author Liruilong
* @date 2021/6/3 11:47
**/
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(){
@Override
protected void postProcessContext(Context context) {
SecurityConstraint constraint = new SecurityConstraint();
constraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
constraint.addCollection(collection);
context.addConstraint(constraint);
}
};
factory.addAdditionalTomcatConnectors(createTomcatConnector());
return factory;
}
/*
* @return
* @Description
* @author Liruilong
* @date 2021/6/3 11:45
**/
private Connector createTomcatConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8081);
return connector;
}
}

这里首先配置一个TomcatServletWebServerFactory,然后添加一个Tomcat中的Connector (监听8080端口) ,并将请求转发到8081上去。

2.4.2 Jetty配置

除了Tomcat外,也可以在Spring Boot中嵌入Jetty,从spring-boot-starter-web中除去默认的Tomcat,然后加入Jetty的依赖即可配置方式如下:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.4.3 Undertow配置

Undertow是一个红帽公司开源的Java服务器,具有非常好的性能,在Spring Boot中也得到了很好的支持,配置方式与Jetty类似。

2.5 Properties配置

Spring Boot项目中的·​​application.properties​​配置文件一共可以出现在如下4个位置:加载的优先级从1到4依次降低

《Spring Boot+Vue全栈开发实战》读书笔记_spring boot_05

  1. 项目根目录下的config文件夹中。
  2. 项目根目录下。
  3. classpath 下的config文件夹中。
  4. classpath 下

​application.yml​​​配置文件的优先级与上面一致默认情况下, 如果开发者不想使用​​application.properties​​​作为配置文件名,也可以自己定义。例如,在resources目录下创建一个配置文件​​app.properties​​,然后将项目打成jar包,打包成功后,使用如下命令运行:

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_06

2.6类型安全配置属性.

Spring提供了​​@Value注解​​​以及​​EnvironmentAware接口​​​来将​​Spring Environment​​​中的数据注入到属性上, Spring Boot对此进一步提出了​​类型安全配置属性​​(Type-safe ConfigurationProperties) ,这样即使在数据量非常庞大的情况下,也可以更加方便地将配置文件中的数据注入Bean中.

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_07


yml类型配置文件:

my:
users:
- name: 江南一点雨
address: China
favorites:
- 足球
- 徒步
- Coding
- name: sang
address: GZ
favorites:
- 阅读
- 吉他
/**
* Created by sang on 2018/7/5.
*/
@Component
@ConfigurationProperties(prefix = "my")
public class Users {
private List<User> users;
}

2.7 YAML配置

​YAML​​​是​​JSON的超集​​​,简洁而强大,是一种专门用来书写​​配置文件​​​的语言,可以替代application.properties。在创建一个Spring Boot项目时,引入的​​spring-boot-starter-web​​​依赖间接地引入了​​snakeyaml​​​依赖, snakeyaml会​​实现对YAML配置的解析​​​。YAML的使用非常简单,利用缩进来表示层级关系,并且​​大小写敏感​​​。在Spring Boot项目中使用YAML只需要在​​resources​​​目录下创建一个​​application.yml​​文件即可,然后向application.yml中添加配置:

server:
port: 80
servlet:
context-path: /chapter02
tomcat:
uri-encoding: utf-8

my:
users:
- name: 江南一点雨
address: China
favorites:
- 足球
- 徒步
- Coding
- name: sang
address: GZ
favorites:
- 阅读
- 吉他

2.8 Profile

开发者在项目发布之前,配置需要频繁更改,例如​​数据库配置、redis配置、mongodb配置、jms配置​​​等。频·繁修改带来了巨大的工作量, Spring对此提供了解决方案(​​@Profile注解​​​) , Spring Boot则更进一步提供了更加简洁的解决方案, Spring Boot中约定的不同环境下配置文件名称规则为​​application-{profile}.properties​​, profile占位符表示当前环境的名称,具体配置步骤如下:

不同的环境指定不同的配置文件
spring.profiles.active=dev

《Spring Boot+Vue全栈开发实战》读书笔记_java_08


第3章Spring Boot整合视图层技术

Spring Boot官方推荐使用的模板引擎是Thymeleaf,不过像FreeMarker也支持, JSP技术在这里并不推荐使用。下面分别向读者介绍Spring Boot整合Thymeleaf和FreeMarker两种视图层技术。

3.1整合Thymeleaf

​Thymeleaf​​​是新一代​​Java模板引擎​​​,类似于​​Velocity​​​, ​​FreeMarker​​​等传统​​Java模板引擎​​​。与传统Java模板引擎不同的是, ​​Thymeleaf支持HTML原型​​​,既可以让前端工程师在浏览器中直接打开查看样式,也可以让后端工程师结合真实数据查看显示效果。同时, ​​Spring Boot提供了Thymeleaf自动化配置解决方案​​,因此在Spring Boot中使用Thymeleaf非常方便。Spring Boot整合Thymeleaf主要可通过如下步骤:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.配置​​Thymeleaf​

Spring Boot为​​Thymeleaf​​​提供了自动化配置类​​ThymeleafAutoConfiguration​​​,相关的配置属性在​​ThymeleatProperties​​类中, ThymeleafProperties部分源码如下:

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_09


如果开发者想对​​默认的Thymeleaf配置参数​​​进行自定义配置,那么可以直接在​​application.properties​​中进行配置,部分常见配置如下:

#是否开启缓存,开发时可设置为false,默认为true
spring.thymeleaf.cache=true
#是否检查模板是否存在,默认为true
spring.thymeleaf.check-template=true
#是否检查模板位置是否存在,默认为true
spring.thymeleaf.check-template-location=true
#模板文件编码
spring.thymeleaf.encoding=UTF-8
#模板文件位置
spring.thymeleaf.prefix=classpath:/templates/
#Content-Type配置
spring.thymeleaf.servlet.content-type=text/html
#模板文件后缀
spring.thymeleaf.suffix=.html

《Spring Boot+Vue全栈开发实战》读书笔记_java_10


《Spring Boot+Vue全栈开发实战》读书笔记_spring_11


官网: https://www.thymeleaf.org

3.2整合FreeMarke

​FreeMarker​​​是一个非常古老的模板引擎,可以用在​​Web环境或者非Web环境​​​中。与​​Thymeleaf​​​不同, ​​FreeMarker​​​需要经过解析才能够在浏览器中展示出来。FreeMarker不仅可以用来配置​​HTML​​​页面模板,也可以作为​​电子邮件模板​​​、​​配置文件模板​​​以及​​源码模板​​等。Spring Boot中对FreeMarker整合也提供了很好的支持.

配置FreeMarker

Spring Boot对​​FreeMarker​​​也提供了​​自动化配置类FreeMarkerAutoConfiguration​​​,相关的配置属性在​​FreeMarkerProperties​​ 中,

#HttpServletRequest的属性是否可以覆盖controller中model的同名项
spring.freemarker.allow-request-override=false
#HttpSession的属性是否可以覆盖controller中model的同名项
spring.freemarker.allow-session-override=true
#是否开启缓存
spring.freemarker.cache=fal se
#模板文件编码
spring.freemarker.charset=UTF-8
#是否检查模板位置
spring.freemarker.check-template-location=true
#Content-Type的值
spring.freemarker.content-type=text/html
#是否将HttpServletRequest中的属性添加到Model中
spring.freemarker.expose-request-attributes=false
#是否将HttpSession中的属性添加到Model中
spring.freemarker.expose-session-attributes=true
#模板文件后缀
spring.freemarker.suffix=.ftl
#模板文件位置
spring.freemarker.template-loader-path=classpath:/templates/

《Spring Boot+Vue全栈开发实战》读书笔记_spring_12


《Spring Boot+Vue全栈开发实战》读书笔记_spring boot_13


官网:https://freemarker.apache.org/

3.3 整合 JSP

<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
package com.liruilong.spring_boot_demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* @Classname WebMvcConfig
* @Description TODO
* @Date 2021/6/4 10:02
* @Created Li Ruilong
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/jsp/", ".jsp");
}
}

第4章 Spring Boot整合Web开发.

4.1返回JSON数据

4.1.1 默认实现

​JSON​​​是目前主流的前后端数据传输方式, ​​Spring MVC​​​中使用消息转换器​​HttpMessageConverter​​​对JSON的转换提供了很好的支持,在​​Spring Boot​​中更进一步,对相关配置做了更进一步的简化。默认情况下,当开发者新创建一个Spring Boot项目后,添加Web依赖,

《Spring Boot+Vue全栈开发实战》读书笔记_spring_14


这个依赖中默认加入了​​jackson-databind​​作为JSON处理器,此时不需要添加额外的JSON处理器就能返回一段JSON了.

如果需要频繁地用到​​@ResponseBody​​​注解,那么可以采用​​@RestController​​​组合注解代替​​@Controller​​​和​​@ResponseBody​

这是Spring Boot自带的处理方式。如果采用这种方式,那么对于字段忽略、日期格式化等常见需求都可以通过注解来解决。这是通过Spring中默认提供的​​MappingJackson2HttpMessageConverter来实现​​​的.
​​​HttpMessageConverter​​ ,看名字就知道,这是一个消息转换工具,有两方面的功能:

  1. ​将服务端返回的对象序列化成 JSON 字符串​
  2. ​将前端传来的 JSON 字符串反序列化成 Java 对象​​​ 所有的 JSON 生成都离不开相关的 ​​HttpMessageConverter​​,SpringMVC 自动配置了​​Jackson​​ 和 ​​Gson​​ 的 HttpMessageConverter,Spring Boot 中又对此做了自动化配置:
  3. ​org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration​
  4. ​org.springframework.boot.autoconfigure.http.GsonHttpMessageConvertersConfiguration​

所以,如果用户使用 ​​jackson​​​ 和 ​​gson​​ 的话,没有其他额外配置,则只需要添加依赖即可。

修改转化器

添加一个MappingJackson2HttpMessageConverter,由​​@ConditionalOnMissingBean​​​确定。
嗯,我们温习一下条件化注解吧

《Spring Boot+Vue全栈开发实战》读书笔记_bc_15


《Spring Boot+Vue全栈开发实战》读书笔记_spring boot_16


《Spring Boot+Vue全栈开发实战》读书笔记_bc_17


当然这里我们也可以只定义一个 ​

​ObjectMapper​

package com.liruilong.spring_boot_demo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;


import java.text.SimpleDateFormat;

/**
* @Classname WebMvcConfig
* @Description TODO
* @Date 2021/6/4 10:02
* @Created Li Ruilong
*/
@Configuration
public class WebMvcConfig {

@Bean
MappingJackson2HttpMessageConverter mappingJackson2CborHttpMessageConverter(){
return new MappingJackson2HttpMessageConverter(objectMapper());
}

@Bean
ObjectMapper objectMapper() {
ObjectMapper om = new ObjectMapper();
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
return om;
}

}
4.1.2 自定义转换器

当然开发者在这里也可以根据实际需求​​自定义JSON转换器​​​。常见的JSON处理器除了​​jackson-databind​​​之外,还有​​Gson​​​和​​fastison​​,这里针对常见用法分别举例.

​Gson​

Gson是Google的一个开源JSON解析框架。使用Gson,需要先除去默认的jackson-databind,然后加入Gson依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>

由于​​Spring Boot​​​中默认提供了​​Gson的自动转换类GsonHttpMessageConvertersConfiguration​​​,因此Gson的依赖添加成功后,可以像使用​​jackson-databind那样直接使用Gson​​​。但是在Gson进行·转换时,如果想对日期数据进行格式化,那么还需要开发者自定义​​HttpMessageConverter​​​.自定义​​HttpMessageConverter​​可以通过如下方式。也可以直接使用Gson对象。

@Configuration
public class WebMvcConfig {
// @Bean
// GsonBuilder gsonBuilder() {
// GsonBuilder gsonBuilder = new GsonBuilder();
// gsonBuilder.setDateFormat("yyyy-MM-dd");
// return gsonBuilder;
// }
@Bean
GsonHttpMessageConverter gsonHttpMessageConverter() {
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat("yyyy-MM-dd");
GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
converter.setGson(gsonBuilder.create());
return converter;
}
}
​fastison​

​fastjson​​​是阿里巴巴的一个开源JSON解析框架,是目前JSON解析速度最快的开源框架,该框架也可以集成到Spring Boot中。不同于​​Gson​​​, ​​fastjson​​​继承完成之后并不能立马使用,需要开发者提供相应的​​HttpMessageConverter​​后才能使用,集成fastison的步骤如下。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
spring.http.encoding.force-response=true

对于​​FastlsonHttpMessageConverter​​​的配置,除了​​FastJsonHttpMessageConverter​​​这种方式之外,还有另一种方式。在Spring Boot项目中,当开发者引入​​spring-boo-starter-web​​​依赖之后,该依赖又依赖了​​spring-boot-autoconfigure​​​,在这个自动化配置中,有一个​​webMvcAutoConfiguration​​​类提供了对Spring MVC最基本的配置,如果某一项自动化配置不满足开发需求,开发者可以针对该项自定义配置,只需要实现​​WebMveConfigurer​​​接口即可(在Spring 5.0之前是通过继承​​WebMvcConfigurerAdapter​​类来实现的) ,代码如下:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setCharset(Charset.forName("UTF-8"));
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
converter.setFastJsonConfig(fastJsonConfig);
converter.setDefaultCharset(Charset.forName("UTF-8"));
converters.add(converter);
}

// @Bean
// FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
// FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
// FastJsonConfig fastJsonConfig = new FastJsonConfig();
// fastJsonConfig.setCharset(Charset.forName("UTF-8"));
// fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
// converter.setFastJsonConfig(fastJsonConfig);
// converter.setDefaultCharset(Charset.forName("UTF-8"));
// return converter;
// }
}

4.2静态资源访问

在​​Spring MVC​​​中,对于静态资源都需要开发者手动配置静态资源过滤。Spring Boot中对此也提供了​​自动化配置​​,可以简化静态资源过滤配置。

Spring MVC中的配置:

​xml​

<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/html/**" location="/html/"/>

由于这是一种Ant风格的路径匹配符,/** 表示可以匹配任意层级的路径,因此上面的代码也可以像下面这样简写:

<mvc:resources mapping="/**" location="/"/>

​java​​:重写 WebMvcConfigurationSupport 类中的addResourceHandlers方法,在该方法中配置静态资源位置即可

4.2.1默认策略

Spring Boot中对于Spring MVC的自动化配置都在​​webMvcAutoConfiguration​​​类中,因此对于默认的静态资源过滤策略可以从这个类中一窥究竟。在WebMvcAutoConfiguration类中有一个静态内部类​​webMvcAutoConfigurationAdapter​​​,实现了4.1节提到的​​WebMvcConfigurer​​​接口。​​webMvcConfigurer​​​接口中有一个方法​​addResourceHandlers​​​是用来配置静态资源过滤的。方法在​​WebMvcAutoConfigurationAdapter​​类中得到了实现,部分核心代码如下

《Spring Boot+Vue全栈开发实战》读书笔记_spring boot_18


Spring Boot在这里进行了​​默认的静态资源过滤配置​​​,其中​​staticPathPattern​​​默认定义在​​WebMvcProperties​​ 中

《Spring Boot+Vue全栈开发实战》读书笔记_java_19


​registration.addResourceLocations(this.resourceProperties.getStaticLocations());​​​获取到的默认静态资源位置定义在​​ResourceProperties​

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_20

在一个新创建的Spring Boot项目中,添加了​​spring-boot-starter-web​​​依赖之后,在​​resources​​​目录下分别创建​​4个目录​​, 4个目录中放入同名的静态资源(如图4-4所示,数字表示不同位置资源的优先级)

《Spring Boot+Vue全栈开发实战》读书笔记_spring_21

4.2.2自定义策略

自定义静态资源过滤策略有以下两种方式;

  1. 在配置文件中定义可以在​​application.properties​​中直接定义过滤规则和静态资源位置,
# 静态资源位置
spring.web.resources.static-locations=classpath:/static/
# 过滤规则
spring.mvc.static-path-pattern=/static/**

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_22

  1. ​Java编码定义​​​也可以通过Java编码方式来定义,此时只需要实现​​WebMveConfigurer接口​​​即可,然后实现该接口的​​addResourceHandlers​​方法,代码如下:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}

4.3文件上传.

​Spring MVC​​​对文件上传做了简化,在Spring Boot中对此做了更进一步的简化,文件上传更为方便。Java中的文件上传一共涉及两个组件,一个是​​CommonsMultipartResolver​​​,另一个是​​StandardServletMultipartResolver​​​.
其中​​​CommonsMultipartResolver​​​使用​​commons-fileupload​​​来处理multipart请求,而​​StandardServletMultipartResolver​​​则是基于​​Servlet 3.0​​​来处理multipart请求的,因此若使用StandardServletMultipartResolver,则不需要添加额外的jar包。​​Tomcat 7.0​​开始就支持Servlet3.0.

Spring Boot提供的文件上传自动化配置类​​MultiparAutoConfiguraton中​​​,默认也是采用​​StandardServletMultipartResolver​

《Spring Boot+Vue全栈开发实战》读书笔记_spring_23

spring.servlet.multipart.max-file-size=1KB
.....
4.3.1单文件上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="上传">
</form>
</body>
</html>
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("/yyyy/MM/dd/");
@PostMapping("/upload")
public String upload(MultipartFile file, HttpServletRequest req) {
String realPath = req.getServletContext().getRealPath("/");
String format = LocalDate.now().format(dateTimeFormatter);
String path = realPath + format;
File folder = new File(path);
if (!folder.exists()) {
folder.mkdirs();
}
String oldName = file.getOriginalFilename();
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
try {
file.transferTo(new File(folder, newName));
String s = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + format + newName;
return s;
} catch (IOException e) {
e.printStackTrace();
}
return "error";
}
4.3.2多文件上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/upload2" method="post" enctype="multipart/form-data">
<input type="file" name="files" multiple>
<input type="submit" value="上传">
</form>
</body>
</html>
@PostMapping("/upload2")
public void upload(MultipartFile[] files, HttpServletRequest req) {
String realPath = req.getServletContext().getRealPath("/");
String format = LocalDate.now().format(dateTimeFormatter);
String path = realPath + format;
File folder = new File(path);
if (!folder.exists()) {
folder.mkdirs();
}
try {
for (MultipartFile file : files) {
String oldName = file.getOriginalFilename();
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
file.transferTo(new File(folder, newName));
String s = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + format + newName;
System.out.println("s = " + s);
}
} catch (IOException e) {
e.printStackTrace();
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/upload3" method="post" enctype="multipart/form-data">
<input type="file" name="file1">
<input type="file" name="file2">
<input type="submit" value="上传">
</form>
</body>
</html>
@PostMapping("/upload3")
public void upload(MultipartFile file1, MultipartFile file2, HttpServletRequest req) {
String realPath = req.getServletContext().getRealPath("/");
String format = LocalDate.now().format(dateTimeFormatter);
String path = realPath + format;
File folder = new File(path);
if (!folder.exists()) {
folder.mkdirs();
}
try {
String oldName1 = file1.getOriginalFilename();
String newName1 = UUID.randomUUID().toString() + oldName1.substring(oldName1.lastIndexOf("."));
file1.transferTo(new File(folder, newName1));
String s1 = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + format + newName1;
System.out.println("s1 = " + s1);

String oldName2 = file2.getOriginalFilename();
String newName2 = UUID.randomUUID().toString() + oldName2.substring(oldName2.lastIndexOf("."));
file2.transferTo(new File(folder, newName2));
String s2 = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + format + newName2;
System.out.println("s2 = " + s2);
} catch (IOException e) {
e.printStackTrace();
}
}
4.3.3AJAX文件上传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
</head>
<body>
<div id="result"></div>
<input type="file" id="file">
<input type="button" value="上传" onclick="uploadFile()">
<script>
function uploadFile() {
var file = $("#file")[0].files[0];
var formData = new FormData();
formData.append("file", file);
formData.append("username", "javaboy");
$.ajax({
type:'post',
url:'/upload',
processData:false,
contentType:false,
data:formData,
success:function (msg) {
$("#result").html(msg);
}
})
}
</script>
</body>
</html>

4.4 @ControllerAdvice

顾名思义, ​​@ControllerAdvice​​​就是​​@Controller​​​的增强版。​​@ControllerAdvice​​​主要用来​​处理全局数据​​​,一般搭配​​@ExceptionHandier​​​. ​​@ModelAttribute​​​以及​​@InitBinder​​使用。

4.4.1 全局异常处理

上传文件大小超出限制。

@ControllerAdvice
//@Controller
//@RestControllerAdvice
//@RestController
public class MyGlobalException {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ModelAndView customException(MaxUploadSizeExceededException e) {
ModelAndView mv = new ModelAndView("javaboy");
mv.addObject("error", e.getMessage());
return mv;
}
}
4.4.2 添加全局数据

​@ControllerAdvice​​​是一个全局数据处理组件,因此也可以在​​@ControllerAdvice​​​中配置全局数据,使用​​@ModelAtribute注解进行配置​​,代码如下:

@ControllerAdvice
public class MyGlobalData {
@ModelAttribute("info")
public Map<String,String> mydata() {
Map<String, String> info = new HashMap<>();
info.put("username", "javaboy");
info.put("address", "www.javaboy.org");
return info;
}
}

在全局配置中添加​​mydata​​​方法,返回一个map.该方法有一个注解​​@ModelAttribute​​​,其中的value属性表示这条返回数据的​​key​​​,而方法的返回值是返回数据的​​value​​​,此时在任意请求的​​Controller​​中,通过方法参数中的Model都可以获取info的数据。

4.4.3 请求参数预处理

​@ControllerAdvice​​​结合​​@InitBinder​​​还能实现​​请求参数预处理​​​,即将表单中的​​数据​​​绑定到​​实体类​​​上时进行一些额外处理。
多个实体类存在相同的字段时,会合并字段值,使用ControllerAdvice来做预处理。

@ControllerAdvice
public class MyGlobalData {

@InitBinder("b")
public void b(WebDataBinder binder) {
binder.setFieldDefaultPrefix("b.");
}
@InitBinder("a")
public void a(WebDataBinder binder) {
binder.setFieldDefaultPrefix("a.");
}

}
@RestController
public class BookController {

@PostMapping("/book")
public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
System.out.println("book = " + book);
System.out.println("author = " + author);
}
}

在GlobalConfig类中创建两个方法,

  • 第一个@InitBinder(“b”)表示该方法是处理@ModelAttribute(")对应的参数的,
  • 第二个@nitBinder(“a”)表示该方法是处理@ModelAttribute(“a”)对应的参数的。

在WebDataBinder对象中,还可以设置允许的字段、禁止的字段、必填字段以及验证器等。

4.5 自定义错误页

​Spring Boot​​​中的全局异常处理。在处理异常时,开发者可以根据实际情况返回不同的页面,但是这种异常处理方式一般用来处理​​应用级别​​​的异常,有一些​​容器级别​​​的错误就处理不了,例如​​Filter 中抛出异常​​​,使用​​@ControllerAdvice​​​定义的全局异常处理机制就​​无法处理​​。

因此, Spring Boot中对于异常的处理还有另外的方式,这就是本节要介绍的内容。在Spring Boot 中,默认情况下,如果用户在发起请求时发生了404错误, Spring Boot会有一个默认的页面展示给用户.

《Spring Boot+Vue全栈开发实战》读书笔记_bc_24


Spring Boot中的错误默认是由​​BasicErrorController类​​来处理的,该类中的核心方法主要有两个:

《Spring Boot+Vue全栈开发实战》读书笔记_java_25


·​​errorHtml​​​方法用来返回错误​​HTML​​​页面, ​​error​​​用来返回错误​​JSON​​​,具体返回的是HTML还是​​JSON​​​,则要看请求头的​​Accept参数​​​。返回JSON的逻辑很简单,不必过多介绍,返回HTML的逻辑稍微有些复杂,在​​errorHtml​​​方法中,通过调用​​resolveErrorView​​​方法来获取一个错误视图的​​ModelAndView​​​,而​​resolveErrorView​​​方法的调用最终会来到​​DefaultErrorViewResolver​​​类中。​​DefaultErrorViewResolver​​类是Spring Boot中默认的错误信息视图解析器,部分源码如下:

《Spring Boot+Vue全栈开发实战》读书笔记_spring boot_26

4.5.1 简单配置.静态页面

要​​自定义错误页面​​​其实很简单,提供​​4xx和Sxx​​​页面即可。如果开发者不需要向用户展示详细的错误信息,那么可以把错误信息定义成静态页面,直接,在​​resources/static​​​ 录下创建​​error​​​目录,然后在​​error​​目录中创建错误展示页面。错误展示页面的命名规则有两种:

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_27

  • 一种是​​4xx.html​​​、 ​​5xx.html​​;
  • 另一种是直接使用响应码命名文件,例如​​404.html.405.html, 500.html​​.第二种命名方式划分得更细,当出错时,不同的错误会展示不同的错误页面
.模板页面

​Spring Boot​​​在这里一共返回了​​5条错误相关​​​的信息,分别是​​timestamp​​​, ​​status​​​, ​​error​​​, ​​message​​​以及​​path​

《Spring Boot+Vue全栈开发实战》读书笔记_spring boot_28


若用户定义了多个错误页面,则响应码html页面的优先级​高​​4xx.html. Sxx.tml页面​的优先级,即若当前是一个404错误,则优先展示​404.html​而不是​4xx.html​;​动态​页面的优级​高​​静态​页面,即若​resources/templates​​resource/static​ 同时定义了​4xx.html​,则​优先​展示​resources/templates/4xx.html.​

4.5.2 复杂配置

上面这种配置还是不够灵活,只能定义​​HTML页面​​​,无法处理​​JSON的定制​​​。Spring Boot中支持对​​Error信息​​​的深度定制,接下来将从三个方面介绍深度定制:​​自定义Error数据​​​、​​自定义Error视图​​​以及​​完全自定义​​。

1.自定义Error数据

自定义Error数据就是对​​返回的数据进行自定义​​​。Spring Boot返回的Error信息一共有5条,分别是timestamp, status, error, message以及path,在BasicErrorController的errorHtml方法和error方法中,都是通过​​getErrorAttributes​​​方法获取​​Error信息​​​的。该方法最终会调用到​​DefaultErrorAttributes类​​​的​​getErrorAttributes​​​方法,而​​DefaultErrorAttributes类​​​是在​​ErrorMvcAutoConfiguration​​​中默认提供的.​​ErrorMvcAutoConfiguration​​​类的​​errorAttributes方法​​源码如下:

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_29


源码中可以看出,当系统​​没有提供ErrorAttributes​​​时才会采用​​DefaultErrorAttributes​​.因此自定义错误提示时,只需要自己提供一个ErrorAttributes 可,而DefaultErroAttributes是ErrorAttributes的子类,因此只需要继承DefaultErrorAttributes即可

@Component
public class MyErrorAtributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map<String, Object> map = super.getErrorAttributes(webRequest, options);
if ((Integer) map.get("status") == 404) {
map.put("message", "页面不存在");
}
return map;
}
}
2,自定义Error视图

​Error视图​​​是展示给用户的页面,在​​BasicErrorController​​​的​​errorHtml​​​方法中调用​​resolveErrorView​​​方法获取一个​​ModelAndView​​​实例。 ​​resolveErrorView​​​方法是由​​ErrorViewResolver​​​提供的,通过​​ErrorMvcAutoConfiguration​​​类的源码可以看到Spring Boot默认采用的ErrorViewResolver是​​DefaultErrorViewResolver​​. ErrorMvcAutoConfiguration部分源码如下:

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_30

@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {
public MyErrorViewResolver(ApplicationContext applicationContext, WebProperties.Resources resources) {
super(applicationContext, resources);
}

@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
Map<String, Object> map = new HashMap<>();
map.putAll(model);
if ((Integer) model.get("status") == 500) {
map.put("message", "服务器内部错误");
}
ModelAndView view = new ModelAndView("javaboy/999",map);
return view;
}
}
3,完全自定义

前面提到的两种自定义方式都是对​​BasicErrorController类​​​中的某个环节进行修补。查看Error自动化配置类​​ErrorMvcAutoConfiguration​​​,读者可以发现​​BasicErrorController​​ 身只是一个默认的配置,相关源码如下:

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_31


从这段源码中可以看到,若开发者没有提供自己的​​ErrorController​​​,则​​Spring Boot​​​提供​​BasicErrorController​​​作为默认的​​ErrorController​​​,因此,如果开发者需要更加灵活地对​​Error视图​​​和​​数据​​​进行处理,那么只需要提供自己的​​ErrorController​​​即可。提供自己的​​ErrorController​​​有两种方式:一种是实现​​ErrorController接口​​​,另一种是直接继承​​BasicErrorController​​​,由于​​ErorController​​​接口只提供一个待实现的方法,而​​BasicErrorController​​​已经实现了很多功能,因此这里选择第二种方式,即通过继承​​BasicErrorController​​​来实现自己的​​ErrorController​​.具体定义如下:

@Controller
public class MyErrorController extends BasicErrorController {
@Autowired
public MyErrorController(ErrorAttributes errorAttributes,
ServerProperties serverProperties,
List<ErrorViewResolver> errorViewResolvers) {
super(errorAttributes, serverProperties.getError(), errorViewResolvers);
}
@Override
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
model.put("custommsg", "出错啦!");
ModelAndView modelAndView = new ModelAndView("myErrorPage", model, status);
return modelAndView;
}
@Override
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
body.put("custommsg", "出错啦!");
HttpStatus status = getStatus(request);
return new ResponseEntity<>(body, status);
}
}
  • 自定义MyErrorController继承自​BasicErrorController​并添加​@Controller​注解,将MyErrorController ​注册​​Spring MVC容器​中·
  • 由于BasicErrorController没有无参构造方法,因此在创建​BasicErrorController​实例时需要​传递参数​,在​MyErrorController​的构造方法上添加​@Autowired注解​注入​所需参数​
  • 参考BasicErrorController中的实现,重写errorHtml和error方法,对Error的视图和数据进行充分的自定义。

4.6 CORS支持

​CORS (Cross-Origin Resource Sharing)​​​是由​​w3C​​​制定的一种​​跨域资源共享技术标准​​​,其目的就是为了解决​​前端的跨域请求​​​。在Java EE开发中,最常见的前端跨域请求解决方案是​​JSONP​​​,但是​​JSONP只支持GET请求​​​,这是一个很大的缺陷,而​​CORS则支持多种HTTP请求方法​​​。以​​CORS​

《Spring Boot+Vue全栈开发实战》读书笔记_bc_32

响应头中有一个​​Access-Control-Allow-Origin​​​字段,用来​​记录​​​可以​​访问该资源的域​​​。当浏览器收到这样的响应头信息之后,提取出​​Access-Control-Allow-Origin字段​​​中的值,发现该值包含​​当前页面​​​所在的​​域​​​,就知道这个跨域是被允许的,因此就不再对前端的跨域请求进行·限制。这就是GET请求的整个跨域流程,在这个过程中,前端请求的代码不需要修改,主.要是后端进行处理。这个流程主要是针对​​GET​​​, ​​POST​​​以及​​HEAD​​​请求,并且没有自定义请求头,如果用户发起一个​​DELETE​​​请求、​​PUT​​​请求或者​​自定义​​了请求头,流程就会稍微复杂一些。

以​​DELETE​​​请求为例,当前端发起​​一个DELETE请求​​​时,这个请求的处理会经过​​两个步骤​​​。
​​​第一步​​:发送一个OPTIONS请求。代码如下:

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_33


这个请求将向服务端询问是否具备该资源的DELETE权限,服务端会给浏览器一个响应,代码如下:


《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_34


服务端给浏览器的响应, ​

​Allow​

​头信息表示服务端支持的请求方法,这个请求相当于一个探测请求,当​

​浏览器​

​分析了请求头字段之后,知道​

​服务端支持本次请​

​求,则进入第二步。


​第二步​

​:​

​发送DELETE请求​

​。接下来浏览器就会发送一个跨域的DELETE请求。

在传统的Java EE开发中,可以通过过滤器统一配置,而Spring Boot中对此则提供了更加简洁的解决方案。在Spring Boot中配置CORS的步骤如下:

3.配置跨域

跨域有两个地方可以配置:

  1. 一个是直接在相应的请求方法上加注解: 这种配置方式是一种​​细粒度的配置​​.可以控制到每一个方法上。
  • ​@CrossOrigin​​​中的​​value​​​表示支持的​​域​​​,这里表示来自​​http://ocalhost:8081​​​域的请求是​​支持跨域​​的.
  • ​maxAge​​​表示​​探测请求​​​的​​有效期​​​,在前面的讲解中,读者已经了解到对于​​DELETE​​​, ​​PUT​​​请求或者有自定义头信息的请求,在执行过程中会先发送探测请求,​​探测请求不用每次都发送,可以配置一个有效期,有效期过了之后才会发送探测请求​​。这个属性默认是1800秒,即30分钟。
  • ​allowedHeaders​​​表示​​允许的请求头​​​,​​*​​​表示所有的​​请求头都被允许​​。
@RestController
@RequestMapping("/book")
public class BookController {
@PostMapping("/")
@CrossOrigin(value = "http://localhost:8081"
,maxAge = 1800,allowedHeaders = "*")
public String addBook(String name) {
return "receive:" + name;
}
@DeleteMapping("/{id}")
@CrossOrigin(value = "http://localhost:8081"
,maxAge = 1800,allowedHeaders = "*")
public String deleteBookById(@PathVariable Long id) {
return String.valueOf(id);
}
}
  1. 另一种全局配置,代码如下:
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("*")
.allowedMethods("*")
.allowedOrigins("http://localhost:8081")
.maxAge(1800);
}

全局配置需要自定义类实现​​WebMvcConfigurer​​​接口,然后实现接口中的​​addCorsMappings​​​方法。,在​​addCorsMappings​​方法中

  • ​addMapping​​表示对哪种格式的请求路径进行跨域处理;
  • ​allowedHeaders​​表示允许的请求头,默认允许所有的请求头信息;
  • ​allowedMethods​​​表示允许的请求方法,默认是GET. POST和HEAD,​​*​​表示支持所有的请求方法;
  • maxAge表示探测请求的有效期;
  • allowedOrigins表示支持的域。

4.7配置类与XML配置.

​Spring Boot​​​推荐使用Java来完成相关的配置工作。在项目中,不建议将所有的配置放在一个配置类中,可以根据不同的需求提供不同的​​配置类​​​,例如专门处理​​Spring Security的配置类​​​、​​提供Bean的配置类​​​、​​Spring MVC相关的配置类​​​。这些配置类上都需要添加​​@Configuration注解​​​。
​​​@ComponentScan注解​​​会扫描所有的Spring组件,也包括​​@Configuration​​​,​​@ComponentScan​​​注解在项目入口类的​​@Spring BootApplication​​​注解中已经提供,因此在实际项目中只需要按需提供相关配置类即可。Spring Boot中并不推荐使用XML配置,建议尽量用​​Java配置代替XML配置​​,本书中的案例都是以Java配置为主。

如果开发者需要​​使用XML配置​​​,只需在​​resources目录​​​下提供配置文件,然后通过​​@ImportResource加载配置文件​​即可。例如,有一个Book类如下:

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_35

4.8注册拦截器

​Spring MVC中​​​提供了​​AOP风格​​​的拦截器,拥有更加精细的拦截处理能力。​​Spring Boot​​中拦.截器的注册更加方便,步骤如下:

创建拦截器实现Handlerinterceptor接口
ublic class MyInterceptor implements HandlerInterceptor {

//该方法返回 false,请求将不再继续往下走
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;
}

//Controller 执行之后被调用
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}

//preHandle 方法返回 true,afterCompletion 才会执行。
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}

拦截器中的方法将按​​preHandle​​​-​​Controller​​​-​​postHandle​​​-​​afterCompletion​​的顺序执行。注意,只有preHandle方法返回true时后面的方法才会执行。当拦截器链内存在​​多个拦截器时​​​, ​​postHandler在拦截器链内的所有拦截器返回成功时才会调用​​​,而​​afterCompletion只有preHandle返回true才调用​​​,但若拦截器链内的第一个拦截器的​​preHandle​​​方法返回​​false​​,则后面的方法都不会执行。

配置拦截器。定义配置类进行拦截器的配置,代码如下:

自定义类实现​​webMveConfigurer接口​​​,实现接口中的​​addInterceptors方法​​​。其中,​​addPathPatterns 表示拦截路径​​​, ​​excludePathPatterns表示排除的路径​​。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/hello");
}
}

4.9启动系统任务

有一些特殊的任务需要在​​系统启动时​​​执行,例如​​配置文件加载​​​、​​数据库初始化​​​等操作。如果没有​​使用Spring Boot​​​,这些问题可以在​​Listener​​​中解决。Spring Boot对此提供了两种解决方案:​​CommandLineRunner​​​和​​ApplicationRunner​​​. ​​CommandLineRunner​​​和​​ApplicationRunner​​基本一致,差别主要体现在参数上。

4.9.1 CommandLineRunner

Spring Boot项目在启动时会遍历所有​​CommandLineRunner​​​的实现类并调用其中的run方法,如果整个系统中有多个​​CommandLineRunner的实现类​​​,那么可以使用​​@Order注解​​对这些实现类的调用顺序进行排序。

《Spring Boot+Vue全栈开发实战》读书笔记_bc_36

4.9.2 ApplicationRunner

​ApplicationRunner​​​的用法和​​CommandLineRunner​​​基本一致,区别主要体现在​​run方法​​的参数上。

@Component
@Order(98)
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
//获取没有键的参数,获取到的值和 commandlinerunner 一致
List<String> nonOptionArgs = args.getNonOptionArgs();
System.out.println("nonOptionArgs1 = " + nonOptionArgs);
Set<String> optionNames = args.getOptionNames();
for (String optionName : optionNames) {
System.out.println(optionName + "-1->" + args.getOptionValues(optionName));
}
//获取命令行中的所有参数
String[] sourceArgs = args.getSourceArgs();
System.out.println("sourceArgs1 = " + Arrays.toString(sourceArgs));
}
}
@Component
@Order(97)
public class MyApplicationRunner2 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
//获取没有键的参数,获取到的值和 commandlinerunner 一致
List<String> nonOptionArgs = args.getNonOptionArgs();
System.out.println("nonOptionArgs2 = " + nonOptionArgs);
Set<String> optionNames = args.getOptionNames();
for (String optionName : optionNames) {
System.out.println(optionName + "-2->" + args.getOptionValues(optionName));
}
//获取命令行中的所有参数
String[] sourceArgs = args.getSourceArgs();
System.out.println("sourceArgs2 = " + Arrays.toString(sourceArgs));
}
}

​@Order注解​​​依然是用来描述执行顺序的,数字越小越优先执行。不同于CommandLineRunner中run方法的String数组参数,这里​​run方法​​​的参数是一个​​ApplicationArguments​​​对象,如果想从​​ApplicationArguments​​​对象中获取入口类中1main方法1接收的参数,调用​​ApplicationArguments​​​中的​​getNonOptionArgs方法​​​即可. ​​ApplicationArguments​​​中的​​getOptionNames方法​​​用来获取项目启动命令行中​​参数的key​​​,例如将本项目打成​​jar包​​​,运行​​java-jar xxx.jar-name-Michael​​​命令来启动项目,此时​​getOptionNames​​​方法获取到的就是name,而​​getOptionValues​​​方法则是获取相应的​​value​​.

4.10整合Servlet, Filter和Listener.

Spring Boot中对于整合这些基本的Web组件也提供了很好的支持。在一个Spring Boot Web项目中添加如下三个组件:

@WebFilter("/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig){
System.out.println("MyFilter>>>init");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("MyFilter>>>doFilter");
chain.doFilter(request,response);
}

@Override
public void destroy() {
System.out.println("MyFilter>>>destroy");
}
}
@WebListener
public class MyListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("MyListener>>>requestDestroyed");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("MyListener>>>requestInitialized");
}
}
@WebServlet("/my")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp){
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp){
System.out.println("name>>>"+req.getParameter("name"));
}
}

启动类需要的配置:
在项目入口类上添加​​​@ServletComponentScan注解​​​,实现对​​Servlet​​​, ​​Filter​​​以及​​Listener​​的扫描,代码如下:

@SpringBootApplication
@ServletComponentScan("org.javaboy.filter")
public class FilterApplication {

public static void main(String[] args) {
SpringApplication.run(FilterApplication.class, args);
}

}

4.11 路径映射.

有一些页面在​​控制器​​​中不需要加载数据,只是完成·​​简单的跳转​​​,对于这种页面,可以​​直接配置路径映射​​​,​​提高访问速度​​​。例如,有两个​​Thymeleaf做模板​​​的页面​​login.html​​​和​​index.tml​​​,直接在MVC配置中重写​​addViewControllers​​方法配置映射关系即可:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// registry.addViewController("/02").setViewName("02");
registry.addViewController("/02").setViewName("02");
}
}

4.12 配置AOP

4.12.1 AOP简介
​​AOP中的相关知识​​
4.12.2 Spring Boot支持

​Spring Boot​​​在​​Spring​​​的基础上对​​AOP​​​的配置提供了​​自动化配置​​​解决方案​​spring-boot-starter-aop​​,使开发者能够更加便捷地在Spring Boot项目中使用AOP,配置步骤如下。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Component
@Aspect
public class LogAspect {
@Pointcut("execution(* org.javaboy.aop.service.*.*(..))")
public void pc1() {

}

@Before("pc1()")
public void before(JoinPoint jp) {
String name = jp.getSignature().getName();
System.out.println(name + " 方法开始执行了...");
}

@After("pc1()")
public void after(JoinPoint jp) {
String name = jp.getSignature().getName();
System.out.println(name + " 方法执行结束了...");
}

@AfterReturning(value = "pc1()", returning = "s")
public void afterReturning(JoinPoint jp, String s) {
String name = jp.getSignature().getName();
System.out.println(name + " 方法的返回值是 " + s);
}

@AfterThrowing(value = "pc1()", throwing = "e")
public void afterThrowing(JoinPoint jp, Exception e) {
String name = jp.getSignature().getName();
System.out.println(name + " 方法抛出了异常 " + e.getMessage());
}

@Around("pc1()")
public Object around(ProceedingJoinPoint pjp) {
try {

//类似于反射中的 invoke 方法
Object proceed = pjp.proceed();
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}

4.13 其他

4.13.1 自定义欢迎页

Spring Boot项目在启动后,首先会去​​静态资源​​​路径下查找​​index.html​​​作为​​首页文件​​​,若查找不到,则会去查找动态的​​index文件​​作为首页文件.

  • 使用​​静态的index.html​​​页面作为项目首页,那么只需在​​resources/static​​​目录下创建​​index.html​​文件即可。
  • 若想使用动态页面作为项目首页,则需在​​resources/templates​​​目录下创建​​index.html​​​(使用Thymeleaf模板)或者​​index.fl (使用FreeMarker模板)​​​ ,然后在​​Controller中返回逻辑视图名​​,代码如下:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("index");

}
}
4.13.2 自定义favicon

​favicon.ico​​​是浏览器选项卡左上角的图标,可以放在​​静态资源路径​​​下或者​​类路径​​​下,静态资源路径下的​​favicon.ico​​​优先级​​高于​​​类路径下的​​favicon.ico​​​。
在线转换网站http:/inaconvert.com/cn/convert-to-ico.php将一张普通图片转为.ico图

4.13.3 除去某个自动配置

​Spring Boot​​​中提供了大量的自动化配置类,例如上文提到过的​​ErrorMvcAutoConfiguration​​​、​​ThymeleafAutoConfiguration​​​,​​FreeMarkerAutoConfiguration​​​, ​​MultipartAutoConfiguration​​​等,这些​​自动化配置​​​可以减少相应​​操作的配置​​​,达到开箱即用的效果。在​​Spring Boot​​​的入口类上有一个​​@Spring BootApplication​​​注解。该注解是一个组合注解, 由​​@Spring BootConfiguration​​​、​​@EnableAutoConfiguration​​​以及​​@ComponentScan​​​组成,其中​​@EnableAutoConfiguration​​注解开启自动化配置,相关的自动化配置类就会被使用。如果开发者不想使用某个自动化配置,按如下方式除去相关配置即可.

@SpringBootApplication
@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})
public class OtherApplication {
public static void main(String[] args) {
SpringApplication.run(OtherApplication.class, args);
}
}
4.13.4 使用类型转化器
@Component
public class MyDateConverter implements Converter<String, Date> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date convert(String source) {
try {
return sdf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}

第5章Spring Boot整合持久层技术.

​Spring Boot​​​中对常见的持久层框架都提供了自动化配置,例如​​JabcTemplate​​​, ​​JPA​​​等, ​​MyBatis​​​的​​自动化配置​​则是MyBatis官方提供的。

5.1 整合JdbcTemplate

​JdbcTemplate​​​是​​Spring​​​提供的一套​​JDBC模板框架​​​,利用​​AOP​​​技术来解决直接使用​​JDBC时大量重复代码​​​的问题。​​JdbcTemplate​​​虽然没有​​MyBatis​​​那么灵活,但是比直接使用​​JDBC​​​要方便很多。​​Spring Boot​​​中对​​JdbcTemplate​​​的使用提供了​​自动化配置类JdbcTemplateConfiguration​​,部分源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class,
NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {

}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(JdbcOperations.class)
class JdbcTemplateConfiguration {

@Bean
@Primary
JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
JdbcProperties.Template template = properties.getTemplate();
jdbcTemplate.setFetchSize(template.getFetchSize());
jdbcTemplate.setMaxRows(template.getMaxRows());
if (template.getQueryTimeout() != null) {
jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds());
}
return jdbcTemplate;
}
}

需要的依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

​spring-bool-starter-jdbc​​​ 中提供了​​spring-jdbc​​​,另外还加入了​​数据库驱动依赖​​​和​​数据库连接池依赖​

# 数据源1
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.username=root
spring.datasource.one.password=123
spring.datasource.one.url=jdbc:mysql:///chapter05-1
  • 创建BookDao,注入​​JdbcTemplate​​​.由于已经添加了​​spring-jdbc​​​相关的依赖, ​​JabcTemplate​​​会被自动​​注册​​​到​​Spring容器​​中,因此这里可以直接注入JdbcTemplate使用。
  • 在​​JdbcTemplate​​​中,​​增删改​​​三种类型的操作主要使用​​update​​​和​​batchUpdate​​​方法来完成.​​query​​​和​​queryForObject​​​方法主要用来完成​​查询​​​功能。另外,还有​​execute​​​方法可以用来执行任意的​​sQL​​​. ​​call​​​方法用来调用​​存储过程​​等。
  • 在执行查询操作时,需要有一个​​RowMapper​​​将查询出来的​​列和实体类​​​中的属性-一对应起来。如果​​列名​​​和​​属性名​​​都是相同的,那么可以直接使用​​BeanPropertyRowMapper​​​;如果列名和属性名不同,就需要开发者自己实现​​RowMapper接口​​,将列和实体类属性-一对应起来。
@Repository
public class BookDao {
@Autowired
JdbcTemplate jdbcTemplate;
public int addBook(Book book) {
return jdbcTemplate.update("INSERT INTO book(name,author) VALUES (?,?)",
book.getName(), book.getAuthor());
}
public int updateBook(Book book) {
return jdbcTemplate.update("UPDATE book SET name=?,author=? WHERE id=?",
book.getName(), book.getAuthor(), book.getId());
}
public int deleteBookById(Integer id) {
return jdbcTemplate.update("DELETE FROM book WHERE id=?", id);
}
public Book getBookById(Integer id) {
return jdbcTemplate.queryForObject("select * from book where id=?",
new BeanPropertyRowMapper<>(Book.class), id);
}
public List<Book> getAllBooks() {
return jdbcTemplate.query("select * from book",
new BeanPropertyRowMapper<>(Book.class));
}
}
public int addUser2(User user) {
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
int result = jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement("insert into user (username,address) values(?,?)", Statement.RETURN_GENERATED_KEYS);
ps.setString(1, user.getUsername());
ps.setString(2, user.getAddress());
return ps;
}
}, keyHolder);
user.setId(keyHolder.getKey().longValue());
return result;
}
public List<User> getAllUsers() {
List<User> list = jdbcTemplate.query("select * from user", new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
String username = resultSet.getString("username");
String address = resultSet.getString("address");
long id = resultSet.getLong("id");
User user = new User();
user.setId(id);
user.setUsername(username);
user.setAddress(address);
return user;
}
});
return list;
}

5.2整合MyBatis

​MyBatis​​​是一款优秀的持久层框架,原名叫作​​iBaits​​​, 2010年由ApacheSoftwareFoundation迁移到​​Google Code​​​并改名为​​MyBatis,​​​ 2013年又迁移到​​GitHub​​​上。​​MyBatis​​​支持定制化​​SQL​​​、​​存储过程​​​以及​​高级映射​​​。​​MyBatis​​​几乎避免了所有的​​JDBC​​​代码手动设置参数以及获取结果集。在传统的​​SSM​​​框架整合中,使用MyBatis需要大量的​​XML​​配置,而在Spring Boot中, MyBatis官方提供了一套自动化配置方案,可以做到MyBatis开箱即用。具体使用步骤如下。

  • 添加MyBatis依赖、数据库驱动依赖以及数据库连接池
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql:///chapter05
spring.datasource.username=root
spring.datasource.password=123
方法一
@SpringBootApplication
@MapperScan(basePackages = "org.javaboy.mybatis.mapper")
public class MybatisApplication {

public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class, args);
}

}
  • 一种简单的方式是在配置类上添加​​@MapperScan("org.sang.mapper")​​​注解,表示扫描​​org.sang.mapper​​​包下的所有接口作为​​Mapper​​​,这样就不需要在每个接口上配置​​@Mapper​​注解了。
public interface UserMapper {
@Select("select * from user where id=#{id}")
User getUserById(Long id);

@Results({
@Result(property = "address",column = "address1")
})
@Select("select * from user")
List<User> getAllUsers();

@Insert("insert into user (username,address1) values (#{username},#{address})")
@SelectKey(statement = "select last_insert_id()",keyProperty = "id",before = false,resultType = Long.class)
Integer addUser(User user);

@Delete("delete from user where id=#{id}")
Integer deleteById(Long id);

@Update("update user set username=#{username} where id=#{id}")
Integer updateById(String username, Long id);
}
方法二

指明该类是一个​​Mapper​​​:第一种如前面的代码所示,在​​BookMapper​​上添加@Mapper注解,表明该接口是一个MyBatis中的Mapper,这种方式需要在每一个Mapper上都添加注解;

@Mapper
public interface BookMapper {
int addBook(Book book);
int deleteBookById(Integer id);
int updateBookById(Book book);
Book getBookById(Integer id);
List<Book> getAllBooks();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.sang.mapper.BookMapper">
<insert id="addBook" parameterType="org.sang.model.Book">
INSERT INTO book(name,author) VALUES (#{name},#{author})
</insert>
<delete id="deleteBookById" parameterType="int">
DELETE FROM book WHERE id=#{id}
</delete>
<update id="updateBookById" parameterType="org.sang.model.Book">
UPDATE book set name=#{name},author=#{author} WHERE id=#{id}
</update>
<select id="getBookById" parameterType="int" resultType="org.sang.model.Book">
SELECT * FROM book WHERE id=#{id}
</select>
<select id="getAllBooks" resultType="org.sang.model.Book">
SELECT * FROM book
</select>
</mapper>
  • 针对​​BookMapper​​​接口中的每一个方法都在​​BookMapper.xml​​中列出了实现
  • ​#{}​​​用来代替接口中的参数,实体类中的属性可以直接通过​​#(实体类属性名}​​获取。
配置pom.xml文件

在​​Maven​​​工程中, ​​XML​​​配置文件建议写在​​resources​​​目录下(同包同级目录),当​​Mapper.xml​​​文件写在包下, ·​​Maven​​​在运行时会​​忽略​​​包下的​​XML文件​​​,因此需要在​​pom.xml​​​文件中重新指明​​资源文件位置​​,配置如下:

<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
......
</build>

也可以自定义​​resources​​​下​​mapper​​位置

mybatis.mapper-locations=classpath:mapper/*.xml

5.3整合Spring Data JPA

​JPA (Java Persistence API)​​​和​​Spring Data​​​是两个范畴的概念。​​JPA​​​则是一种​​ORM​​​规范, ​​JPA​​​和​​Hibernate​​​的关系就像​​JDBC​​​与​​JDBC驱动​​​的关系,即​​JPA​​​制定了​​ORM​​​规范,而​​Hibernate​​​是这些规范的实现(事实上,是先有Hibernate后有JPA, JPA规范的起草者也是Hibernate的作者) ,因此从功能上来说, ​​JPA​​​相当于​​Hibernate​​的一个子集。

​Spring Data​​​是​​Spring​​​的一个子项目,致力于​​简化数据库访问​​​,通过​​规范的方法名称​​​来分析开发者的意图,进而减少数据库访问层的代码量。​​Spring Data​​​不仅支持​​关系型数据库​​​,也支持​​非关系型数据库​​​。​​Spring Data JPA​​​可以有效 ​​简化​​​关系型数据库访问代码。Spring Boot整合​​Spring Data JPA​​的步骤如下:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql:///jpa
spring.datasource.username=root
spring.datasource.password=123
spring.jpa.show-sql=true
spring.jpa.database=mysql
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57InnoDBDialect
#spring.jpa.properties.database=mysql
#spring.jpa.properties.hibernate.hbm2ddl.auto=update
#spring.jpa.properties.show-sql= true
  • ​@Entity​​​注解表示该类是一个实体类,在项目启动时会根据该类自动生成一张表,表的名称即​​@Entity​​​注解中​​name​​的值,如果不配置name,默认表名为类名。所有的实体类都要有主键,
  • ​@ld​​​注解表示该属性是一个主键, ​​@GeneratedValue​​​注解表示主键自动生成, ​​strategy​​​则表示主键的生成策略。默认情况下,生成的表中字段的名称就是实体类中属性的名称,通过​​@Column​​​注解可以定制生成的字段的属性, ​​name​​​表示该属性对应的数据表中字段的名称, ​​nullable​​表示该字段非空。
  • ​@Transient​​注解表示在生成数据库中的表时,该属性被忽略,即不生成对应的字段。
@Entity(name = "t_book")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "book_name",nullable = false)
private String name;
private String author;
private Float price;
@Transient
private String description;
//省略getter/setter
}
  • 自定义​​BookDao​​​继承自​​JpaRepository​​​. ​​JpaRepository​​​中提供了一些​​基本的数据操作方法​​,有基本的增删改查、分页查询、排序查询等。
  • 第​​2行​​​定义的方法表示查询以某个​​字符开始​​的所有书。·
  • 第​​3行​​​定义的方法表示查询​​单价大于某个值​​的所有书。
  • 在​​Spring Data JPA​​​中,只要方法的定义符合既定规范, ​​Spring Data​​​就能分析出开发者的意图,从而​​避免开发者定义SQL​​​所谓的既定规范,就是一定的​​方法命名规则​​。
public interface BookDao extends JpaRepository<Book,Integer>{
List<Book> getBooksByAuthorStartingWith(String author);
List<Book> getBooksByPriceGreaterThan(Float price);
@Query(value = "select * from t_book where id=(select max(id) from t_book)",nativeQuery = true)
Book getMaxIdBook();
@Query("select b from t_book b where b.id>:id and b.author=:author")
List<Book> getBookByIdAndAuthor(@Param("author") String author, @Param("id") Integer id);
@Query("select b from t_book b where b.id<?2 and b.name like %?1%")
List<Book> getBooksByIdAndName(String name, Integer id);
}
public interface BookDao extends JpaRepository<Book,Long> {
List<Book> getBookByAuthorIs(String author);

@Query(nativeQuery = true,value = "select * from t_book where id=(select max(id) from t_book)")
Book maxIdBook();

@Query("update t_book set b_name=:name where id=:id")
@Modifying
void updateBookById(String name, Long id);
}

支持的命名规则如表所示:

《Spring Boot+Vue全栈开发实战》读书笔记_java_37


部分方法直接由JpaRepository

@Service
public class BookService {
@Autowired
BookDao bookDao;
public void addBook(Book book) {
bookDao.save(book);
}
public Page<Book> getBookByPage(Pageable pageable) {
return bookDao.findAll(pageable);
}
public List<Book> getBooksByAuthorStartingWith(String author){
return bookDao.getBooksByAuthorStartingWith(author);
}
public List<Book> getBooksByPriceGreaterThan(Float price){
return bookDao.getBooksByPriceGreaterThan(price);
}
public Book getMaxIdBook(){
return bookDao.getMaxIdBook();
}
public List<Book> getBookByIdAndAuthor(String author, Integer id){
return bookDao.getBookByIdAndAuthor(author, id);
}
public List<Book> getBooksByIdAndName(String name, Integer id){
return bookDao.getBooksByIdAndName(name, id);
}
}
@GetMapping("/findAll")
public void findAll() {
PageRequest pageable = PageRequest.of(2, 3);
Page<Book> page = bookService.getBookByPage(pageable);
System.out.println("总页数:"+page.getTotalPages());
System.out.println("总记录数:"+page.getTotalElements());
System.out.println("查询结果:"+page.getContent());
System.out.println("当前页数:"+(page.getNumber()+1));
System.out.println("当前页记录数:"+page.getNumberOfElements());
System.out.println("每页记录数:"+page.getSize());
}

5.4多数据源.

所谓​​多数据源​​​,就是一个​​Java EE​​​项目中采用了​​不同数据库实例​​​中的​​多个库​​​,或者同一个数据库实例中多个不同的库。一般来说,采用​​MyCat​​​等​​分布式数据库中间件​​​是比较好的解决方案,这样可以把​​数据库读写分离、分库分表、备份​​​等操作交给中间件去做, Java代码只需要专注于业务即可。不过,这并不意味着无法使用Java代码解决类似的问题,在​​Spring Framework​​​中就可以配置多数据源, ​​Spring Boot​​继承其衣钵,只不过配置方式有所变化。

5.4.1 JdbcTemplate多数据源

​JdbcTemplate​​​多数据源的配置是比较简单的,因为一个​​JdbcTemplate​​​对应一个​​DataSource​​​,开发者只需要手动提供多个​​DataSource​​​,再手动配置​​JdbcTemplate​​即可。具体步骤如下。

# 数据源1
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.username=root
spring.datasource.one.password=123
spring.datasource.one.url=jdbc:mysql:///chapter05-1
# 数据源2
spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.username=root
spring.datasource.two.password=123
spring.datasource.two.url=jdbc:mysql:///chapter05-2
  • ​DataSourceConfig​​​中提供了两个数据源: ​​dsOne​​​和​​dsTwo​​,默认方法名即实例名。
  • ​@ConfigurationProperties​​​注解表示使用不同前缀的配置文件来创建不同的​​DataSource​​实例。
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.one")
DataSource dsOne() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.two")
DataSource dsTwo() {
return DruidDataSourceBuilder.create().build();
}
}
@Configuration
public class JdbcTemplateConfig {
@Bean
JdbcTemplate jdbcTemplateOne(@Qualifier("dsOne") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
JdbcTemplate jdbcTemplateTwo(@Qualifier("dsTwo") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
  • ​JdbcTemplateConfig​​​中提供两个​​JdbcTemplate​​​实例。每个​​JdbcTemplate​​​实例都需要提供-​​DataSource​​​,由于​​Spring​​​容器中有两个​​DataSource​​​实例,因此需要通过方法名查找。​​@Qualifier​​​注解表示查找不同名称的​​DataSource​​实例注入进来
@Resource(name = "jdbcTemplateOne")
// @Autowired
JdbcTemplate jdbcTemplate;
@Autowired
@Qualifier("jdbcTemplateTwo")
JdbcTemplate jdbcTemplateTwo;
@GetMapping("/test1")
5.4.2 MyBatis多数据源
# 数据源1
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.one.username=root
spring.datasource.one.password=123
spring.datasource.one.url=jdbc:mysql:///chapter05-1
# 数据源2
spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.two.username=root
spring.datasource.two.password=123
spring.datasource.two.url=jdbc:mysql:///chapter05-2
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.one")
DataSource dsOne() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.two")
DataSource dsTwo() {
return DruidDataSourceBuilder.create().build();
}
}
@Configuration
@MapperScan(basePackages = "org.javaboy.mybatismulti.mapper1",sqlSessionFactoryRef = "sqlSessionFactory1",sqlSessionTemplateRef = "sqlSessionTemplate1")
public class MyBatisConfigOne {
@Autowired
@Qualifier("dsOne")
DataSource ds;

@Bean
SqlSessionFactory sqlSessionFactory1() {
SqlSessionFactory sqlSessionFactory = null;
try {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(ds);
sqlSessionFactory = bean.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return sqlSessionFactory;
}

@Bean
SqlSessionTemplate sqlSessionTemplate1() {
return new SqlSessionTemplate(sqlSessionFactory1());
}
}
@Configuration
@MapperScan(basePackages = "org.javaboy.mybatismulti.mapper2",sqlSessionFactoryRef = "sqlSessionFactory2",sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MyBatisConfigTwo {
@Autowired
@Qualifier("dsTwo")
DataSource ds;

@Bean
SqlSessionFactory sqlSessionFactory2() {
SqlSessionFactory sqlSessionFactory = null;
try {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(ds);
sqlSessionFactory = bean.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return sqlSessionFactory;
}

@Bean
SqlSessionTemplate sqlSessionTemplate2() {
return new SqlSessionTemplate(sqlSessionFactory2());
}
}
5.4.3 JPA多数据源
spring.datasource.one.password=123
spring.datasource.one.username=root
spring.datasource.one.url=jdbc:mysql:///chapter05-1
spring.datasource.one.type=com.alibaba.druid.pool.DruidDataSource

spring.datasource.two.password=123
spring.datasource.two.username=root
spring.datasource.two.url=jdbc:mysql:///chapter05-2
spring.datasource.two.type=com.alibaba.druid.pool.DruidDataSource

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57InnoDBDialect
spring.jpa.properties.database=mysql
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.show-sql= true

这里的配置与配置单独的​​JPA​​​有区别,因为在后文的配置中要从​​JpaProperties​​​中的​​getProperties​​​方法中获取所有JPA相关的配置, 因此这里的属性前缀都是​​spring.jpa.properties​

@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.one")
@Primary
DataSource dsOne() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.two")
DataSource dsTwo() {
return DruidDataSourceBuilder.create().build();
}
}
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "org.sang.dao1",
entityManagerFactoryRef = "entityManagerFactoryBeanOne",
transactionManagerRef = "platformTransactionManagerOne")
public class JpaConfigOne {
@Resource(name = "dsOne")
DataSource dsOne;
@Autowired
JpaProperties jpaProperties;
@Bean
@Primary
LocalContainerEntityManagerFactoryBean entityManagerFactoryBeanOne(
EntityManagerFactoryBuilder builder) {
return builder.dataSource(dsOne)
.properties(jpaProperties.getProperties())
.packages("org.sang.model")
.persistenceUnit("pu1")
.build();
}
@Bean
PlatformTransactionManager platformTransactionManagerOne(
EntityManagerFactoryBuilder builder) {
LocalContainerEntityManagerFactoryBean factoryOne = entityManagerFactoryBeanOne(builder);
return new JpaTransactionManager(factoryOne.getObject());
}
}
  • 使用​​@EnableJpaRepositories​​​注解来进行​​JPA​​​的配置,该注解中主要配置三个属性:​​basePackages​​​, ​​entityManagerFactoryRef​​​以及​​transactionManagerRef.​​​其中, ​​basePackages​​​用来指定​​Repository​​​所在的位置, ​​entityManagerFactoryRef​​​用来指定实体类管理工厂​​Bean​​​的名称,​​transactionManagerRef​​​则用来指定​​事务管理器​​​的引用名称,这里的引用名称就是​​JpaConfigOne​​​类中注册的​​Bean​​​的名称(默认的​​Bean​​名称为方法名)
  • 创建​​LocalContainerEntityManagerFactoryBean​​​,该Bean将用来提供​​EntityManager实例​​​,在该类的创建过程中,首先配置数据源,然后设置​​JPA​​​相关配置(​​JpaProperties​​​由系统自动加载),再设置实体类所在的位置,最后配置持久化单元名,若项目中只有一个​​EntityManagerFactory​​​, 则​​persistenceUnit​​可以省略掉,若有多个,则必须明确指定持久化单元名。
  • 由于项目中会提供两个​​LocalContainerEntityManagerFactoryBean​​​实例,第12行的注解​​@Primary​​​表示当存在多个​​LocalContainerEntityManagerFactoryBean​​​实例时,该实例将被优先使用。
    +​​​PlatformTransactionManager​​​表示创建一个事务管理器。 ​​JpaTransactionManager​​​提供对单个​​EntityManagerFactory​​​的事务支持,专门用于解决​​JPA​​中的事务管理。
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "org.sang.dao2",
entityManagerFactoryRef = "entityManagerFactoryBeanTwo",
transactionManagerRef = "platformTransactionManagerTwo")
public class JpaConfigTwo {
@Resource(name = "dsTwo")
DataSource dsTwo;
@Autowired
JpaProperties jpaProperties;
@Bean
LocalContainerEntityManagerFactoryBean entityManagerFactoryBeanTwo(
EntityManagerFactoryBuilder builder) {
return builder.dataSource(dsTwo)
.properties(jpaProperties.getProperties())
.packages("org.sang.model")
.persistenceUnit("pu2")
.build();
}
@Bean
PlatformTransactionManager platformTransactionManagerTwo(
EntityManagerFactoryBuilder builder) {
LocalContainerEntityManagerFactoryBean factoryTwo = entityManagerFactoryBeanTwo(builder);
return new JpaTransactionManager(factoryTwo.getObject());
}
}

第6章Spring Boot整合NosQL

​NoSQL​​​是指​​非关系型数据库​​​,非关系型数据库和关系型数据库两者存在许多显著的不同点,其中最重要的是​​NoSQL​​​不使用​​SQL​​​作为查询语言。其数据存储可以不需要固定的​​表格模式​​​,一般.都有​​水平可扩展性的特征​​。NoSQL主要有如下几种不同的分类:

  • ​Key/Value​​​键值存储。这种数据存储通常都是无数据结构的,一般被当作​​字符串​​​或者​​二进制​​​数据,但是数据​​加载速度快​​​,典型的使用场景是处理​​高并发​​​或者用于日志系统等,这一类的数据库有​​Redis​​​. ​​Tokyo Cabinet​​等.
  • ​列存储​​​数据库。列存储数据库功能相对​​局限​​​,但是查找​​速度快​​​,容易进行​​分布式扩展​​​,一般用于​​分布式文件系统​​​中,这一类的数据库有​​HBase​​​, ​​Cassandra​​等。
  • 文档型数据库 。和​​Key/Value​​​键值存储类似,文档型数据库也没有严格的​​数据格式​​​,这既是缺点也是优势,因为不需要预先创建表结构,数据格式更加灵活,一般可用在​​Web​​​应用中,这一类数据库有​​MongoDB​​​, ​​CouchDB​​等。
  • 图形数据库 。图形数据库专注于​​构建关系图谱​​​,例如​​社交网络​​​,​​推荐系统​​​等,这一类的数据库有​​Neo4J​​​、​​DEX​​等。

6.1整合Redis

​Redis​​​是一个使用​​C​​​编写的基于​​内存​​​的​​NoSQL​​​数据库,它是目前最流行的键值对存储数据库。​​Redis​​​由一个​​Key, Value​​​映射的​​字典​​​构成,与其他NoSQL不同, Redis中​​Value​​​的类型不局限于​​字符串​​​,还支持​​列表​​​、​​集合​​​、​​有序集合​​​、​​散列​​等。

6.1.1 Redis简介

​Redis​​​不仅可以当作缓存使用,也可以配置​​数据持久化​​​后当作​​NoSQL​​​数据库使用, 目前支持两种持久化方式:​​快照持久化​​​和​​AOF持久化​​​。另一方面, Redis也可以搭建​​集群​​​或者​​主从复制​​​结构,在​​高并发​​​环境下具有​​高可用性​​。

6.1.2 Redis安装
Loaded plugins: fastestmirror, product-id, search-disabled-repos, subscription-manager

This system is not registered with an entitlement server. You can use subscription-manager to register.

Repository epel is listed more than once in the configuration
Repository epel-debuginfo is listed more than once in the configuration
Repository epel-source is listed more than once in the configuration
Loading mirror speeds from cached hostfile
* base: mirrors.cloud.aliyuncs.com
* extras: mirrors.cloud.aliyuncs.com
* updates: mirrors.cloud.aliyuncs.com
Package redis-3.2.12-2.el7.x86_64 already installed and latest version
Nothing to do
[root@liruilong ~]#
[root@liruilong ~]# redis-server -v
Redis server v=3.2.12 sha=00000000:0 malloc=jemalloc-3.6.0 bits=64 build=7897e7d0e13773f
[root@liruilong ~]# redis-cli -v
redis-cli 3.2.12
6.1.3 Redis整合Spring Boot

​Redis​​​的​​Java​​​客户端有很多,例如​​Jedis​​​、​​JRedis​​​、 ​​Spring Data Redis​​​等, ​​Spring Boot​​​借助于​​Spring. Data Redis​​​为Redis提供了开箱即用​​自动化配置​​,开发者只需要添加相关依赖并配置Redis连接信息即可,具体整合步骤如下。

添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

默认情况下,​​spring-boot-starter-data-redis​​​使用的Redis工具是​​Lettuce​​​,考虑到有的开发者习惯使用​​Jedis​​​,因此可以从​​spring-boot-starter-data-redis​​​中排除​​Lettuce​​​并引入​​Jedis​​,修改为如下依赖:

配置Redis接下来在application.properties 中配置Redis连接信息
#基本连接信息配置

#表示使用的Redis库的编号, Redis中提供了16个database,编号为0-15
spring.redis.database=0
spring.redis.host=192.168.66.130
spring.redis.port=6379
spring.redis.password=123@456
#连接池信息配置.
spring.redis.lettuce.pool.max-active=
spring.redis.lettuce.pool.max-idle=
spring.redis.lettuce.pool.max-wait=
spring.redis.lettuce.pool.min-idle=
spring.redis.lettuce.shutdown-timeout=
#连接池最大连接数
spring.redis.jedis.pool.max-active=8
#连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
#连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1ms
#连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0

在​​·Spring Boot·​​​的自动配置类中提供了​​·RedisAutoConfiguration·​​进行Redis的配置,部分源码

《Spring Boot+Vue全栈开发实战》读书笔记_新星计划_38


由这一段源码可以看到, ​​application.properties​​​中配置的信息将被注入​​RedisProperties​​​中,如果开发者自己没有提供RedisTemplate或者​​StringRedis Template​​​实例,则​​Spring Boot​​​默认会提供这两个实例, ​​RedisTemplate​​​和​​StringRedisTemplate​​​实例则提供了​​Redis​​的基本操作方法。

《Spring Boot+Vue全栈开发实战》读书笔记_spring boot_39

@RestController
public class BookController {
@Autowired
RedisTemplate redisTemplate;
@Autowired
StringRedisTemplate stringRedisTemplate;
@GetMapping("/test1")
public void test1() {
ValueOperations<String, String> ops1 = stringRedisTemplate.opsForValue();
ops1.set("name", "三国演义");
String name = ops1.get("name");
System.out.println(name);
ValueOperations ops2 = redisTemplate.opsForValue();
Book b1 = new Book();
b1.setId(1);
b1.setName("红楼梦");
b1.setAuthor("曹雪芹");
ops2.set("b1", b1);
Book book = (Book) ops2.get("b1");
System.out.println(book);
}
}
  • ​StringRedisTemplate​​​是​​RedisTemplate​​​的子类,​​StringRedisTemplate​​​中的​​key​​​和​​value​​​都是​​字符串​​​,采用的序列化方案是​​StringRedisSerializer​​​,而​​RedisTemplate​​​则可以用来操作对象,​​RedisTemplate​​​采用的序列化方案是​​JdkSerializationRedisSerializer.​​​无论是​​StringRedis Template​​​还是​​RedisTemplate,​​​操作​​Redis​​的方法都是一致的。
  • ​StringRedisTemplate​​​和​​RedisTemplate​​​都是通过​​opsForValue​​​, ​​opsForZSet​​​或者​​opsForSet​​​等方法首先​​获取​​​一个​​操作对象​​​,再使用该​​操作对象​​完成数据的读写。
  • 第10行向Redis中存储一条记录,第11行将之读取出来,第18行向Redis中存储一个对象,第19行将之读取出来。
6.1.4 Redis集群整合Spring Boot.
1,搭建Redis集群

​·(1)集群原理·​​​在​​Redis集群​​​中,所有的​​Redis节点​​​彼此​​互联​​​,节点内部使用​​二进制协议​​​优化传输速度和带宽。当一个节点挂掉后,集群中​​超过半数​​​的节点​​检测失效​​​时才认为该节点​​已失效​​​。不同于​​Tomcat集群​​​需要使用​​反向代理服务器​​​, ​​Redis集群​​​中的​​任意节点​​​都可以直接和​​Java客户端连接​​。

​Redis集群​​​上的数据分配则是​​采用哈希槽(HASH SLOT)​​​, ​​Redis集群​​​中内置了​​16384个哈希槽​​​,当有数据需要​​存储​​​时, Redis会首先使用​​CRC16​​​算法对​​key​​​进行计算,将​​计算获得的结果​​​对​​16384​​​取余,这样每一个​​key​​​都会对应一个取值在0-16383之间的​​哈希槽​​​, ​​Redis​​​则根据这个​​余数​​​将该条​​数据存储​​​到对应的​​Redis节点​​​上,开发者可根据每个​​Redis实例的性能​​​来调整每个​​Redis实例上哈希槽​​​的​​分布范伟​

6.2 整合MongoDB.

6.2.1 MongoDB简介

​·MongoDB​​​·是一种面向文档的数据库管理系统,它是一个介于关系型数据库和非关系型数据库,之间的产品, ​​·MongoDB·​​​功能丰富,它支持一种类似​​JSON​​​的​​BSON​​​数据格式,既可以存储简单的数据格式,也可以存储复杂的数据类型。·​​MongoDB​​​·最大的特点是它支持的查询语言非常强大,并且还支持对数据建立索引。总体来说, ​​·MongoDB·​​​是一款应用相当广泛的​​NosQL​​数据库。

6.2.2 MongoDB安装
6.2.3 MongoDB整合Spring Boot.

借助于​​Spring Data MongoD​​​B, ​​Spring Boot​​​为​​MongoDB​​也提供了开箱即用的自动化配置方案,具体配置步骤如下

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring.data.mongodb.authentication-database=admin
spring.data.mongodb.database=test
spring.data.mongodb.host=192.168.248.144
spring.data.mongodb.port=27017
spring.data.mongodb.username=root
spring.data.mongodb.password=123
#spring.data.mongodb.uri=mongodb://root:[email protected]:27017/admin
#spring.data.mongodb.uri=mongodb://192.168.248.144:27017/test
public interface BookDao extends MongoRepository<Book,Integer> {
List<Book> findByAuthorContains(String author);
Book findByNameEquals(String name);
}

使用​​MongoTemplate​​​除了继承​​MongoRepository​​​外,​​Spring Data MongoDB​​​还提供了​​MongoTemplate​​​用来方便地操作​​MongoDB​​​。在​​Spring Boot​​​中,若添加了​​MongoDB​​​相关的依赖,而开发者并没有提供​​MongoTemplate​​​,则默认会有一个​​MongoTemplate​​​注册到​​Spring容器​​​中,相关配置源码在​​MongoDataAutoConfiguration​​​类中。因此,用户可以直接使用​​MongoTemplate​​​,在​​Controller​​​中直接注入​​MongoTemplate​​就可以使用了,添加如下代码到第5步的Controller中:

@RestController
public class BookController {
@Autowired
BookDao bookDao;
@Autowired
MongoTemplate mongoTemplate;

@GetMapping("/test2")
public void test2() {
List<Book> books = new ArrayList<>();
....
mongoTemplate.insertAll(books);
List<Book> list = mongoTemplate.findAll(Book.class);
System.out.println(list);
Book book = mongoTemplate.findById(3, Book.class);
System.out.println(book);
}

@GetMapping("/test1")
public void test1() {
List<Book> books = new ArrayList<>();
....
bookDao.insert(books);
List<Book> books1 = bookDao.findByAuthorContains("鲁迅");
System.out.println(books1);
Book book = bookDao.findByNameEquals("朝花夕拾");
System.out.println(book);
}
}

6.3 Session共享

正常情况下, ​​HttpSession​​​是通过​​Servlet容器​​​创建并进行管理的,创建成功之后都是保存在​​内存中​​​。如果开发者需要对项目进行​​横向扩展搭建集群​​​,那么可以利用一些​​硬件​​​或者​​软件工具​​​来做​​负载均衡​​​,此时,来自同一用户的​​HTTP请求​​​就有可能被​​分发到不同的实例​​​上去,如何保证​​各个实例之间Session的同步​​就成为一个必须解决的问题。

​Spring Boot​​​提供了自动化的​​Session共享配置​​​,它结合​​Redis​​​可以非常方便地解决这个问题。使用​​Redis​​​解决​​Session共享问题​​​的原理非常简单,就是把原本​​存储在不同服务器上​​​的​​Session​​​拿出来放在​​一个独立的服务器​​上.

《Spring Boot+Vue全栈开发实战》读书笔记_java_40


当一个请求到达​​Nginx​​​服务器后,首先进行​​请求分发​​​,假设请求被​​real serverl​​​处理了, ​​real server​​​在处理请求时,无论是存储​​Session​​​还是读取​​Session​​​,都去操作​​Session服务器​​​而不是操作自身​​内存中的Session​​​,其他​​real server​​​在处理请求时也是如此,这样就可以实现​​Session共享​​了

6.3.1 Session共享配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

除了​​Redis依赖​​​之外,这里还要提供​​spring-session-data-redis​​​依赖, ​​Spring Session​​​可以做到​​透·明化​​​地​​替换​​​掉应用的​​Session容器​​​。项目创建成功后,在​​application.properties​​​中进行​​Redis​​基本连接信息配置,代码如下:

spring.redis.database=0
spring.redis.host=192.168.66.130
spring.redis.port=6379
spring.redis.password=123@456
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.min-idle=0

添加··​​@EnableRedisHttpSession​

/**
* session托管到redis
*/
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds= 3600*24, redisFlushMode = RedisFlushMode.ON_SAVE, redisNamespace = "aurora-web")
public class RedisSessionConfig {

}
  • maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,原 Spring Boot 的server.session.timeout 属性不再生效。
  • 经过上面的配置后,Session调用就会自动去Redis存取。另外,想要达到Session共享的目的,只需要在其他的系统上做同样的配置即可。
@EnableRedisHttpSession源码
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({RedisHttpSessionConfiguration.class})
@Configuration
public @interface EnableRedisHttpSession {
//Session默认过期时间,单位秒,默认1800秒
int maxInactiveIntervalInSeconds() default 1800;

//配置key的namespace,默认的是spring:session,如果不同的应用共用一个redis,应该为应用配置不同的namespace,这样才能区分这个Session是来自哪个应用的
String redisNamespace() default "spring:session";

//配置刷新Redis中Session方式,默认是ON_SAVE模式,只有当Response提交后才会将Session提交到Redis,也可以配置成IMMEDIATE模式,即所有对Session的更改会立即更新到Redis
RedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;

//清理过期Session的定时任务
String cleanupCron() default "0 * * * * *";
}

测试一下

@RestController
public class HelloController {
@Value("${server.port}")
String port;
@PostMapping("/save")
public String saveName(String name, HttpSession session) {
session.setAttribute("name", name);
return port;
}
@GetMapping("/get")
public String getName(HttpSession session) {
return port + ":"
+ session.getAttribute("name").toString();
}
}
6.3.2 Nginx负载均衡

​nginx.conf·​​配置文件修改:

《Spring Boot+Vue全栈开发实战》读书笔记_java_41

upstream backend {
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
}
server {
location / {
proxy_pass http: //backend;
}
}

第7章构建RESTful服务

7.1 REST简介

​REST (Representational State Transfer)​​​是一种​​Web​​​软件架构风格,它是一种​​风格​​​,而不是​​标准​​​,匹配或兼容这种架构风格的网络服务称为​​REST服务​​​。REST服务简洁并且有层次, REST通常基于​​HTTP​​​,​​URI​​​和​​XML​​​以及​​HTML​​这些现有的广泛流行的协议和标准。

​在REST​​​中,资源是由​​URI​​​来指定的,对资源的增删改查操作可以通过​​HTTP​​​协议提供的​​GET, POST, PUT, DELETE​​​等方法实现。使用REST可以更高效地利用缓存来提高响应速度,同时​​REST​​​中的通信会话状态由​​客户端​​​来维护,这可以让不同的服务器处理一系列请求中的不同请求,进而提高服务器的扩展性。在前后端分离项目中,一个设计良好的Web软件架构必然要满足REST风格。在​​Spring MVC​​​框架中,开发者可以通过​​@RestController​​​注解开发一个RESTful服务,不过,​​Spring Boot​​​对此提供了自动化配置方案,开发者只需要添加相关依赖就能快速构建一个​​RESTful​​服务。

7.2 JPA实现REST

在​​Spring Boot​​​中,使用S​​pring Data JPA​​​和​​Spring Data Rest​​​可以快速开发出一个​​RESTful应用​​​。接下来向读者介绍​​Spring Boot​​​中非常方便的​​RESTful应用开发​​。

7.2.1 基本实现

这里的依赖除了​​数据库相关的依赖外​​​,还有​​Spring Data JPA​​​的依赖以及​​Spring Data Rest​​​的依赖。项目创建完成后,在​​application.properties​​ 中配置基本的数据库连接信息:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

这里的依赖除了数据库相关的依赖外,还有​​Spring Data JPA​​​的依赖以及​​Spring Data Rest​​​的依赖。项目创建完成后,在​​application.properties​​中配置基本的数据库连接信息:

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=123
spring.datasource.url=jdbc:mysql:///jparestful
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=mysql
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
spring.jpa.show-sql=true

##每页默认记录数,缺省值为20
#spring.data.rest.default-page-size=2
##分页查询页码参数名,缺省值为page
#spring.data.rest.page-param-name=page
##分页查询记录数参数名,缺省值为size
#spring.data.rest.limit-param-name=size
##分页查询排序参数名,缺省值为sort
#spring.data.rest.sort-param-name=sort
##base-path表示给所有请求路径都加上前缀
#spring.data.rest.base-path=/api
##添加成功时是否返回添加内容
#spring.data.rest.return-body-on-create=true
##更新成功时是否返回更新内容
#spring.data.rest.return-body-on-update=true

创建实体类,创建​​BookRepository​​​类继承​​JpaRepository​​​, ​​JpaRepository​​中默认提供了一些基本的操作方法,代码如下:

public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();

List<T> findAll(Sort var1);

List<T> findAllById(Iterable<ID> var1);

<S extends T> List<S> saveAll(Iterable<S> var1);

void flush();

<S extends T> S saveAndFlush(S var1);

<S extends T> List<S> saveAllAndFlush(Iterable<S> var1);

/** @deprecated */
@Deprecated
default void deleteInBatch(Iterable<T> entities) {
this.deleteAllInBatch(entities);
}

void deleteAllInBatch(Iterable<T> var1);

void deleteAllByIdInBatch(Iterable<ID> var1);

void deleteAllInBatch();

/** @deprecated */
@Deprecated
T getOne(ID var1);

T getById(ID var1);

<S extends T> List<S> findAll(Example<S> var1);

<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

7.2.2 自定义请求路径

默认情况下,请求路径都是​​实体类名​​​小写加​​s​​​,如果开发者想对​​请求路径进行重定义​​​,通过​​@RepositoryRestResource​​​注解即可实现,下面的案例只需在​​BookRepository​​​上添加​​@RepositoryRestResource​​注解即可:

@CrossOrigin
@RepositoryRestResource(path = "bs",collectionResourceRel = "bs",itemResourceRel = "b")
public interface BookRepository extends JpaRepository<Book, Integer> {
.....
}
  • ​@RepositoryRestResource​​​·注解的​​·path·​​​属性表示将所有请求路径中的​​books​​​都修改为​​bs,​​​如​​http://ocalhost: 8080/bs:​
  • ​collectionResourceRel​​​属性表示将返回的​​JSON​​​集合中​​book​​​集合的​​key​​​修改为​​bs​​;
  • ​itemResourceRel​​​表示将返回的​​JSON​​集合中的单个book的key修改为b,

7.2.3 自定义查询方法.

默认的查询方法支持​​分页查询​​​、​​排序查询​​​以及按照​​id查询​​​,如果开发者想要按照某个​​属性查询​​​,只需在​​BookRepository​​​中定义相关​​方法并暴露​​出去即可,代码如下:

@RepositoryRestResource(path = "bs",collectionResourceRel = "bs",itemResourceRel = "b")
public interface BookRepository extends JpaRepository<Book, Integer> {
@Override
@RestResource(exported = false)
void deleteById(Integer integer);

@RestResource(path = "author",rel = "author")
List<Book> findByAuthorContains(@Param("author") String author);
@RestResource(path = "name",rel = "name")
Book findByNameEquals(@Param("name") String name);
}
  • ·​​自定义查询​​​只需要在​​BookRepository​​​中定义相关查询方法即可,方法定义好之后可以不添加​​@RestResource​​​注解,默认路径就是​​方法名​​​。以第4行定义的方法为例,若不添加​​@RestResource​​​注解, 则默 认 该方法的调用路径为​​http://ocalhost:8080/bs/search/indByAuthorContains?author=鲁迅​​​。如果想对查询​​路径进行自定义​​​,只需要添加​​@RestResource注解​​​即可, ​​path属性​​​即表示最新的路径。还是以第4行的方法为例,添加​​@RestResource(path = "author",rel = "author")​​注解后的查询路径为"http://ocalhost:8080/bs/search/author?author=鲁迅".
  • 用户可以直接访问​​http/ocalhost:8080/bs/search​​​路径查看该​​实体类暴露​​​出来了哪些​​查询方法​​​,默认情况下,在​​查询方法展示​​​时使用的路径是方法名,通过​​@RestResource​​​注解中的​​rel​​​属性可以对这里的​​路径进行重定义​​,如图7-6所示。

7.2.4 隐藏方法

默认情况下,凡是继承了​​Repository接口​​​(或者​​Repository的子类​​​)的类都会被暴露出来,即开发者可执行​​基本的增删改查​​​方法。以上文的​​BookRepository​​​为例,如果开发者提供了​​BookRepository​​​继承自​​Repository​​​,就能执行对Book的基本操作,如果开发者继承了​​Repository​​但是又不想暴露相关操作,做如下配置即可:

//@RepositoryRestResource(exported = false)
public interface BookRepository extends JpaRepository<Book, Integer> {
@Override
@RestResource(exported = false)
void deleteById(Integer integer);
....
}

将·​​@RepositoryRestResource​​​注解中的​​exported​​​属性置为​​false​​​之后,则​​增删改查接口​​​都会​​失效​​​, ​​BookRepository​​​类中定义的相关方法也会失效。若只是单纯地​​不想暴露某个方法​​​,则在方法上进行配置即可,例如开发者想​​屏蔽DELETE接口​​.

7.2.5 配置CORS

在​​4.6节​​​已经向读者介绍了​​CORS​​​两种不同的配置方式,一种是直接在方法上添加​​@CrosSorigin注解​​​,另一种是​​全局配置​​​。​​全局配置​​​在这里依然适用,但是默认的​​RESTful工程​​​不需要开发者自己​​提供Controller​​​,因此添加在​​Controller​​​的方法上的​​注解​​​可以直接写在​​BookRepository​​​上,代码如下:接口跨域:​​@CrossOrigin​​注解添加到某一个方法上即可。

//@CrossOrigin
@RepositoryRestResource(path = "bs",collectionResourceRel = "bs",itemResourceRel = "b")
public interface BookRepository extends JpaRepository<Book, Integer> {
@CrossOrigin
List<Book> findByAuthorContains(@Param("author") String author);
....
}

7.2.6其他配置

​application.properties​​配置
##每页默认记录数,缺省值为20
#spring.data.rest.default-page-size=2
##分页查询页码参数名,缺省值为page
#spring.data.rest.page-param-name=page
##分页查询记录数参数名,缺省值为size
#spring.data.rest.limit-param-name=size
##分页查询排序参数名,缺省值为sort
#spring.data.rest.sort-param-name=sort
##base-path表示给所有请求路径都加上前缀
#spring.data.rest.base-path=/api
##添加成功时是否返回添加内容
#spring.data.rest.return-body-on-create=true
##更新成功时是否返回更新内容
#spring.data.rest.return-body-on-update=true

当然,这些​​XML配置​​​也可以在​​Java代码​​​中配置,且代码中配置的优先级高于​​application.properties​​配置的优先级,代码如下

@Configuration
public class RestConfig extends RepositoryRestConfigurerAdapter {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setDefaultPageSize(2)
.setPageParamName("page")
.setLimitParamName("size")
.setSortParamName("sort")
.setBasePath("/api")
.setReturnBodyOnCreate(true)
.setReturnBodyOnUpdate(true);
}
}

7.2.7 JPA使用rest自定义链接

@Configuration
public class MyResourceProcessor implements ResourceProcessor {
@Override
public ResourceSupport process(ResourceSupport resourceSupport) {
resourceSupport.add(new Link("http://www.baidu.com", "百度一下"));
return resourceSupport;
}
}

7.3 MongoDB实现REST

​MongoDB​​​整合​​Spring Boot​​​,而使用​​Spring Boot​​​快速构建​​RESTful·服务​​​除了结合​​Spring Data JPA​​​之外,也可以结合​​Spring Data MongoDB​​​实现。使用​​Spring DataMongoDB​​​构建​​RESTful​​服务也是三个步骤,分别如下。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

这里​​Spring Data Rest​​​的依赖和7.2节中的一致,只是将​​Spring Data JPA​​​的依赖变为​​Spring DataMongoDB​​​的依赖。项目创建成功后,在​​application.properties​​​中配置​​MongoDB​​的基本连接信息,

spring.data.mongodb.authentication-database=test
spring.data.mongodb.database=test
spring.data.mongodb.username=sang
spring.data.mongodb.password=123
spring.data.mongodb.host=192.168.248.144
spring.data.mongodb.port=27017
public interface BookRepository extends MongoRepository<Book,Integer> {
}

第8章开发者工具与单元测试

8.1 devtools简介

​Spring Boot​​​中提供了一组开发工具​​spring-boot-devtools​​​,可以提高开发者的工作效率,开发者可以将该模块包含在任何项目中, ​​spring-boot-devtools​​​最方便的地方莫过于​​热部署​​了。

8.2 devtools实战

8.2.1 基本用法

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
  • 这里多了一个optional选项,是为了防止将​​devtools依赖传递​​​到其他模块中。当开发者将应用打包运行后, ​​devtools​​会被自动禁用。
  • 当开发者将​​spring-boot-devtools​​​引入项目后,只要​​classpath​​​路径下的​​文件发生了变化​​,项目就会自动重启,这极大地提高了项目的开发速度。

8.2.2 基本原理

​Spring Boot中​​​使用的自动重启技术涉及两个类加载器,一个是​​baseclassloader,​​​用来​​加载不会变化的类​​​,例如项目引用的第三方的jar;另一个是​​restartclassloader​​​,用来加载​​开发者自己写的会·变化的类​​​。当项目需要重启时, restartclassloader将被一个新创建的类加载器代替,而baseclassloader则继续使用原来的,这种启动方式要比​​冷启动​​​快很多,因为​​baseclassloader​​已经存在并且已经加载好

8.3单元测试

8.3.1 基本用法

当开发者使用Intelli IDEA或者在线创建一个Spring Boot项目时,创建成功后,默认都添加了spring-bool-starter-est依赖,并且创建好了测试类。

@RunWith(SpringRunner.class)
@SpringBootTest
public class AppTest {
MockMvc mockMvc;
@Autowired
WebApplicationContext wac;
@Autowired
HelloService helloService;
@Before
public void before() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void test2() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/hello", "name=aaa")).andDo(MockMvcResultHandlers.print());
}
@Test
public void test1() {
System.out.println(helloService.sayHello("里斯"));
}
}
  • ​@RunWith​​​注解,该注解将​​JUnit​​​执行类修改为​​SpringRunner​​​,而​​SpringRunner​​​是​​Spring Framework​​​中测试类​​SpringJUnit4ClassRunner​​的别名。
  • ​@Spring BootTest​​​注解除了提供​​Spring TestContext​​​中的常规测试功能之外,还提供了其他特性:提供默认的​​ContextLoader​​​, 自动搜索​​@Spring BootConfiguration​​​、自定义环境属性、为不同的​​webEnvironment​​​模式提供支持,这里的​​webEnvironment​​模式主要有4种.這裏不説了。

8.3.2 Service测试

@Service
public class HelloService {
public String sayHello(String name) {
return "Hello " + name + " !";
}
}
@Autowired
HelloService helloService;
@Test
public void contextLoads() {
String hello = helloService.sayHello("Michael");
Assert.assertThat(hello, Matchers.is("Hello Michael !"));
}

8.3.3 Controller测试.

@Test
public void test2() throws Exception {
ObjectMapper om = new ObjectMapper();
Book book = new Book();
book.setAuthor("罗贯中");
book.setName("三国演义");
book.setId(1);
String s = om.writeValueAsString(book);
MvcResult mvcResult = mockMvc
.perform(MockMvcRequestBuilders
.post("/book")
.contentType(MediaType.APPLICATION_JSON)
.content(s))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
}
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(String name) {
return helloService.sayHello(name);
}
}
MockMvc mockMvc;
@Autowired
WebApplicationContext wac;
@Autowired
HelloService helloService;
@Before
public void before() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void test1() throws Exception {
MvcResult mvcResult = mockMvc.perform(
MockMvcRequestBuilders
.get("/hello")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("name", "Michael"))
//返回什么数据
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
}

除了​​MockMvc​​​这种测试方式之外,​​Spring Boot​​​还专门提供了​​TestRestTemplate​​​用来实现集成测试,若开发者使用了​​@Spring BootTest​​​注解,则​​TestRestTemplate​​​将自动可用,直接在测试类中注入即可。注意,如果要使用​​TestRestTemplate​​​进行测试,需要将​​@Spring BootTest​​​注解中​​webEnvironment​​​属性的默认值由​​WebEnvironment.MOCK​​​修改为​​webEnvironment.DEFINED PORT​​​或者​​WebEnvironment.RANDOM PORT​​,因为这两种都是使用一个真实的Servlet环境而不是模拟的Serlet环境。其代码如下:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Autowired
TestRestTemplate restTemplate;
@Test
public void test3() {
ResponseEntity<String> hello = restTemplate.getForEntity("/hello?name={0}", String.class, "Michael");
System.out.println(hello.getBody());
}
  • test2方法演示了POST请求如何传递JSON数据,首先在32行将一个book对象转为一段JSON,然后在36行设置请求的contentType为APPLICATION-JSON,最后在37行设置content为上传的JSON即可。

8.3.4 JSON测试

开发者可以使用​​@JsonTest​​​测试​​JSON序列化​​​和​​反序列化​​​是否工作正常,该注解将​​自动配置Jackson ObjectMapper​​​.​​@JsonComponent​​​以及​​Jackson Modules​​.如果开发者使用Gson代替Jackson,该注解将配置Gson,具体用法如下:

@RunWith(SpringRunner.class)
@JsonTest
public class JSONTest {
@Autowired
JacksonTester<Book> jacksonTester;
@Test
public void testSerialize() throws IOException {
Book book = new Book();
book.setId(1);
book.setName("三国演义");
book.setAuthor("罗贯中");
Assertions.assertThat(jacksonTester.write(book))
.isEqualToJson("book.json");
Assertions.assertThat(jacksonTester.write(book))
.hasJsonPathStringValue("@.name");
Assertions.assertThat(jacksonTester.write(book))
.extractingJsonPathStringValue("@.name")
.isEqualTo("三国演义");
}
@Test
public void testDeserialize() throws Exception {
String content = "{\"id\":1,\"name\":\"三国演义\",\"author\":\"罗贯中\"}";
// Book book = new Book();
// book.setId(1);
// book.setName("三国演义");
// book.setAuthor("罗贯中");
Assertions.assertThat(jacksonTester.parseObject(content).getName())
.isEqualTo("三国演义");
}
}

第9章Spring Boot缓存

​Spring 3.1​​​中开始对​​缓存​​​提供支持,核心思路是对​​方法的缓存​​​,当开发者调用一个方法时,将​​方法的参数​​​和​​返回值​​​作为​​key/value​​​缓存起来,当再次调用该方法时,如果​​缓存中有数据​​​,就直接,从缓存中获取,否则再去执行该方法。但是, Spring中并未提供​​缓存​​​的实现,而是提供了一套​​缓存API​​​,开发者可以自由选择缓存的实现, 目前Spring Boot支持的缓存有如下几种:​​Cache (JSR-107)​​​、​​EhCache 2.x​​​、​​Hazelcast​​​、​​Infinispan​​​、​​Couchbase​​​、​​Redis​​​、​​Caffeine​​​、​​Simple​

9.1 Ehcache 2.x缓存.

​Ehcache​​​缓存在Java开发领域已是久负盛名,在​​Spring Boot​​​中,只需要一个配置文件就可以将​​Ehcache​​​集成到项目中。​​Ehcache 2.x​​的使用步骤如下。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>

2.添加缓存配置文件如果Ehcache的依赖存在,并且在classpath下有一个名为​​ehcache2.xml​​​的​​Ehcache配置文件​​​,那么​​EhCacheCacheManager​​​将会自动作为缓存的实现。因此,在​​resources​​目录下创建ehcache.xml文件作为Ehcache缓存的配置文件,代码如下:

<ehcache>
<diskStore path="java.io.tmpdir/cache"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<cache name="book_cache"
maxElementsInMemory="10000"
eternal="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="10"/>
</ehcache>

这是一个常规的​​Ehcache​​​配置文件,提供了两个缓存策略,一个是默认的,另一个名为​​book-cache​​.其中,

  • ​name​​​表示缓存名称; ​​maxElementsInMemor​​y表示缓存最大个数:
  • ​eternal​​​表示缓存对象是否永久有效,一旦设置了​​永久有效​​​, ​​timeout​​将不起作用;
  • ​timeToldleSeconds​​​表示缓存对象在失效前的允许闲置时间(单位:秒) ,当​​eternal-false​​对象不是永久有效时,该属性才生效;
  • ​timeToLiveSeconds​​​表示缓存对象在失效前允许存活的时间(单位:秒),当​​eternal-false​​对象不是永久有效时,该属性才生效;
  • ​overflowToDisk​​​表示当内存中的对象数量达到​​maxElementsInMemory​​时, Ehcache是否将对象写到磁盘中;
  • ​diskExpiryThreadIntervalSeconds​​表示磁盘失效线程运行时间间隔。

另外,如果开发者想自定义Ehcache配置文件的名称和位置,可以在​​application.properties​​中添加如下配置:

spring.cache.ehcache.config=classpath:ehcache2.xml
@SpringBootApplication
@EnableCaching
public class CacheApplication {

public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
@Service
@CacheConfig(cacheNames = "book_cache")
public class BookDao {
@Autowired
MyKeyGenerator myKeyGenerator;
@Cacheable(keyGenerator = "myKeyGenerator")
public Book getBookById(Integer id) {
System.out.println("getBookById");
Book book = new Book();
book.setId(id);
book.setName("三国演义");
book.setAuthor("罗贯中");
return book;
}
@CachePut(key = "#book.id")
public Book updateBookById(Book book) {
System.out.println("updateBookById");
book.setName("三国演义2");
return book;
}
@CacheEvict(key = "#id")
public void deleteBookById(Integer id) {
System.out.println("deleteBookById");
}
}

9.2 Redis单机缓存

9.3 Redis集群缓存

9.3.1 搭建Redis集群.

9.3.2 配置缓存

9.3.3 使用缓存

第10章 Spring Boot安全管理…

标签:Vue,读书笔记,spring,配置,Boot,class,Spring,public
From: https://blog.51cto.com/u_13474506/5931193

相关文章

  • 组件嵌套以及VueComponent的讲解(代码实现)
    1、效果图分析2、先创建一个组件//第一步、创建city组件constcity=Vue.extend({template:`<divclass="cityDemo">......
  • SpringBoot+Vue实现第三方Gitee登录(二)
    1.准备工作_OAuth2(官网地址:开发流程)1.1 API使用条款  1.OSCHINA用户是资源的拥有者,需尊重和保护用户的权益。  2.不能在应用中使用OSCHINA的名称。  3.......
  • Vue 路由传参加密
    首先,创建一个base64.jsconstBase64={//加密encode(str){returnbtoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,function......
  • 轻量前后端分离简单网页版聊天(Spring Boot+WebSocket+Vue)Demo实现
    WebSocket是啥?在HTTP协议中,所有的请求都是由客户端发起的,由服务端进行响应,服务端无法向客户端推送消息,但是在一些需要即时通信的应用中,又不可避免地需要服务端向客户端推......
  • 《Kubernetes权威指南:从Docker到Kubernetes实践全接触》读书笔记
    写在前面之前简单的了解过,但是机器的原因,只有单机,因为安装Docker的原因,本机VM上的红帽节点起不来了。懂得不多,视频上都是多节点的,所以教学视屏上的所以Demo没法搞。前些时间......
  • 《Java并发编程详解》读书笔记
    嗯,书是假期开始看的感觉,虽然部分知识以前学过,但是在学一次巩固一下。嗯,加油生活。摘一句子,^_^有一天,突然发现自己没有热爱的东西了。就这样进入浪费时间的年代。秋天一到,候......
  • 《从Paxos到Zookeeper分布式一致性原理与实践》读书笔记
    写在前面嗯,一直听人家说​​分布式​​​,奈何这个概念一直不清晰,而且问大佬,也总是听的一知半解的,一直听人家讲​​Zookeeper​​,很早就想系统的学习一下,奈何时间挤不出来,除......
  • 《ES6标准入门》读书笔记
         嗯,之前之做项目大概了解一些,之后看Vue实战里讲一些,简历里写了这个,所以决定系统学习,第一次接触大佬阮一峰是讲Flex布局的一篇博文,感觉很好,居然把书开源,嗯,......
  • Vue.js 学习笔记
    我想要的,时间自然会给我。年龄也不会是我的阻碍,反而会是我的骄傲。:我不用在某个年龄段必须做某事,不要让任何人打乱自己的节奏。---------------------摘加油生活:我要努力呀......
  • 《Python核心编程》第三版 读书笔记
    “一个不成熟男子的标志是为了某种事业英勇地死去,一个成熟男子的标志是为了某种事业卑微地活着。”                       ......