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

在这里插入图片描述

本书将带你全面了解Spring Boot基础与实践,带领读者一步步进入 Spring Boot 的世界。

    • 前言
    • 第一章 Spring Boot入门
    • 第二章 Spring Boot基础配置
    • 第三章 Spring Boot整合视图层
    • 第四章 Spring Boot整合Web开发
    • 第五章 持久层技术
    • 第六章 Spring Boot整合NoSQL
    • 第八章 开发者工具与单元测试

前言

本文为读书流水笔记 加深印象的同时为后面回忆和查漏补缺提供便利 各级别开发工程师也可以作快速浏览发现疏漏之用
Spring 是一个轻量级的容器但配置繁琐和各第三方框架整合时代码量都非常大并且大多是重复代码 Spring Boot 带来了 新的自动化配置解决方案,使用 Spring Boot 可以快速创建基于 Spring 产级的独立应用程序 Spring Boot 中对一些常用的第方库提供了默认的自动化配置方案
Spring Boot 项目可以采用传统的方案打成 war 包,然后部署到 Tomcat 中运行,也可以直接打成可执行 jar 包
本书基于 Spring Boot 2.0.4 需要 Java 8及以上
Spring Boot 主要优势
(1) 提供一个快速的 Spring 项目搭建渠道
(2) 开箱即用,很少的 Spring 配置就能运行一个 Java 项目
(3) 提供了生产级的服务监控方案
(4) 内嵌服务器, 可以快速部署
(5) 提供了一系列非功能性的通用配置
(6) 纯 Java 配置,没有代码生成, 也不需要 XML 配置

第一章 Spring Boot入门

  • 添加依赖

org.springframework.boot
spring-boot-starter-parent
2.0.4.RELEASE

 
 
org.springframework.boot 
spring-boot-starter-web 
 

  • 编写启动类
@EnableAutoConfiguration
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

@EnableAutoConfiguration 注解表示开启自动化配置 由于添加了web依赖所以在开启自动化配置后会自动进行Spring和Spring MVC的配置
main方法中通过run方法传入App.class告诉Spring哪个是主要组件

  • 创建一个Spring MVC中的控制器
    @RestController
public class HelloController {
	@GetMapping("/hello")
	public String hello() {
		return "hello spring boot";
	}
}

在控制器中提供了"/hello"接口 此时需要配置包扫描才能将HelloController注册到Spring MVC容器中 因此需要在App类上添加注解@ComponentScan

@EnableAutoConfiguration
@ComponentScan
public class App {...

也可以直接使用组合注解@SpringBootApplication

@SpringBootApplication
public class App {...
  • 启动项目
    (1) 使用Maven命令启动 mvn spring-boot:run
    (2) 直接运行main方法 IDE中直接运行
    (3) 打包启动 pom中添加插件spring-boot-maven-plugin
    mvn package命令进行打包 在项目target目录下会生成jar文件
    java -jar命令启动jar文件

第二章 Spring Boot基础配置

  • @SpringBootApplication是一个组合注解
    (1) @SpringBootConfiguration
    定义如下
    @Configuration
    public @interface SpringBootConfiguration {…
    实际就是个@Configuration注解 功能是表明这是个配置类 可以在类中配置Bean
    从这个角度讲这个类有点类似于Spring中applicationContext.xml文件的角色
    开发者可以新建一个类专门用来配置Bean便于配置的管理
    @Configuration
    public class MyConfig {…
    (2) @EnableAutoConfiguration
    表示开启自动化配置 这是非侵入式的 开发者可以使用自定义配置代替其中配置
    (3) @ComponentScan
    包扫描也是Spring中的功能 由于该注解默认扫描的类都位于当前类所在包的下面 因此建议在开发中项目启动类放在根包中
    项目启动类中的@ComponentScan注解,除了扫描@Service、@Repository、@Component、@Controller、@RestController等之外 也会扫描@Configuration注解的类
  • 定制banner
    Spring Boot项目在启动时会打印一个banner 在resource目录下创建banner.txt文件
    将 TXT 文本设成艺术字,可供参考
    http://www.network-science.de/ascii/
    http://www.kammerl.de/ascii/AsciiSignature.php
    http://patorjk.com/software/taag
  • Web容器配置
    常规配置
    在Spring Boot项目中可以内置Tomcat、Jetty、Undertow、Netty等容器
    当添加starter-web依赖后默认会使用Tomcat作为Web容器 配置application.properties
server.port=8081
server.error.path=/error
server.servlet.session.timeout=30m
server.servlet.context-path=/
server.tomcat.uri-encoding=utf-8
server.tomcat.max-threads=500
server.tomcat.basedir=/home/tmp
  • 配置解释
    port配置Web容器的端口号
    error.path配置当项目出错时跳出去的页面
    servlet.session.timeout配置了session失效时间 默认单位是秒(但会向下取最大分钟数)
    servlet.context-path项目名称 默认为/ 如果配置就要在访问路径中添加该路径
    tomcat.uri-encoding配置Tomcat请求编码
    tomcat.max-threads配置Tomcat最大线程数
    tomcat.basedir存放Tomcat运行日志和临时文件的目录 默认使用系统的临时目录

  • HTTPS配置
    使用jdk提供的/jdk/bin/keytool可以自生成数字证书 x.p12 将该文件复制到项目的根目录然后再application.properties中配置

server.ssl.key-store=x.p12
server.ssl.key-alias=tomcathttps
server.ssl.key-store-password=123456

然后使用https访问 因为证书是自己生成的不被浏览器认可 需添加信任继续前进
此时如果进行HTTP请求就会失败 因为Spring Boot不支持同时HTTP和HTTPS方式访问接口 会出现访问失败

Bad Request
This combination of host and port requires TLS.

可用重定向请求将HTTP重定向为HTTPS请求

@Configuration
public class TomcatConfig {
    @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;
    }

    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
Jetty配置

org.springframework.boot 
spring-boot-starter-web 
 
 
org.springframework.boot 
spring-boot-starter-tomcat 
 

 
org.springframework.boot 
spring-boot-starter-jetty 

移除starter-web中默认的Tomcat并加入Jetty依赖再启动Spring Boot
从日志可以看到 Jetty started on port(s) 8080 取代了 Tomcat
其他如Undertow配置与Jetty类似 是红帽公司开源的Java服务器

  • Properties配置
    Spring Boot承载自定义配置的文件是application.properties文件(通常在resources目录下 也可以使用YAML文件代替)该文件加载优先级分4个位置
    (1) 项目根目录下的config文件夹中
    (2) 项目根目录下
    (3) classpath下的config文件夹中
    (4) classpath下
    如果想使用app.properties代替application.properties
    java -jar x.jar --spring.config.name=app

● 类型安全配置属性
properties或yaml配置最终加载到Spring Environment
Spring提供@Value注解和EnvironmentAware接口来讲数据注入到属性 Spring Boot对此进一步提出了类型安全配置属性(Type-safe Configuration Properties)
在application.properties中添加

book.name=三国演义
book.author=罗贯中

将配置注入Bean中

@Component
@ConfigurationProperties(prefix="book")
public class Book {
private String name;
private String author;
//getter/setter
}

@ConfigurationProperties中的prefix属性描述要加载的配置文件的前缀

● YAML配置
YAML配置是JSON的超集 starter-web依赖间接引入snakeyaml实现对YAML解析
YAML虽然方便但也有缺陷 如无法使用@PropertySource注解加载YAML文件

● Profile
测试和生产环境配置之间切换 Spring对此提供@Profile注解的方案 Spring Boot更进一步约定配置名称application-{profile}.properties profile占位符代表当前环境的名称
然后再application.properties中配置
spring.profiles.active=dev 表示使用application-dev.properties配置文件启动项目
也可以再main方法上添加代码使用配置
SpringApplicationBuilder builder = new SpringApplicationBuilder(XApplication.class);
builder.application().setAdditionalProfiles(“dev”);
builder.run(args);
还可以再项目启动时配置
java -jar x.jar --spring.profiles.active=dev

第三章 Spring Boot整合视图层

Spring Boot官方推荐使用模板引擎Thymeleaf 也支持FreeMarker JSP技术不推荐使用
整合Thymeleaf和FreeMarker为例

第四章 Spring Boot整合Web开发

● 返回JSON数据
JSON是目前主流的数据传输方式 Spring MVC中使用消息转换器HttpMessageConverter对JSON的转换提供很好的支持 在Spring Boot中更为简化只要添加web依赖默认加入jackson-databind作为JSON处理器

public class Book {
	@JsonIgnore
	private Float price;
	@JsonFormat(pattern = "yyyy-MM-dd")
	private Date publicationDate;
	//getter/setter
}

需要@ResponseBody注解 可以采用@RestController代替@Controller和@ResponseBody
这是通过Spring中默认提供的MappingJackson2HttpMessageConverter来实现的

● 自定义转换器
常见的JSON处理器除了jackson-databind还有Gson和fastjson
● Gson
Gson是Google开源的JSON解析框架 使用Gson需要先除去starter-web中默认的jackson-databind然后加入Gson依赖
Spring Boot中默认提供Gson的自动转换类GsonHttpMessageConvertersConfiguration 但Gson如果想对日期数据格式化还需要开发者自定义HttpMessageConverter 自定义
首先看GsonHttpMessageConvertersConfiguration中的一段源码

@Bean
@ConditionalOnMissingBean
public GsonHttpMessageConverter gsonHttpMessageConverter(Gson gson) {
	GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
	converter.setGson(gson);
	return converter;
}

@ConditionOnMissingBean注解表示当前项目中没有提供GsonHttpMessageConverter时才会使用默认的GsonHttpMessageConverter 所以开发者只要提供一个即可

@Configuration
public class GsonConfig {
	@Bean
	GsonHttpMessageConverter gsonHttpMessageConverter() {
		GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
		GsonBuilder builder = new GsonBuilder();
		builder.setDateFormate("yyyy-MM-dd");
		builder.excludeFieldsWithModifiers(Modifier.PROTECTED);
		Gson gson = builder.create();
		converter.setGson(gson);
		return converter;
	}
}

● fastjson
fastjson是阿里巴巴开源的JSON解析框架 是目前JSON解析最快的开源框架 不同于Gson fastjson必须要开发者提供HttpMessageConverter才能使用 先除去jackson-databind依赖并引入fastjson依赖

@Configuration
publica class MyFastJsonConfig {
	@Bean
	FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
		FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
		FastJsonConfig config = new FastJsonConfig();
		config.setDateFormat("yyyy-MM-dd");
		config.setCharset(Charset.forName("UTF-8"));
		config.setSerializerFeatures(
		SerializerFeature.WriteClassName,
		SerializerFeature.WriteMapNullValue,
		SerializerFeature.PrettyFormat,
		SerializerFeature.WriteNullListAsEmpty,
		SerializerFeature.WriteNullStringAsEmpty
		);
		converter.setFastJsonConfig(config);
		return converter;
	}
}

配置完还要配置响应编码 否则返回JSON中文会乱码 在配置文件中spring.http.encoding.force-response=true

● 静态资源访问默认策略
在Spring MVC中对静态资源需要开发者手动配置静态资源过滤 Spring Boot中对此也提供了自动化配置简化过滤配置
Spring Boot中对Spring MVC的自动化配置都在WebMvcAutoConfiguration中 在其中还有个静态内部类WebMvcAutoConfigurationAdapter实现了WebMvcConfiguration接口 该接口中的addResourceHandlers是用来配置静态资源过滤的

public void addResourceHandlers(ResourceHandlerRegistry registry) {
 ...
	String staticPathPattern = this.mvcProperties.getStaticPathPattern();
	if (!registry.hasMappingForPattern(staticPathPattern)) {
	customizeResourceHandlerRegistration(
	registry.addResourceHandler(staticPathPattern)
	.addResourceLocations(getResourceLocations(
	this.resourceProperties.getStaticLocations()))
	.setCachePeriod(getSeconds(cachePeriod))
	.setCacheControl(cacheControl));
}

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

private String staticPathPattern= "/**";

this.resourceProperties.getStaticLocations()获取的默认静态资源位置定义在ResourceProperties中

private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };

在getResourceLocations方法中对这4个静态资源位置作了扩充

static String[] getResourceLocations(String[] staticLocations) {
String[] locations = new String[staticLocations.length + SERVLET_LOCATIONS.length];
System.arraycopy(staticLocations, 0, locations, 0, staticLocations.length);
System.arraycopy(SERVLET_LOCATIONS, 0, locations, staticLocations.length, SERVLET_LOCATIONS.length);
return locations;
}

其中SERVLET_LOCATIONS的定义是{"/"} 综上Spring Boot默认会过滤所有的静态资源 一共5个位置分别是 “classpath:/META-INF/resources/”、“classpath:/resources/”、“classpath:/static/”、“classpath:/public/“以及”/”
开发者可以将静态资源放到这5个位置 按照定义的顺序优先级递减
Spring Boot项目不需要webapp目录 所以第5个"/"可以暂不考虑
在浏览器中“hppt://localhost:8080/p1.png”即可看到classpath:/META-INF/resources的p1

● 静态资源访问自定义策略
在配置文件中定义
在application.properties中直接定义过滤规则和静态资源位置

spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath:/static/

过滤规则为/static/** 静态资源位置为classpath:/static/
浏览器中输入“http://localhost8080/static/p1.png”即可看到classpath:/static/下的资源
在Java编码定义

@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addresourceHandler("/static/**")
		.addResourceLocations("classpath:/static/");
	}
}

● 文件上传
Spring MVC对文件上传做了简化 在Spring Boot做了更进一步简化
Java中文件上传涉及两个组件CommonsMultipartResolver和StandardServletMultipartResolver
CommonsMultipartResolver使用commons-fileupload处理multipart请求
StandardServletMultipartResolver基于Servlet3.0处理multipart请求 使用时不用添加额外jar包 Tomcat7.0开始支持Servlet3.0而Spring Boot2.0.4内嵌Tomcat8.5.32可以直接使用
Spring Boot文件上传自动配置类MultipartAutoConfiguration中默认采用StandardServletMultipartResolver
部分可选配置

spring.servlet.multipart.enable=true   # 是否开启文件上传支持 默认true
spring.servlet.multipart.file-size-threshold=0   # 文件写入磁盘的阈值 默认0
spring.servlet.multipart.location=E:\\temp   #上传文件的临时保存位置
spring.servlet.multipart.max-file-size=1MB   # 上传单个文件最大大小 默认1MB
spring.servlet.multipart.max-request-size=10MB   # 多个文件上传的总大小 默认10MB
spring.servlet.multipart.resolve-lazily=false   # 文件是否延迟解析 默认false

● 多文件上传

@PostMapping("/uploads")
public String upload(MultipartFile[] uploadFiles) {}

● @ControllerAdvice
是@Controller的增强版 主要用来处理全局数据 一般搭配@ExceptionHandler、@ModelAttribute和@InitBinder使用

● 全局异常处理
@ControllerAdvice最常用就是全局异常处理

@ControllerAdvice
public class CustomExceptionHandler {
	@ExceptionHandler(MaxUploadSizeExceededException.class)
	public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {
		resp.setContentType("text/html;charset=utf-8");
		PrintWriter out = resp.getWriter();
		out.write("上传文件大小超出限制");
		out.flush();
		out.close();
	}
}

如果想让该方法处理所有异常只需要将异常类改成Exception即可 方法的参数可以有异常实例、HttpServletResponse、HttpServletRequest和Model等 返回值可以是JSON、ModelAndView等
添加全局数据
在@ControllerAdvice类中使用@ModelAttribute来设置任意Controller中Model可获取的值
请求参数预处理
@ControllerAdvice结合@InitBinder实现请求参数预处理

● 自定义错误页
@ControllerAdvice的全局异常处理用来处理应用级别的异常 容器级别的错误如Filter抛出异常就无法处理 Spring Boot还有另外的异常处理方式
Spring Boot中的错误默认是由BasicErrorController类处理 该类核心方法有两个
读书笔记《Spring Boot+Vue全栈开发实战》(上)_第1张图片
errorHtml方法返回错误HTML页面 error返回错误JSON 返回JSON比较简单 返回HTML需要调用resolveErrorView获取一个错误视图的ModelAndView 默认在error目录下查找4xx、5xx等文件作为错误视图 找不到时会用error作为默认错误页面

● 自定义Error数据
Spring Boot返回的Error信息共有5条 分别是timestamp、status、error、message和path 在BasicErrorController的errorHtml和error方法中都是通过getErrorAttributes方法获取Error信息的 该方法最终会调用DefaultErrorAttributes类的getErrorAttributes方法,而DefaultErrorAttributes类是在ErrorMvcAutoConfiguration中提供的

@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.Current)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());
}

可以看到没有提供ErrorAttributes时默认采用DefaultErrorAttributes(同时是ErrorAttributes的子类)自定义错误只要我们提供一个类继承DefaultErrorAttributes即可
读书笔记《Spring Boot+Vue全栈开发实战》(上)_第2张图片
自定义Error视图同理 默认提供DefaultErrorViewResolver
当前还有一种完全替换异常流程自定义的方式 替换ErrorMvcAutoConfiguration中的ErrorController(默认提供BasicErrorController)

● CORS支持
CORS(Cross-Origin Resource Sharing)是由W3C制定的跨域资源共享技术标准 目的是为了解决前端的跨域请求 Java中常见的跨域解决方案是JSONP但只支持GET请求

@RestController
@RequestMapping("/book")
public class BookController {
	@PostMapping("/")
	@CrossOrigin(value="http://localhost:8081", maxAge=1800,allowedHeaders="*")
		public String addBook(String name) {
		return "receive:" + name;
	}
}

@CrossOrigin中的value表示支持的域
maxAge表示探测请求的有效期 如对DELETE、PUT请求或自定义头信息的请求浏览器在执行前会先发送探测请求 配置有效期可以等过期后才再发送一次探测请求 默认1800秒(30分钟)
allowedHeaders表示允许的请求头 *表示所有请求头都被允许
可采用全局配置更方便

@Configuration
public class MyWebMvcConfig implements WebMvcConfiguration {
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/book/**")
		.allowedHeaders("*")
		.allowedMethods("*")
		.maxAge(1800)
		.allowedOrigins("http://localhost:8081");
	}
}

allowedHeaders 允许的请求头 默认允许所有的请求头信息
allowedMethods 允许的请求方法 默认允许GET、POST和HEAD
maxAge OPTIONS 探测请求的有效期
allowedOrigins 支持的域

● 配置类与XML配置
Spring Boot推荐使用Java来完成相关配置而不推荐使用XML配置
@ComponentScan注解会扫描所有Spring组件包括@Configuration
@ComponentScan注解在@SpringBootApplication注解中已经提供

● 注册拦截器
Spring MVC中提供AOP风格的拦截器 精细拦截处理能力 Spring Boot拦截器注册更加方便

public class MyInterceptor1 implements HandlerInterceptor {
	@Override
	public boolean preHandle(HttpServletRqeust request, HttpServletResponse response, Object handler) {
		System.out.println("preHandler");
		return true;
	}
	@Override
	public void postHandle(HttpServletRqeust request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
		System.out.println("postHandle");
	}
	@Override
	public void afterCompletion(HttpServletRqeust request, HttpServletResponse response, Object handler, Exception ex) {
		System.out.println("afterCompletion");
	}
}

拦截器方法执行顺序 preHandle->Controller->postHandle->afterCompletion
只有preHandle方法返回true后面的方法才执行
postHandle在拦截器链内所有拦截器返回成功时才会调用
afterCompletion只有preHandle返回true才调用

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

自定义类实现WebMvcConfigurer接口 设置拦截路径和排除路径

● 启动系统任务
有一些特殊的任务需要在系统启动时执行 如配置加载或数据库初始化等
没有使用Spring Boot可以在Listener中解决 Spring Boot中提供两种方案 CommandLineRunner和ApplicationRunner 两者基本一致 差别主要在参数上
Spring Boot启动时会遍历所有实现Runner接口的@Component并调用run方法 如果有多个可以使用@Order(1) 实现调用顺序(越小先执行)

● 路径映射
使用页面模板后 有些不需要经过控制器加载数据只是简单跳转的页面如login.html和index.html 直接在MVC配置中重写addViewControllers方法配置直接映射 提高访问速度

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

● 配置AOP
面向切面编程(Aspect-Oriented Programming) 系统运行时动态添加代码的方式
Spring框架对AOP提供很好支持
Joinpoint(连接点) 类里面可以被增强的方法即为连接点
Pointcut(切入点) 对Joinpoint进行拦截的定义即为切入点
Advice(通知) 拦截到Joinpoint之后要做的事就是通知
Aspect(切面) Pointcut和Advice的结合
Target(目标对象) 要增强的类称为Target
Spring Boot在Spring的基础上对AOP配置提供自动化配置
添加spring-boot-starter-aop依赖
在org.sang.aop.service下创建UserService

@Service
public class UserService {
	public String getUserById(Integer id) {
		return "user";
	}
}

创建切面

@Component
@Aspect
public class LogAspect {
	@Pointcut("execution(* org.sang.aop.service.*.*(..))")
	public void pc1() {}
	@Before(value = "pc1()")
	public void before(JoinPoint jp) {
		String name = jp.getSignature().getName();
		System.out.println(name + "方法开始执行");
	}
	@After(value = "pc1()")
	public void after(JoinPoint jp) {
		String name = jp.getSignature().getName();
		System.out.println(name + "方法开始结束");
	}
	@AfterReturning(value = "pc1()", returning = "result")
	public void afterReturning(JoinPoint jp, Object result) {
		String name = jp.getSignature().getName();
		System.out.println(name + "方法返回值: " + result);
	}
	@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) throws Throwable {
		return pjp.proceed();
	}
}

@Aspect注解表示这是个切面类
@Pointcut(“execution(* org.sang.aop.service..(…))”) 第一个表示返回任意值 第二个表示service包下任意类 第三个*表示类内任意方法 括号中两个点表示方法参数任意
@Around注解表示环绕通知 是所有通知里功能最强大的 可以实现前置通知、后置通知、异常通知以及返回通知的功能并且可以在此处理目标方法的异常

● 自定义欢迎页
Spring Boot项目启动后首先在静态资源路径下查找index.html作为首页文件 没有则去查找动态的index文件作为首页文件 favicon查找favicon.ico 如静态资源路径resources/static

● 除去某个自动配置
@SpringBootApplication
@EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})
开发者也可以在application.properties配置文件中配置
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration

第五章 持久层技术

● 持久层技术
持久层是Jav EE中访问数据库的核心操作 Spring Boot对常见持久层框架提供了自动化配置 如JdbcTemplate、JPA等 MyBatis的自动化配置则是MyBatis官方提供的

● 整合JdbcTemplate
添加依赖 starter-jdbc mysql-connector-java com.alibaba.druid
读书笔记《Spring Boot+Vue全栈开发实战》(上)_第3张图片
properties配置文件

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql:///chapter05
spring.datasource.username=root
spring.datasource.password=l23

实体类

public class Book {...}

数据库访问层

@Repository
public class BookDao {
	@Autowired
	JdbcTemplate jdbcTemplate;
	public int addBook(Book boook) {
		return jdbcTemplate.update("INSERT INTO book(name, author) VALUES (?, ?)", book.getName(), book.getAuthor());
	}
}

JdbcTemplate 主要操作有update、batchUpdate、query和queryForObject 还有execute方法可执行任意SQL、call方法调用存储过程等
查询时需要RowMapper将列和实体属性对应 如果一致可以直接使用BeanPropertyRowMapper 不一致则要自己实现RowMapper接口

● 整合MyBatis
MyBatis支持定制化SQL、存储过程及高级映射 几乎避免了所有JDBC代码
在传统SSM框架中MyBatis需大量XML配置 在Spring Boot中MyBatis官方提供了自动化配置方案开箱即用
读书笔记《Spring Boot+Vue全栈开发实战》(上)_第4张图片
druid数据库连接配置同jdbc
创建数据库访问层

@Mapper
public interface BookMapper {
	int addBook(Book book);
	int deleteBookById(Integer id);
	int updateBookById(Book book);
	Book getBookById(Integer id);
	List getAllBooks();
}

可以在类上添加@Mapper注解表示是MyBatis的Mapper 还有种方式是在配置类上添加@MapperScan(“org.sang.mapper”)注解表示扫描该目录所有接口作为Mapper
读书笔记《Spring Boot+Vue全栈开发实战》(上)_第5张图片

针对BookMapper接口中每个方法都在BookMapper.xml中实现
#{}用来代替接口中的参数 实体类中的属性可以直接通过#{实体类属性名}获取
XML文件建议写在resources目录下 如果放在包下Maven运行时会忽略包下的XML文件(还需要在pom.xml文件中指明资源文件位置)

@Service
public class BookService {
	@Autowired
	BookMapper bookMapper;
	public int addBook(Book book) {
		return bookMapper.addBook(book);
	}
}

● 整合Spring Data JPA
JPA(Java Persistence API)和Spring Data是两个范畴的概念
如Hibernate是一个ORM框架而JPA则是一种ORM规范 Hibernate是实现了JPA制定的ORM规范(JPA规范起草者也是Hibernate的作者) 从功能上来说 JPA相当于Hibernate的子集
Spring Data是Spring的一个子项目致力于简化数据库访问
Spring Data JPA可以有效简化关系型数据库访问代码
配置Spring Data JPA依赖
读书笔记《Spring Boot+Vue全栈开发实战》(上)_第6张图片
配置数据库和JPA相关配置

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

show-sql 表示在控制台打印JPA执行过程中生成的SQL
ddl-auto 表示项目启动时根据实体类更新数据库中的表(其他可选值create、create-drop、validate、no)
创建实体类

@Entity(name = "t_book")
public class Book {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	@Column(name = "book_name", nullable = false)
	private String name;
	@Transient
	private String description;
	//省略getter/setter
}

@Entity注解表示实体类 项目启动时自动生成表 表明为name值 不填默认为类名
所有实体都要有主键@Id @GeneratedValue表示主键自动生成及生成策略
默认表中字段名就是实体类属性名 @Column可以定制生成字段的属性 name为表的列名
@Transient表示生成数据库表时该属性被忽略
创建BookDao接口集成JpaRepository

public interface BookDao extends JpaRepository {
	List getBooksByAuthorStartingWith(String author);
	LIst 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 getBookByIdAndAuthor(@Param("author") String author, @Param("id") Integer id);
}

BookDao继承JpaRepository JpaRepository中提供了一些基本增删改查、分页和排序等 如save、findAll(pageable)
Spring Data JPA中只要方法符合既定规范就能分析出意图 如And、Or等
读书笔记《Spring Boot+Vue全栈开发实战》(上)_第7张图片

● 多数据源
JdbcTemplate、MyBatis及Spring Data JPA都可以进行多数据源配置
JdbcTemplate使用并不广泛 MyBatis灵活性较好 方便开发者进行SQL优化 Spring Data JPA使用方便 特别是快速实现一个RESTful风格的应用

第六章 Spring Boot整合NoSQL

● 整合NoSQL
非关系型数据库 不使用SQL作为查询语言 数据存储可以不需固定表格模式 可水平扩展

  1. Key/Value键值存储
    这种数据存储通常无数据结构 一般被当做字符串或二进制数据但数据加载速度快 典型场景是处理高并发或用于日志系统等 如Redis、Tokyo Cabinet等
  2. 列存储数据库
    功能相当局限 但查找速度快 容易进行分布式扩展 一般用于分布式文件系统 如HBase、Cassandra等
  3. 文档型数据库
    没有严格的数据格式 数据格式更加灵活 一般可用在Web应用中 如MongoDB、CouchDB等
  4. 图形数据库
    专注于构建关系图谱 如社交网络、推荐系统等 这类数据库有Neo4J、DEX等

● 整合Redis
Redis的Java客户端很多 Jedis、JRedis、Spring Data Redis等
Spring Boot使用Spring Data Redis为Redis提供开箱即用自动化配置

默认情况下starter-data-redis使用Redis工具是Lettuce 有的开发者习惯使用Jedis可以排除Lettuce并引入Jedis

配置Redis

spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456
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

如果使用Lettuce只需要将上面的jedis替换成lettuce即可
在Spring Boot的自动配置类中提供了RedisAutoConfiguration
读书笔记《Spring Boot+Vue全栈开发实战》(上)_第8张图片
StringRedisTemplate是RedisTemplate的子类 key和value都是字符串 采用序列化方案是StringRedisSerializer 而RedisTemplate则可以用来操作对象 序列化方案是JdkSerializationRedisSerializer 两者操作Redis的方法都是一致的
StringRedisTemplate和RedisTemplate都是通过opsForValue、opsForZSet或者opsForSet等方法首先获取一个操作对象 在使用该对象完成数据的读写
Redis集群配置 除了添加jedis外再添加依赖commons-pool2


org.apache.commons
commons-pool2

配置信息(YAML)

spring:
	redis:
		cluster:
		ports:
			- 8001
			- 8002
		host: 127.0.0.1
		poolConfig:
		max-total: 8
		max-idle: 8
		max-wait-millis: -1
		min-idle: 0

配置Redis

@Configuration
@ConfigurationProperties("spring.redis.cluster")
public class RedisConfig {
	List ports;
	String host;
	JedisPoolConfig poolConfig;
	@Bean
	RedisClusterConfiguration redisClusterConfiguration() {
		RedisClusterConfiguration configuration = new RedisClusterConfiguration();
		List nodes = new ArrayList<>();
		for (Integer port: ports) {
			nodes.add(new RedisNode(host, port));
		}
		configuration.setPassword(RedisPassword.of("123456"));
		configuration.setClusterNodes(nodes);
		return configuration;
	}
	@Bean
	JedisConnectionFactory jedisConnectionFactory() {
		JedisConnectionFactory factory = new JedisConnectionFactory(redisClusterConfiguration(), poolConfig); 
		return factory;
	}
	@Bean
	RedisTemplate redisTemplate() {
		RedisTemplate redisTemplate = new RedisTemplate();
		redisTemplate.setConnectionFactory(jedisConnectionFactory());
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
		return redisTemplate;
	}
	@Bean
	StringRedisTemplate stringRedisTemplate() {
		StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(jedisConnectionFactory());
		stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
		return stringRedisTemplate;
	}
	//省略 getter/setter
}

通过@ConfigurationProperties注解将配置文件前缀注入
配置RedisClusterConfiguration实例 设置Redis密码和Redis节点信息
根据RedisClusterConfiguration实例及连接池配置创建连接工厂JedisConnectionFactory
根据JedisConnectionFactory创建RedisTemplate和StringRedisTemplate 同时配置key和value的序列化方式 剩下的用法就和单实例Redis一致了

● 整合MongoDB
Spring Boot也提供了开箱即用的自动化配置方案Spring Data MongoDB


	org.springframework.boot
	spring-boot-starter-data-mongodb

配置文件

spring.data.mongo.authentication-database=admin
spring.data.mongo.database=test
spring.data.mongo.host=127.0.0.1
spring.data.mongo.port=27017
spring.data.mongo.username=root
spring.data.mongo.password=123

创建BookDao 定义类似于Spring Data JPA中的Repository定义

public interface BookDao extends MongoRepository {
	List findByAuthorContains(String author);
	Book findByNameEquals(String name);
}

除了继承MongoRepository外 Spring Data MongoDB还提供MongoTemplate操作 如果开发者未提供则默认会有MongoTemplate注册到Spring容器中 配置源码在MongoDataAutoConfiguration类中 直接@Autowired MongoTemplate即可使用

● Session共享
正常情况下 HttpSession是通过Servlet容器创建并进行管理的 创建成功后保存在内存中 如果做了分布式系统 可能同一用户的HTTP请求分发到不同实例就要保证Session同步
Spring Boot提供了自动化Session共享配置 它结合Redis非常方便的解决了这个问题 原理就是把原来存储在服务器上的Session放在了一个独立的服务器上 实现了Session共享
添加Redis和Session依赖


	org.springframework.session
	spring-session-data-redis

Spring Session可以做到透明化的替换掉应用的Session容器

@RestContoller
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();
	}
}

虽然操作的还是HttpSession 实际上HttpSession容器已经被透明替换 此时存储在Redis上

第七章 构建RESTful服务

● JPA实现RESTful
REST(Representational State Transfer)是Web软件架构风格而不是标准 匹配或兼容这种架构风格的网络服务称为REST服务
REST通常基于HTTP、URI、XML及HTML等现有的广泛流行协议和标准 资源由URI来指定 对资源的增删改查通过HTTP提供的GET、POST、PUT、DELETE等方法实现 通讯会话状态由客户端维护 提高服务器的扩展性
在Spring MVC框架中通过@RestController注解开发RESTful服务 Spring Boot对此又提供了自动化配置方案 只需要添加rest依赖就能快速构建RESTful服务


org.springframework.boot
spring-boot-starter-data-rest

创建实体类

@Entity(name = "t_book")
public class Book {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	private String name;
	//getter/setter
}

创建BookRepository

public interface BookRepository extends JpaRepository {}

完成上面步骤即使什么都没写 RESTful服务就已经构建成功了
可以用 POST json http://localhost:8080/books 进行提交测试
GET http://localhost:8080/books[/1] 获取数据
POST json 修改数据
DELETE http://localhost:8080/books/1 删除数据
默认情况下请求路径是实体类名小写加s 如果想对请求路径重定义可通过@RepositoryRestResource注解在BookRepository来实现 @RestResource则可用在方法上修改调用路径 如果不想暴露类或部分方法只要在注解里设置exported=false即可 默认暴露
其他配置

#每页默认记录值 默认值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
#bash-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代码中配置且代码中配置优先级高于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);
	}
}

● MongoDB实现REST


org.springframework.boot
spring-boot-starter-data-mongodb


org.springframework.boot
spring-boot-starter-data-rest

properties配置mongodb
创建Book实体类和BookRepository 服务成功搭建

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

● devtools实战
Spring Boot提供一组开发工具spring-boot-devtools可提高开发效率且支持热部署


org.springframewokr.boot
spring-boot-devtools
true

注意这里optional选项是防止将devtools依赖传递到其他模块 当开发者将应用打包运行后devtools会自动禁用
引入项目后只要classpath路径下的文件发生变化项目就会重启 提高开发速度 IDEA需要Ctrl+F9手动编译或在设置里打开自动编译
静态资源目录下文件变化和模板文件变化不会触发重启 devtools默认嵌入LiveReload服务器可以解决静态文件热部署(浏览器插件监控静态资源目录)
禁用自动重启配置 spring.devtools.restart.enabled=false

● devtools热部署基本原理
Spring Boot自动重启技术涉及两个类加载器
baseclassloader用来加载不会变化的类 例如引用的第三方jar
restartclassloader用来加载开发者写的会变化的类
项目需重启时 restartclassloader将被一个新创建的类加载器替代 而baseclassloader继续使用原来的 所以比冷启动快很多

● 单元测试 Service测试
Spring Boot的单元测试与Spring中的测试一脉相承 又做了大量的简化 进而实现对Controller、Service和Dao层的代码进行测试
添加spring-boot-starter-test依赖 创建测试类

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

@RunWith注解 将JUnit执行类修改为SpringRunner 而Spring Runner是Spring Framework中测试类SpringJUnit4ClassRunner的别名
@SpringBootTest注解除了提供SpringTestContext中的常规测试功能外还提供默认的ContextLoader、自动搜索@SpringBootConfiguration、自定义环境属性、为不同的webEnvironment模式提供支持等 其中webEnvironment模式主要有四种
MOCK 这种模式是当classpath下存在servletAPIS时创建WebApplicationContext并提供一个mockservlet的模拟环境 存在Spring WebFlux则创建 ReactWebApplicationContext 都不存在则创建常规的ApplicationContext
RANDOM_PORT 提供一个真实的Servlet环境 使用内嵌的容器 端口随机
DEFINED_PORT 提供一个真实的Servlet环境 使用内嵌的容器 使用定义好的端口
NONE 加载一个普通的ApplicationContext 不提供Servlet环境 一般不适用于Web测试
Spring Boot中@*Test注解会去包含测试类的包下查找带有@SpringBootApplication或@SpringBootConfiguration注解的主配置类
@Test注解junit junit中的@After、@AfterClass、@Before、@BeforeClass、@Ignore等注解一样可以在这里使用

● 单元测试 Controller测试
Controller测试要使用到Mock测试 对一些不易获取的对象采用创建虚拟对象 Spring中提供的MockMvc提供了对HTTP请求的模拟 不依赖网络即可实现对Controller快速测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class Test01ApplicationTests {
    @Autowired
    HelloService helloService;
    @Autowired
    WebApplicationContext wac;
    MockMvc mockMvc;
    @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());
    }
}

注入一个WebApplicationContext来模拟ServletContext环境
@Before每个测试方法执行前进行MockMvc初始化操作
MockMvc调用perform开启一个RequestBuilders请求并验证响应码200和打印结果

● JSON测试
开发者使用@JsonTest测试JSON序列化和反序列化 该注解自动配置Jackson ObjectMapper @JsonComponent以及Jackson Modules 如果用Gson代替Jackson该注解将配置Gson

至此上篇更新本书的1-8章节完成,即将发布本书下篇9-16章节,敬请关注~

你可能感兴趣的:(读书笔记,spring,boot,vue.js,java)