全文
发现书中的示例代码跑不通和官方提供的有差别,这一点比较坑,主要还是前4章的介绍,后面Actuator、部署大概就这些吧
书中源码地址:https://github.com/habuma/spring-boot-in-action-samples.git
## SpringBoot实战 自动配置、起步依赖、命令行界面和Actuator 不需要写一些样板配置 ### 第1章 入门 - 自动配置 SpringBoot会自动配置Bean,不用自己声明Bean - 起步依赖 把某个功能需要的一些依赖包,合成一个统一的starter依赖包 - Actuator 理解为:一种Spring运行时的日志信息 - 项目 - 命令行界面CLI spring Homebrew进行安装 brew tap pivotal/tap brew install springboot spring --version spring run xxx.groovy //运行一个SpringBoot程序 - Spring Initializr 提供脚手架项目生成 ### 第2章 开发第一个应用程序 - 项目目录结构说明 - 启动类@SpringBootApplication Spring Boot 1.2.0开始,实际包含3个注解功能 @Configuration //标明该类使用Spring基于Java的配置 @ComponentScan //启用组件扫描,Web控制器类和其他组件才能被自动发现并注册为Spring应用程序上下文里的Bean @EnableAutoConfiguration //开启了Spring Boot自动配置的魔力,让你不用再写成篇的配置了 - 使用gradle bootRun命令,来启动 使用gradle命令来启动 gradle build java -jar build/libs/readinglist-0.0.1-SNAPSHOT.jar - 测试类@SpringApplicationConfiguration 配置类里加载Spring应用程序上下文,标识如何加载Spring的应用程序上下文。 - 配置文件application.properties server.port=8000 //应用启动端口修改 - 构建过程 构建插件的主要功能是把项目打包成一个可执行的超级JAR( uber-JAR),包括把应用程序的 所有依赖打入JAR文件内,并为JAR添加一个描述文件,其中的内容能让你用java -jar来运行应用程序 - Gradle中的SpringBoot插件 //依赖插件 dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin: ➥ ${springBootVersion}") } //使用插件 apply plugin: 'spring-boot' gradle bootRun 运行程序 - Maven中的SpringBoot插件 <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> spring-boot:run 运行程序 - 使用起步依赖:基于功能的依赖 POM (Project Object Model) 起步依赖没有版本,起步依赖本身的版本是由正在使用的Spring Boot的版本来决定的,而起步依赖则会决定它们引入的传递依赖的版本。 依赖树🌲: gradle dependencies mvn dependency:tree \ -符号表示此节点是为当前父节点列出的最后一个兄弟节点 ±表示此节点有多个兄弟节点,每个兄弟节点的开头都会有这个符号。 - Maven和Gradle对传递依赖版本不同处理的差异 Maven:总是会用最近的依赖,也就是说,你在项目的构建说明文件里增加的这个依赖,会覆盖传递依赖引入的另一个依赖。 Gradle:倾向于使用库的最新版本。因此,如果你要使用老版本的Jackon,则不得不把老版本的依赖加入构建,并把Web起步依赖传递依赖的那个版本排除掉。 例如: compile("com.fasterxml.jackson.core:jackson-databind:2.3.1") compile("org.springframework.boot:spring-boot-starter-web") { exclude group: 'com.fasterxml.jackson.core' } //starter里面传递依赖的jackson库大于2.3.1,就需要做exclude排除掉。而Maven就不用,直接依赖引入就可以了。 ========== 排除传递依赖写法: Gradle中: compile("org.springframework.boot:spring-boot-starter-web") { exclude group: 'com.fasterxml.jackson.core' } Maven中: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>com.fasterxml.jackson.core</groupId> </exclusion> </exclusions> </dependency> - 使用自动配置 - Web应用程序 - 1.定义领域模型(Model) @Entity:标记JPA实体 id属性加了@Id和@GeneratedValue:唯一键 - 2.定义仓库接口 定义接口并继承JpaRepository<Book, Long>接口 //<Book, Long>是实体类、ID //继承了18个执行常用持久化操作的方法 Spring Data提供了很神奇的魔法,只需定义仓库接口,在应用程序启动后,该接口在运行时会自动实现。 注意:自定义接口方法时,使用特定的命名方式才行: 比如:findByReader(String reader) 其中reader是一个表属性,这个表示查询的意思。 - 3.创建Web界面 Spring MVC控制器就能为应用程序处理HTTP请求。 Thymeleaf来定义应用程序的视图。 @Controller @RequestMapping("/") //来标记一个Controller @RequestMapping(value="/{reader}",method = RequestMethod.GET) //来标记某个方法,处理http请求 @Autowired 来装载Repository 方法返回值: "readingList" 标记为thymeleaf模版文件,在目录resources/templates 目录下 "redirect:/{reader}" 标记为重定向的某个http路径方法调用处理 - 资源文件 resources目录下 - readBookList.html :模版html文件 <html> <head> <title>Reading List</title> <link rel="stylesheet" th:href="@{/style.css}"></link> </head> <body> <h2>Your Reading List</h2> <div th:unless="${#lists.isEmpty(books)}"> <dl th:each="book : ${books}"> <dt class="bookHeadline"> <span th:text="${book.title}">Title</span> by <span th:text="${book.author}">Author</span> (ISBN: <span th:text="${book.isbn}">ISBN</span>) </dt> <dd class="bookDescription"> <span th:if="${book.description}" th:text="${book.description}">Description</span> <span th:if="${book.description eq null}"> No description available</span> </dd> </dl> </div> <div th:if="${#lists.isEmpty(books)}"> <p>You have no books in your book list</p> </div> <hr/> <h3>Add a book</h3> <form method="POST"> <label for="title">Title:</label> <input type="text" name="title" size="50"></input><br/> <label for="author">Author:</label> <input type="text" name="author" size="50"></input><br/> <label for="isbn">ISBN:</label> <input type="text" name="isbn" size="15"></input><br/> <label for="description">Description:</label><br/> <textarea name="description" cols="80" rows="5"> </textarea><br/> <input type="submit"></input> </form> </body> </html> - style.css body { background-color: #cccccc; font-family: arial,helvetica,sans-serif; } .bookHeadline { font-size: 12pt; font-weight: bold; } .bookDescription { font-size: 10pt; } label { font-weight: bold; } - 自动配置基于条件化配置实现 条件化配置允许配置存在于应用程序中,但在满足某些特定条件之前都忽略这个配置。 条件化装载Bean: 1.使用@Configuration来标记装载Bean的配置类 2.使用@Bean来标记注入的Bean 3.使用@Conditional,进行条件化装配,比如有两个一样的Bean,通过条件化配置来匹配某个Bean === 自动化配置基于条件化配置实现: @ConditionalOnClass 自动配置会做出以下配置决策,例如 因为Classpath里有Spring Data JPA,所以它会自动配置为根据仓库的接口创建仓库实现。 因为Classpath里有Thymeleaf,所以Thymeleaf会配置为Spring MVC的视图,包括一个 Thymeleaf的模板解析器、模板引擎及视图解析器。视图解析器会解析相对于Classpath根 目录的/templates目录里的模板。 因 为 Classpath 里 有 Spring MVC ( 归 功 于 Web 起 步 依 赖 ), 所 以 会 配 置 Spring 的 DispatcherServlet并启用Spring MVC。 ### 第3章自定义配置 1.SpringBoot提供一个标准版配置 2.SpringBoot可以让你在标准版配置上进行增删配置 - 覆盖SpringBoot自动配置(用户登录模块例子) 想要深入了解Spring Security,可以参考《 Spring实战(第4版)》中的第9章和第14章 SpringBoot添加安全配置例子(login模块): Gradle: compile("org.springframework.boot:spring-boot-starter-security") Maven: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 默认用户:user, 密码:控制台下日志打印类似:Using default security password: d9d8abe5-42b5-4f20-a32a-76ee3df658d9 - 通过属性文件外置配置 - 自动配置微调 - 禁用模版缓存 1.禁用模版缓存:(resources里面的html文件,由于缓存修改不会立即变更,需要再次启动应用才行。) java -jar readinglist-0.0.1-SNAPSHOT.jar --spring.thymeleaf.cache=false 2.或者 application.properties spring.thymeleaf.cache=false - 配置HTTPS JDK的keytool工具来创建一个密钥存储( keystore) 1.生成密钥文件 keytool -keystore mykeys.jks -genkey -alias tomcat -keyalg RSA 2.在application.properties配置: server.ssl.key-store=file:///path/to/mykeys.jks server.ssl.key-store-password=letmein server.ssl.key-password=letmein - 配置嵌入式服务器-端口修改 配置嵌入式服务器-端口修改 java -jar readinglist-0.0.1-SNAPSHOT.jar --server.port=8000 或者 application.properties server.port=8000 - 默认Logback日志配置修改 默认Logback日志配置修改: Gradle: configurations { all*.exclude group:'org.springframework.boot', module:'spring-boot-starter-logging' } Maven: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> 添加log4j依赖包 Gradle: compile("org.springframework.boot:spring-boot-starter-log4j") Maven: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> </dependency> 日志配置:application.yml下 logging: path: /var/logs/ file: BookWorm.log level: root: WARN org: springframework: security: DEBUG - 数据库配置 SpringBoot 默认是有个H2的内存数据库。 spring: datasource: url: jdbc:mysql://localhost/readinglist username: dbuser password: dbpass #driver-class-name: com.mysql.cj.jdbc.Driver //驱动可选项 Gradle添加依赖:implementation 'mysql:mysql-connector-java' - 应用程序 Bean 的配置外置(读取配置文件的值) 1.在Controller上添加@ConfigurationProperties(prefix="amazon") 2.定义一个属性:private String associateId; 3.通过application.properties声明属性 amazon.associateId=habuma-20 4.完成属性注入 ----- 通过加载一个Bean来添加属性 1.定义一个属性文件 @Component @ConfigurationProperties("amazon") public class AmazonProperties { private String associateId; ... } 2.Controller注入 @Autowired private AmazonProperties amazonProperties; - 使用 Profile 进行配置 Spring Framework从Spring 3.1开始支持基于Profile的配置 利用注解@Profile是一种条件化配置,基于运行时激活的Profile,会使用或者忽略不同的Bean或配置类 1.添加注解 @Profile("production") //条件化配置 @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { ... } 2.激活Profile,指定profiles java -jar readinglist-0.0.1-SNAPSHOT.jar -- spring.profiles.active=production application.yml里添加spring.profiles.active属性: spring: profiles: active: production - 不同环境使用不同配置 application-{profile}.properties application-{profile}.yml 特殊:yml可以使用“---”,在同一个文件配置多个profiles //未指定profile是共有的属性 logging: level: root: INFO --- spring: profiles: development logging: level: root: DEBUG --- spring: profiles: production logging: path: /tmp/ file: BookWorm.log level: root: WARN #新版profiles写法: #spring: # config: # activate: # on-profile: # -development - 定制应用程序错误页面 Spring默认会查找error的视图,找不到用默认的 实现了Spring的View接口的Bean,其 ID为error(由Spring的BeanNameViewResolver 所解析)。 如果配置了Thymeleaf,则有名为error.html的Thymeleaf模板。 如果配置了FreeMarker,则有名为error.ftl的FreeMarker模板。 如果配置了Velocity,则有名为error.vm的Velocity模板。 如果是用JSP视图,则有名为error.jsp的JSP模板。 === <html> <head> <title>Oops!</title> <link rel="stylesheet" th:href="@{/style.css}"></link> </head> <html> <div class="errorPage"> <span class="oops">Oops!</span><br/> <img th:src="@{/MissingPage.png}"></img> <p>There seems to be a problem with the page you requested (<span th:text="${path}"></span>).</p> <p th:text="${'Details: ' + message}"></p> </div> </html> </html> - error携带的属性信息 Spring Boot会为错误视图提供如下错误属性。 timestamp:错误发生的时间。 status: HTTP状态码。 error:错误原因。 exception:异常的类名。 message:异常消息(如果这个错误是由异常引起的)。 errors: BindingResult异常里的各种错误(如果这个错误是由异常引起的) trace:异常跟踪信息(如果这个错误是由异常引起的)。 path:错误发生时请求的URL路径。 - 静态资源访问,会被Controller拦截掉处理 ### 第4章测试 - 集成测试自动配置 Spring 2.5开始,集成测试支持的形式就变成了SpringJUnit4ClassRunner。这是一个JUnit类运行器,会为JUnit测试加载Spring应用程序上下文,并为测试类自动织入所需的Bean。 1.加载Spring上下文所需配置注解 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration( classes=AddressBookConfiguration.class) public class AddressServiceTests { ... } 2.使用@SpringApplicationConfiguration注解: SpringApplication不仅加载应用程序上下文,还会开启日志、加载外部属性( application.properties或application.yml),以及其他Spring Boot特性。用@ContextConfiguration则得不到这些特性,需要使用@SpringApplicationConfiguration注解。//新版本使用@SpringBootTest @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration( classes=AddressBookConfiguration.class) public class AddressServiceTests { ... } - 测试 Web 应用程序 对HTTP请求测试: 1.Spring Mock MVC提供模拟Servlet容器里测试控制器,不用真实启动应用服务器。 2.Web集成测试,在嵌入式Servlet容器(比如Tomcat或Jetty)真正的应用服务器里面测试。 - 模拟 Spring MVC MockMvcBuilders该类提供了两个静态方法 1.standaloneSetup():手工创建并注入要使用的控制器。 2.webAppContextSetup():基于WebApplicationContext实例,让Spring加载上下文及其依赖。 2例子: @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration( classes = ReadingListApplication.class) @WebAppConfiguration //开启Web上下文测试 public class MockMvcWebTests { @Autowired //注入该实例 private WebApplicationContext webContext; private MockMvc mockMvc; @Before public void setupMockMvc() { mockMvc = MockMvcBuilders .webAppContextSetup(webContext) .build();//获取MockMvc对象 } @Test public void homePage() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/readingList"))//执行get请求 .andExpect(MockMvcResultMatchers.status().isOk())//测试判断 .andExpect(MockMvcResultMatchers.view().name("readingList"))//测试判断 .andExpect(MockMvcResultMatchers.model().attributeExists("books"))//测试判断 .andExpect(MockMvcResultMatchers.model().attribute("books", Matchers.is(Matchers.empty())));//测试判断 } } - mockMvc.perform(MockMvcRequestBuilders.get("/aca"))?测试错误 - 测试 Web 安全(Spring Security) 1.添加依赖 Gradle testCompile("org.springframework.security:spring-security-test") Maven <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> 2.MockMvc添加springSecurity()方法配置 MockMvcBuilders .webAppContextSetup(webContext) .apply(springSecurity()) .build(); 3.当触发跳转到登录授权时, 3.1提供@WithMockUser登录授权,绕过登录授权查询验证 @WithMockUser(username="craig", password="password", roles="READER") 3.2@WithUserDetails会启动一个登录授权查询验证,先测试登录用户是否符合。 ``` @WithUserDetails("craig") //用户名 方法体 Reader expectedReader = new Reader(); expectedReader.setUsername("craig"); expectedReader.setPassword("password"); expectedReader.setFullname("Craig Walls"); mockMvc.perform(get("/")) .andExpect(status().isOk()) .andExpect(view().name("readingList")) .andExpect(model().attribute("reader", samePropertyValuesAs(expectedReader))) .andExpect(model().attribute("books", hasSize(0))) ``` - 测试运行中的应用程序 @WebIntegrationTest 版本太旧了,被弃用 @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration( classes=ReadingListApplication.class) @WebIntegrationTest public class SimpleWebTest { @Test(expected=HttpClientErrorException.class) public void pageNotFound() { try { RestTemplate rest = new RestTemplate(); rest.getForObject( "http://localhost:8080/bogusPage", String.class); fail("Should result in HTTP 404"); } catch (HttpClientErrorException e) { assertEquals(HttpStatus.NOT_FOUND, e.getStatusCode()); throw e; } } } - Selenium测试 它的功能远不止提交请求和获取结果。它能实际打开一个Web浏览器,在浏览器的上下文中执行测试。 ### 第5章 Groovy与Spring Boot CLI (深坑-主要讲了用Groovy怎么实现第2章的示例程序) - 选择JdbcTemplate的原因 选择JDBC的最主要原因是, Spring Data JPA在生成仓库接口的自动实现时要求有一个.class文件。 当你在命令行里运行Groovy脚本时, CLI会在内存里编译脚本,并不会产生.class文件。因此, 当你在CLI里运行脚本时, Spring Data JPA并不适用 ### 第6章 在Spring Boot中使用Grails (深坑:主要讲了Grails3的方式,实现第2章的示例程序) - 除了Thymeleaf模板的其他方式 Thymeleaf模板定义阅读列表应用程序的视图。除了Thymeleaf,Spring Boot还支持Freemarker、 Velocity和基于Groovy的模板。 ### 第7章 深入Actuator 可以监控和度量SpringBoot 通过众多的REST端点、远程Shell、JWX获得。 相当于提供一个REST接口形式,来访问查看SpringBoot的一些状态信息 - 揭秘Actuator的端点(REST接口形式) 1.引入Actuator端点依赖 Gradle compile 'org.springframework.boot:spring-boot-starter-actuator' Maven <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> CLI @Grab('spring-boot-starter-actuator') - 查看配置 /beans 查看所有Bean /autoconfig 提供Bean加载的条件 /env 查看环境配置信息、application.xml等 /mappings 查看接口路径和控制器映射关系 - 运行时度量 /metrics http请求计数、内存、线程等信息 /trace 所有Web请求的详细信息 /dump 线程信息 /health 磁盘、数据库连接状态信息 - 关闭应用程序 /shutdown application.yml 默认关闭 endpoints: shutdown: enabled: true - 获取应用信息 /info 发布应用的信息 - 连接 Actuator 的远程 shell 1.依赖 Gradle: compile("org.springframework.boot:spring-boot-starter-remote-shell") Maven: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-remote-shell</artifactId> </dependency> CLI: @Grab("spring-boot-starter-remote-shell") 2.通过ssh连接shell :类似adb shell ,adb程序内部的命令行工具 ssh user@localhost -p 2000 - autoconfig命令-类似/autoconfig - beans 命令-类似/beans - metrics命令-类似/metrics - endpoint list 获取端点列表,例如:endpoint invoke health ,来访问health端点 - 通过 JMX 监控应用程序 - 定制 Actuator,application.yml内部文件 - 修改端点名称:endpoints.endpoint-id.id - 启用和禁用端点:endpoints:metrics: enabled: false - 添加自定义度量信息 - 创建自定义跟踪仓库/健康 - 保护Actuator端点:URL路径访问加权限 @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/").access("hasRole('READER')") .antMatchers("/shutdown").access("hasRole('ADMIN')" //shutdown端点路径,需要ADMIN权限 .antMatchers("/shutdown", "/metrics", "/configprops") .access("hasRole('ADMIN')") //多个端点,,需要ADMIN权限 - application.yml端点,加统一的前缀路径 management: context-path: /mgmt ### 第8章 部署SpringBoot应用程序 部署方式: 1.命令行执行:Maven的spring-boot:run或Gradle的bootRun 2.生成可运行的JAR文件,随后在命令行中运行。 3.Spring Boot CLI在命令行中运行Groovy脚本、或者运行JAR文件 - 打包成一个WAR文件,应用服务器的部署(Tomcat、 WebSphere或WebLogic) 目前应用运行都通过内嵌在应用里的Tomcat提供服务。 1.插件 apply plugin: 'war' Gradle中的配置: war { baseName = 'readinglist' version = '0.0.1-SNAPSHOT' } Maven中: <packaging>war</packaging> 2.继承extends SpringBootServletInitializer类实现
```
public class SpringBootDeploy extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(ReadBookApplication.class); //应用启动类 ReadBookApplication } }
``` 3.构建应用程序 Gradle: gradle build Maven: mvn package 4.部署: 4.1 复制到Tomcat的webapps目录 4.2 java -jar readinglist-0.0.1-SNAPSHOT.war 5.创建生产的Profile 5.1通过DataSource来切换数据库 ``` @Bean @Profile("production") public DataSource dataSource() { DataSource ds = new DataSource(); ds.setDriverClassName("org.postgresql.Driver"); ds.setUrl("jdbc:postgresql://localhost:5432/readinglist"); ds.setUsername("habuma"); ds.setPassword("password"); return ds; } ``` 5.2 application.yml配置文件 ``` --- spring: profiles: production datasource: url: jdbc:postgresql://localhost:5432/readinglist username: habuma password: password jpa: database-platform: org.hibernate.dialect.PostgreSQLDialect ``` 6.设置生产的Profiles 6.1通过spring.profiles.active属性设置为production 6.2应用服务器上设置环境变量方式:export SPRING_PROFILES_ACTIVE=production 7.介绍了数据库迁移: Flyway( http://flywaydb.org) Liquibase( http://www.liquibase.org) - 部署云端:Cloud Foundry里部署可执行JAR文件、Heroku里部署可执行JAR文件 Platform as a Service( PaaS)
说明 · · · · · ·
表示其中内容是对原文的摘抄