SpringBoot学习part2
微服务
微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合;可以通过http的方式进行互通.
将一个个微服务想象成一个个的业务,一个模块一个模块的
-
单体应用架构(all in one)是指,我们将一个应用的中的所有应用服务都封装在一个应用中。无论是ERP、CRM或是其他什么系统,你都把数据库访问,web访问,等等各个功能放到一个war包内。
- 缺点是,哪怕我要修改一个非常小的地方,我都需要停掉整个服务,重新打包、部署这个应用war包
-
所谓微服务架构,就是打破之前all in one的架构方式,把每个功能元素独立出来。把独立出来的功能元素的动态组合,需要的功能元素才去拿来组合,需要多一些时可以整合多个功能元素。所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。
-
比如一个电商系统,查缓存、连数据库、浏览页面、结账、支付等服务都是一个个独立的功能服务,都被微化了,它们作为一个个微服务共同构建了一个庞大的系统。
-
但是这种庞大的系统架构给部署和运维带来很大的难度。于是,spring为我们带来了构建大型分布式微服务的全套、全程产品
springboot原理
1、pom.xml文件
- < parent>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
点入spring-boot-starter-parent—>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
点入spring-boot-dependencies—>核心依赖,在父工程中,所以引入一些springboot依赖时,不需要指定的版本
- 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
上面的含义就是在web场景下启动SpringBoot,自动就装配web环境所有依赖
Springboot会将所有的功能场景,变成一个个的启动器,使用什么功能,就找到对应的starter
。
2、主程序
@SpringBootApplication
public class Spbootdemo2Application {
public static void main(String[] args) {
SpringApplication.run(Spbootdemo2Application.class, args);
}
}
表面解释:@SpringBootApplication标注这个类是一个SpringBoot的应用:启动类下的所有资源被导入;调用SpringApplication的静态方法run将springboot应用启动。
- 注解:
//元注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//重点是:
@SpringBootConfiguration //springboot的配置
@Configuration //spring配置类
@Component //说明这也是个spring的组件
@EnableAutoConfiguration //自动配置
@AutoConfigurationPackage //自动配置包
@Import(AutoConfigurationPackages.Registrar.class)//自动配置包,注册
@Import(AutoConfigurationImportSelector.class) //自动配置导入选择器
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//获取所有的配置
获取候选配置的方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
自动配置的核心文件
META-INF/spring.factories
所有的资源加载到配置类里
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
springboot所有的自动配置都在启动类中扫描、加载,加载在spring.factories
,所有的自动配置类都在这里,要判断条件是否成立,只有导入了对应的start,有了对应的启动器,自动装配就生效
- run方法
//参数一:应用入口的类 参数二:命令行参数
SpringApplication.run(Spbootdemo2Application.class, args);
加载主类然后加载配置类以及其他组件
yaml配置文件
配置文件作用:修改SpringBoot自动装配的默认值,SpringBoot在底层都给我们自动配置好了;
yaml标记语言
基础语法:
k:(空格) v
来表示一对键值对(空格不能省略)。以空格的缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
值的写法:
字面量:普通的值 [数字,布尔值,字符串]
字面量直接写在后面就可以,字符串默认不用加上双引号或者单引号;
“”双引号,不会转义字符串里面的特殊字符,特殊字符会作为本身想表示的意思;
比如:name: “xing \n ming" 输出: xing 换行 ming
'' 单引号,会转义特殊字符,特殊字符最终会变成和普通字符一样输出
比如:name: ‘xing \n ming’ 输出 : xing \n ming
对象、Map(键值对)
k:
v1:
v2:
数组( List、set )
用 - 值表示数组中的一个元素,比如:
XYZ:
- X
- Y
- Z
注入配置文件
yaml可以直接给实体类赋值
测试:
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
编写Person,Dog类
@Component //注册bean
@ConfigurationProperties(prefix = "person")//把Person类与配置文件绑定
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
//get,set方法
//toString方法
}
@Component //注册bean
public class Dog {
private String name;
private Integer age;
//get、set方法
//toString()方法
}
编写yaml配置文件
person:
name: 小名
age: 6
happy: true
birth: 2020/02/02
maps: {k1: v1,k2: v2}
lists:
- code
- play
- music
dog:
name: 旺旺
age: 2
测试单元中进行测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootDemo03ApplicationTests {
@Autowired
Person person = new Person();
@Test
public void contextLoads() {
System.out.println(person);
}
}
控制台查看输出
tips:松散绑定,这个什么意思呢? 比如在yml中写的last-name,类中的属性lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定
JSR303数据校验
在Person类中使用 @validated来校验数据 ,如,在name属性上 只能支持Email格式
@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated //数据校验
public class Person {
@Email //name必须是邮箱格式
//还可自定义报错信息@Email(message="邮箱格式不对哦")
private String name;
}
校验的源码位置:
更多的注解如下
多文档环境切换
配置文件加载位置,默认配置位置有4个,其中file指的项目路径
springboot 启动会扫描以下位置的application.properties或application.yml文件作为Spring boot的默认配置文件
- file:./config/
- file:./
- classpath:/config/
- classpath:/
优先级1:项目路径下的config文件夹配置文件
优先级2:项目路径下配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件
Springboot的多环境配置,选择激活哪一个配置文件
可建立application.properties(默认)、application-dev.properties(开发)、application-test.properties(测试)
# application.properties中
# Springboot的多环境配置,选择激活哪一个配置文件
spring.profiles.active=dev
测试:
# application-dev.properties
server.port=8081
# application-test.properties
server.port=8082
当active=dev时,就会从8081端口启动项目。
用yml实现
建立application.yml,多环境使用---
分隔
server:
port: 8081
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: test
如何激活?
server:
port: 8081
spring:
profiles:
active: dev
自动装配再学习
- 配置文件到底怎么写?
配置文件中都存在一个规律,
@xxxAutoConfiguration ---- @xxxProperties ---- 自己的配置文件绑定
@xxxAutoConfiguration 存在默认值,如何改变?
@xxxProperties和自己的配置文件绑定后就可以使用自定义的配置了
如:配置Http的编码
在META-INF/spring.factories中
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
HttpEncodingAutoConfiguration类
//表示这是一个配置类,会被Spring接管配置
@Configuration(proxyBeanMethods = false)
//自动配置属性,HttpProperties 代码如下面
@EnableConfigurationProperties(HttpProperties.class)
//@ConditionalOnXX,spring的底层注解,根据不同的条件,来判断当前配置或类是否生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final HttpProperties.Encoding properties;
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
private static class LocaleCharsetMappingsCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final HttpProperties.Encoding properties;
LocaleCharsetMappingsCustomizer(HttpProperties.Encoding properties) {
this.properties = properties;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
@Override
public int getOrder() {
return 0;
}
}
}
HttpProperties类
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
/**
* Whether logging of (potentially sensitive) request details at DEBUG and TRACE level
* is allowed.
*/
private boolean logRequestDetails;
/**
* HTTP encoding properties.
*/
private final Encoding encoding = new Encoding();
public boolean isLogRequestDetails() {
return this.logRequestDetails;
}
public void setLogRequestDetails(boolean logRequestDetails) {
this.logRequestDetails = logRequestDetails;
}
public Encoding getEncoding() {
return this.encoding;
}
/**
* Configuration properties for http encoding.
*/
public static class Encoding {
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
/**
* Charset of HTTP requests and responses. Added to the "Content-Type" header if
* not set explicitly.
*/
private Charset charset = DEFAULT_CHARSET;
/**
* Whether to force the encoding to the configured charset on HTTP requests and
* responses.
*/
private Boolean force;
/**
* Whether to force the encoding to the configured charset on HTTP requests.
* Defaults to true when "force" has not been specified.
*/
private Boolean forceRequest;
/**
* Whether to force the encoding to the configured charset on HTTP responses.
*/
private Boolean forceResponse;
/**
* Locale in which to encode mapping.
*/
private Map<Locale, Charset> mapping;
public Charset getCharset() {
return this.charset;
}
public void setCharset(Charset charset) {
this.charset = charset;
}
public boolean isForce() {
return Boolean.TRUE.equals(this.force);
}
public void setForce(boolean force) {
this.force = force;
}
public boolean isForceRequest() {
return Boolean.TRUE.equals(this.forceRequest);
}
public void setForceRequest(boolean forceRequest) {
this.forceRequest = forceRequest;
}
public boolean isForceResponse() {
return Boolean.TRUE.equals(this.forceResponse);
}
public void setForceResponse(boolean forceResponse) {
this.forceResponse = forceResponse;
}
public Map<Locale, Charset> getMapping() {
return this.mapping;
}
public void setMapping(Map<Locale, Charset> mapping) {
this.mapping = mapping;
}
public boolean shouldForce(Type type) {
Boolean force = (type != Type.REQUEST) ? this.forceResponse : this.forceRequest;
if (force == null) {
force = this.force;
}
if (force == null) {
force = (type == Type.REQUEST);
}
return force;
}
public enum Type {
REQUEST, RESPONSE
}
}
}
看到HttpProperties的注解上有perfix=“spring.http”
此时,我们对应配置文件可以写
# 就会提示出所能写的配置,对应HttpProperties类中的属性
spring.http.
当这个配置类生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的,所以这样就可以使配置文件动态修改springboot 的一些东西。
知识点:
- Springboot启动会加载大量的自动配置类
- 我们看我们需要的功能有没有在springboot默认写好的自动配置类当中
- 再来看这个自动配置类中到底配置了那些组件,只要我们要用的组件存在,我们就不会需要手动配置了
- 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,我们只需要在配置文件中指定这些属性的值即可
- XX AutoConfiguration:自动配置类,给容器中添加组件
- XX Properties:封装配置文件中相关属性,如何修改,springboot配置文件(yml,properties,)
查看项目中哪些自动配置类生效,可以在配置文件中使用下面的代码:
debug=true
这样启动项目后,在控制台打印输出生效配置
Web开发准备
静态资源导入
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
- 第一种拿到静态资源的方法,是webjars
- 可以用maven的方式,如在webjars网站上取得< dependency>XXX< /dependency>文件放入pom.xml中
- 启动项目,在路径输入
localhost:8080/webjars/**(比如jquery/3.4.1/jquery.js)
就可以拿到web资源
- 第二种
在WebMvcProperties类中
/**
* Path pattern used for static resources.
*/
private String staticPathPattern = "/**";
//当在localhost:8080/**--->会到下面的4个路径去找
在ResourceProperties类中
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" };
/**
* Locations of static resources. Defaults to classpath:[/META-INF/resources/,
* /resources/, /static/, /public/].优先级如排序顺序!
*/
- 还可以自定义路径
配置文件中
spring.mvc.static-path-pattern=/lala/
首页定制
WebMvcConfiguration类中
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
}
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
//在静态资源目录下,找到index.html就会映射到首页
return this.resourceLoader.getResource(location + "index.html");
}
private boolean isReadable(Resource resource) {
try {
return resource.exists() && (resource.getURL() != null);
}
catch (Exception ex) {
return false;
}
}
在templates目录下的所有页面,只能通过controller来跳转
Thymeleaf模板引擎
模板引擎作用:
使用:在pom.xml中
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
thymeleaf所有模板引擎写在templates目录下
ThymeleafProperties类:
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
.....
}
使用:
controller中:
@Controller
public class Controller {
@RequestMapping("/home")
public String list(Model model){
model.addAttribute("msg","hello,thymeleaf");
return "home";
}
}
在templates下建立home.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text=${msg}"></p>
<!--th: 元素名 ;就是使用thymeleaf模板,所有html元素可以被thymeleaf接管-->
</body>
</html>
简单的一些表达式:
-
Variable Expressions: ${…}
-
Selection Variable Expressions: *{…}
-
Message Expressions: #{…}
-
Link URL Expressions: @{…}
-
Fragment Expressions: ~{…}
有一些二元三元表达式:
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue)
语法:
model.addAttribute(“msg”,“hello,thymeleaf”);
return “home”;
}
}
在templates下建立home.html
```html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text=${msg}"></p>
<!--th: 元素名 ;就是使用thymeleaf模板,所有html元素可以被thymeleaf接管-->
</body>
</html>
简单的一些表达式:
-
Variable Expressions: ${…}
-
Selection Variable Expressions: *{…}
-
Message Expressions: #{…}
-
Link URL Expressions: @{…}
-
Fragment Expressions: ~{…}
有一些二元三元表达式:
- If-then: (if) ? (then)
- If-then-else: (if) ? (then) : (else)
- Default: (value) ?: (defaultvalue)
语法: