文章写得比较详细,所以很长(105336 字数),可以参考目录
Spring Boot 官方文档
Spring Boot 来简化Spring 应用的开发,约定大于配置,去繁从简,明显提高开发效率
入门快,精通很难
Spring Boot是对Spring 框架的再封装,若对Spring不了解,对SpringBoot的封装机制也不会很了解,Spring Boot中许多自动配置,需要我们了解Spring的API(只有在了解Spring的API后才能更精通Spring Boot
martin fowler详细介绍了微服务Martin Fowler:microservices
微服务是一种架构风格
提倡在开发应用时,一个应用应该是一组小型服务;可以通过HTTP的方式进行互通
给maven的settings.xml配置文件的profiles标签添加
表明使用jdk1.8进行开发
<profile>
<id>jdk-1.8id>
<activation>
<activeByDefault>true/ activeByDefault>
<jdk>1.8jdk>
activation>
<properties>
<maven. compiler .source>1。8maven.compiler.source>
<maven. compi ler. target>1.8maven.compller.target>
<maven. compiler. compilerverslon>1.8maven.compiler.compllerversion>
propert1es>
profile>
进入设置页面 Ctrl+Alt+S
Build,Execution,Deployment–>Build Tools–>Maven
设置maven为本地的maven, 库也为本地maven库
完成一个功能:Hello,比较简单,不记了
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.0.RELEASEversion>
<relativePath/>
parent>
他的父项目是
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.2.0.RELEASEversion>
<relativePath>../../spring-boot-dependenciesrelativePath>
parent>
其中有各种依赖的版本,Spring Boot 通过此父项目真正管理Spring Boot里面的所有依赖版本(Spring Boot的依赖仲裁中心)
以后导入依赖不需要版本,(没有depenencies中依赖管理的需要自己声明版本号)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
spring-boot-stater-web
spring-boot-stater:Spring Boot 场景启动器:帮我们导入了web模块正常运行的组件
Spring Boot将所有的功能都抽取出来,做成一个个的staters(启动器),只需要在项目中引入这些启动器,相关的依赖就能自动导入
@SpringBootApplication
public class CaitApplication {
public static void main(String[] args) {
SpringApplication.run(CaitApplication.class, args);
}
}
@SpringBootApplication 核心注解,标注在某个类上说明这个类是Spring Boot的主配置类,Spring Boot 一个运行这个类的main方法来启动应用:
@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}
)}
)
@ConfigurationPropertiesScan
public @interface SpringBootApplication {
@SpringBootConfiguration Spring Boot 的配置类;
@EnableAutoConfiguration 开启自动配置功能;
以前我们需要配置的东西,Spring Boot 帮我们自动配置;
@EnableAutoConfiguration告诉SpringBoot开启自动配置功能;这样自动配置才能生效
更多知识在 Spring注解版
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
接下来从底层理解:
我们进入
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
@Import既然是SpringBoot 的底层注解,那Import的是什么?我们查看Registar的内容,发现这个是一个静态类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
其中,这个方法registerBeanDefinitions将bean注册到容器中,我们想知道注册的到底是什么,对这行代码进行调试,
点击运行Spring Boot,
通过调试信息我们知道,这个注解源头在CaitApplication(Spring Boot 启动类)
我们选择getPackName()方法,鼠标右键选择计算表达
结果证明:导入的包为com.cait.cait,也就是启动类所在的包中所有的bean。这表明,
@AutoConfigurationPackage的实质是注册启动类所在包中所有标记的类为Bean
(将主配置类所在包下以及所有子包中所有的组件扫描到Spring容器)
那么问题来了EnableAutoConfiguration中也有一个Import,这个Import又是什么作用
@Import()给容器中导入括号内组件
点进AutoConfigurationImportSelector.class
由于本人使用的Spring Boot 2.X,与1略有不同,下方为1的老师讲解
EnableAutoConfigurationImportSelector:导入哪些组件的选择器
选择debug模式,查看
通过名字了解到是很多自动配置类(XXXAutoConfiguration):就是给容器中导入这个场景需要的所有组件并配置好组件,使用不同的功能对应不同的配置文件
通过配置类,免去了我们手动编写配置注入功能组件的工作!!!
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,classLoader);
Spring Boot 在启动的时候从类路径下META-INF/spring.factories中获取EnableAutoConfiguration指定的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作以前我们需要自己配置的配置类,自动配置类都帮我们完成。J2EE的整体解决方案和自动配置都在spring-boot-autoconfigure-XXXX.jar中
IDE都支持Spring Boot 的快速创建项目
IDEA对SpringBoot的项目支持可以说是点击就能完成基础的搭建,方便的不得了,
1.左上角File选项,New project,选择Spring Initializr
2.设置项目信息,Group 会自动创建Group文件夹,包含项目的代码;Artifact 的名字必须使用小写与下划线构成!!
3.选择web项目,右上角可以选择SpringBoot 的版本,一定要使用relese版本(正式版),不要使用SNAPSHOT版本
4.确定project的名字与位置,名字就是项目文件夹的名字
点击finish就好啦,一个SpringBoot+Maven项目就搞定了,最后创建三个基础包,上车愉快!
CSDN博客
Spring Boot 默认使用两种配置文件
配置文件的作用:修改Spring Boot自动配置的默认值;Spring Boot在底层自动配置好
YAML(YAML Ain’t Markup Language )语言的文本,
标记语言:
K:(空格)Value 表示一对键值对(空格必备)
以**空格**的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的
server:
port: 8081
path: /hello
属性与值也是大小写敏感的
key: value (中间有空格)字面直接来写;
key: value
对象还是key: value的方式
example: create a object with propertities : lastName age
friends:
lastName: zhangshan
age: 22
行内写法:用一行表示
friends: {lastName: zhangshan , age: 18}
用-值表示数组中的一个元素
pets:
-cat
-dog
-pig
行内写法
pets: [cat,dog,pig]
可以在测试期间很方便的类似编码一样进行自动注入容器的功能
package com.cait.cait;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class CaitApplicationTests {
@Autowired
要进行测试的类
@Test
void contextLoads() {
进行操作
}
}
@Value
String LastName;
是Spring 底层的一个注解,
对应的xml
<bean class="person">
<property name="LastName" value="字面量/${key}/#{SpEL}">property>
bean>
注: prefix必须全部为小写,配置的类名不能使用大写!!!!!,如 subMit是错误的!!
首先 类 与 配置文件要对应,在类前注释 @ConfigurationProperties(prefix=“pets”)
@ConfigurationProperties 告诉SpringBoot将本类中的所有属性和配置文件中相关属性进行绑定;
perfix=“pets”:配置文件中,该前缀下面的所有属性,进行一一映射
若报错,点击提示,官方说明应该添加依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能
添加@Component注解加入容器组件
总结:导入配置文件处理器,以后进行配置文件操作就有提示,类中属性的名要对应,类名可以不同,加入注解@ConfigurationProperties(prefix = “属性的前缀”)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
配置文件少用中文!!!
若输出为乱码,原因是idea使用的是utf8
打开Setting,搜索file encoding(文件编码),选择UTF8,同时选择需要在运行时转译为ascii
类型 | @ConfigurationProperties | @Value |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | supported | unsupported,要严格对应名 |
SpEL | unsupported | supported |
JSR303 | supported | unsupported |
复杂类型封装 | supported | unsupported, only simple type is permitted |
配置文件yml于properties他们都能获取到值
注:@PropertySource默认只能支持properties文件!!!!,
解决方案:SpringBoot配置文件@PropertySource 同时支持properties文件与yaml(yml)
想让Spring的配置文件生效,加载进来;将@ImportResouce标注在一个类上
@ImportResource(locations= {"classpath:bean.xml"})
导入Spring配置文件:beans.xml并使其生效
配置类=======Spring 配置文件
使用@Bean添加
package com.jirath.springboot_learn.config;
import com.jirath.springboot_learn.service.HelloService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* '@Configuratoin' point out that current object is a Configuration Class,which used to replace the Spring Configuration file before.
*/
@Configuration
public class MyConfig {
/**
* add current function's returned value to the ContextContainer,
* the id of the module in this ContextContainer is the name of function;
* @return the object which used to build a Bean
*/
@Bean
public HelloService helloService(){
return new HelloService();
}
}
r a n d o m . v a l u e 、 {random.value}、 random.value、{random.int}、 r a n d o m . l o n g 、 {random.long}、 random.long、{random.int(10)}、 r a n d o m . i n t [ 1024 , 65536 ] 、 {random.int[1024,65536]}、 random.int[1024,65536]、{random.uuid}
app.name=MyApp
app.description=${app.name} is a Spring Boot application
在开发过程中会遇到开发和实际生产时项目的配置是不同的情况,应对这种情况,Spring设置了Profile,
Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境
我们在主配置文件编写的时候,文件名可以是 application-(profile).properties/yml
!!!注意,文件的profile必须在三个字符以内,否自无法作为配置文件!!
默认使用application.properties的配置:
spring:
profiles:
active: dev
---
server:
port: 8081
spring:
profiles: dev
---
server:
port: 8081
spring:
profiles: prod
spring.profiles.active=dev
命令行:
–spring.profile.active=dev
idea测试方法
打包:
虚拟机方法:
spring boot 启动会扫描以下位置的application.properties / yml 文件作为Spring Boot的默认配置文件
Spring Boot也可以从以下位置加载配置;优先级从高到低
Spring Boot官方文档
优先加载带Profile,jar包外部的
Spring Boot官方文档关于配置文件
Spring Boot启动的时候,加载主配置类,开启了自动配置功能@EnableAutoConfiguration,
@EnableAutoConfiguration的作用:
利用EnableAutoConfigurationImportSelector给容器中导入一些组件
可以查询selectImports()方法的内容;
Listconfigurations=getCandidateConfigurations(annotation Metadata,attributes);获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();
private SpringFactoriesLoader() {
}
扫描所有jar包类路径下 META-INF/spring.factories
把扫描到的这些文件的内容包装成properties对象
从properties中获取到EnableAutoCongratulation.class类(类名)对应的值,然后把他们添加在容器中
将类路径下 META-INF/spring.factories 里面配置的所有EnableAutoCongratulation的值加入到容器当中
位置:maven:spring-boot-autocongratulation->spring-boot-autoconfiguare-2X->META-INF->spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveRestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration
每一个这样的XXXAutoConfrigulation类都是容器的一个组件,都加入到容器中;用他们来做自动配置;
每一个自动配置类进行自动配置功能;
以**HttpEncodingAutoConfiguration(Http编码自动配置)**为例解释自动配置原理;
@Configuration(
proxyBeanMethods = false
)//表示这是一个配置类,与之前编写配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties({HttpProperties.class})//启用ConfigurationProperties功能:将配置文件中对应的值和HttpEncodingProperties绑定起来;并把 HttpProperties 加入到Spring的ioc容器中来
@ConditionalOnWebApplication(
type = Type.SERVLET
)//考虑webapp,Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类就会生效; 判断当前应用是否是web应用,如果是,当前配置类才生效
@ConditionalOnClass({CharacterEncodingFilter.class})//判断当前项目有没有这个类 CharacterEncodingFilter :SpringMVC进行乱码解决的过滤器
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)//判断配置文件中是否存在某个配置spring.http.encoding.enabled ; 如果不存在判断也是成立的,即使配置文件中不配值,也是默认生效的;
public class HttpEncodingAutoConfiguration {
/**
*他已经和SpringBoot的配置文件映射了
*
**/
private final Encoding properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean//给容器中添加组件,这个组件的某些值需要从properties中获取
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());//获取编码
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
}
所有配置文件中能配置的属性都是在xxxxProperties类中封装着
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.autoconfigure.http;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(
prefix = "spring.http"
)//从配置文件中获取指定的值和bean的属性进行绑定
public class HttpProperties {
private boolean logRequestDetails;
private final HttpProperties.Encoding encoding = new HttpProperties.Encoding();
//...................
}
```
#我们能配置的属性都来源于这个功能的properties类
spring.http.encoding.enabled=true
#字符集
spring.http.encoding.charset=utf-8
#强制编码 请求响应必须为UTF8
spring.http.encoding.force=true
xxxxxAutoConfiguration:自动配置类;
给容器中添加组件
xxxxproperties:封装配置文件中相关属性;
for example:
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的内容才生效
@Conitional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的Java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 满足SpEl表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
自动配置类必须在一定条件下生效
我们怎么知道那些自动配置类生效?
在配置文件中编写
#开启SpringBoot的debug
debug=true
我们可以通过启用debug=true属性;来让控制台打印自动生成报告,这样我们就可以很方便的知道哪些自动配置类生效
Positive matches: 自动配置类启用的
Negative matches: 未匹配到
小张在开发一个大型系统;
市面上的日志框架
JUL , JCL , Jboss-logging , logback , log4j , log4j2 , slf4j
日志门面(日志抽象层) | 日志实现 |
---|---|
log4j、JUL(java.util.logging)、log4j2、logback |
左边选一个门面(抽象层)、右边来选一个实现
日志门面:SLF4J;
日志实现:Logback > log4j
SpringBoot :底层是Spring框架,Spring框架默认使用是JCL
SpringBoot 选用SLF4j和logback
SLF4j官网
以后在开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,二傻调用日志抽象层里面的方法;
给系统里面导入slf4j的jar和logback的实现jar
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
slf4j使用的情况
调用SELF4j的接口,SELF4j调用底层日志的jar包
通过适配层将两者关联
for example:a情况(slf4j+logback):Spring (commons-logging)、Hibernate( jBOSS-logging)、Mybatis…
统一日志记录,即使 是别的框架,和我一起统一使用slf4j进行输出
介绍
----------------------SpringBoot就是这么实现的
使用idea创建,选择web,默认携带self4j+logback
在pom文件中,选择导入的依赖,右键,选择Diagrams->Show Dependencies,idea就可以用树状图的方式展现依赖之间的关系,按住alt即可放大镜查看
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
SpringBoot 使用他来做日志功能
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
<version>2.2.0.RELEASEversion>
<scope>compilescope>
dependency>
总结:
SpringBoot能自动适配所有的日志,而且底层使用的是slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉
SpringBoot 默认帮我们配置好了日志;
//记录器
org.slf4j.Logger logger= org.slf4j.LoggerFactory.getLogger(getClass());
@Test
void contextLoads() {
//日志的级别
//由低到高 trace debug info warn error
//可以调整需要输出的日志级别,日志就只会在这个级别的更高级别生效
logger.trace("这是trance日志");
logger.debug("这是debug日志");
//SpringBoot 默认使用的是info级别,没有指定级别的就用SpringBoot默认规定的级别:root级别(info)
logger.info("这是info日志");
logger.warn("这是warn日志");
logger.error("这是error日志");
}
SpringBoot修改日志默认配置
path和file都存在,file为主
logging.level.com.jirath=trace
#在当前磁盘下新建Spring和log文件夹:使用spring.log作为默认文件
logging.file.path=/spring/log
#不指定路径就在当前项目下生成日志
#logging.file.name=springboot.log
#可以指定完整路径
logging.file.name=E:/springboot.log
#在控制台输出的日志格式
logging.pattern.console================%n%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
#指定文件中日志输出格式
logging.pattern.file=
SpringBoot 关于日志的其他默认设置在哪里?
位于
spring-boot-2.2.0.RELEASE.jar!/org/springframework/boot/logging/logback/中
打开即为SpringBoot针对LogBack的配置封装。
其中,SpringBoot将默认级别(root)设置为了info
在配置文件中配置的属性会被一个名为LoggingApplicationListener(单击配置文件配置项即可查看)相似的类接收
LoggingApplicationListener同时与一个LoggingSystemProperties(在logback桶目录下)的类对应在这里获取输入的值,进行解析
在base.xml中SpringBoot针对控制台与文件输出有分别的定义,如图
在文件默认配置中,SpringBoot同时设置了文件在最大数值,当超出数值,日志就会自动分文件记录,如:log1,log2
在实际使用时,同时会遇到其他功能的需求,自动归档等等等等,
要想使用自己的配置文件,Spring.io中在特性下有一章节专门讲述了SpringBoot配置默认日志的方法:在Spring目录中放logback.xml即可
给类路径下放上每个框架自己的配置文件即可:SpringBoot就不再使用默认配置
Spring Boot includes a number of extensions to Logback that can help with advanced configuration. You can use these extensions in your logback-spring.xml
configuration file.
logback.xml:直接被日志框架识别
logback-spring.xml:日志就不直接加载日志配置项,由SpringBoot解析日志配置,就可以使用SpringBoot的高级Profile功能
<springProfile name="dev">
springProfile>
例子:使用log4j
但是,此行为无意义,log4j因为表现不理想,原作开发了logback;
使用其他日志框架:
Thymeleaf、web定制、容器定制
自动配置原理?
这个场景SpringBoot帮我们配置了什么?能不能修改?能不能扩展?…
在WebMvcAutoConfiguration中
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
}
由上方代码,所有/webjars/**,下的请求都会去classpath:/META-INF/resources/webjars/中寻找资源
代码中同时设置了缓存时间,缓存时间可以在resourceProperties中设置
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
@ConfigurationProperties(
prefix = "spring.resources",
ignoreUnknownFields = false
)
public class ResourceProperties {
//可以设置与静态资源有关的参数,缓存时间
第二种规则:"/**"访问当前项目的任何资源,
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
进入getResoutceLocations
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;
}
查看代码是从其他方法中引入了参数(与1版本不同)
返回查看参数的来源
getStaticLocations
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
得总结
"/**"访问当前项目的任何资源(静态资源文件夹)都会在下方的文件夹中找内容
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
}
private Optional<Resource> getWelcomePage() {
String[] locations = WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
静态资源文件夹下所有index.html页面:被"/**"映射
所有的**/favicon.ico都是在静态资源环境下找;
同时静态资源的位置是可以改变的
spring.resources.static-location=位置
之前多使用jsp页面,可以很方便的嵌入数据等,但SpringBoot 使用嵌入式的tomcat,不支持jsp页面,只能支持HTML,
JSP、Velocity、Freemarker、Thymeleaf;
模板引擎的作用:在写页面时,将一些数据与渲染数据的模板结合输出,JSP与其他的模板引擎都是这一原理
SpringBoot推荐的Thymeleaf;
语法更简单,功能更强大;
- ````xml
org.springframework.boot
spring-boot-starter-thymeleaf
````
- 详情见SpringBoot官网文档
- 布局功能的支持程序 thymeleaf3主程序 layout2以上版本
- 在AutoConf...中有配置类,打开properties有默认自动配置项
- ````java
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
private Integer templateResolverOrder;
private String[] viewNames;
private String[] excludedViewNames;
private boolean enableSpringElCompiler;
private boolean renderHiddenMarkersBeforeCheckboxes;
private boolean enabled;
private final ThymeleafProperties.Servlet servlet;
private final ThymeleafProperties.Reactive reactive;
//只要我们把HTML文件放在classpath:/templates/,thymeleaf就能自动渲染
//同时可以在配置文件中修改配置
````
- [thymeleaf官网](https://www.thymeleaf.org/)
- ![image-20191122231053533](image-20191122231053533.png)
- 现代化java服务端的模板引擎
- ![image-20191122231204398](image-20191122231204398.png)
- 使用
````ht
导入开启thymeleaf的语法空间,开启语法提示
````
````java
@RequestMapping("/html")
public String testHtml(Map map){
map.put("hello", "这是Controller中返回的话");
return "testPage";
}
````
````html
test
这是一个测试文字
这是静态的语句
````
详见官方文档:Attribute Precedence
使用${AttributeName}取值
th:任意html属性 替换原生属性的值
详见thymeleaf文档 expression
Simple expressions:(表达式语法)
Established locale country: US.
OGNL的例子
/* * Access to properties using the point (.). Equivalent to calling property getters. */
${person.father.name}
/* * Access to properties can also be made by using brackets ([]) and writing * the name of the property as a variable or between single quotes. */
${person['father']['name']}
/* * If the object is a map, both dot and bracket syntax will be equivalent to * executing a call on its get(...) method. */ ${countriesByCode.ES} ${personsByName['Stephen Zucchini'].age}
/* * Indexed access to arrays or collections is also performed with brackets, * writing the index without quotes. */ ${personsArray[0].name}
/* * Methods can be called, even with arguments. */ ${person.createCompleteName()} ${person.createCompleteNameWithSeparator('-')}
SpringBoot官方文档对SpringMVC的介绍
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
The auto-configuration adds the following features on top of Spring’s defaults:
以下是SpringBoot 对SpringMVC的默认配置:
Inclusion of ContentNegotiatingViewResolver
and BeanNameViewResolver
beans.
Support for serving static resources, including support for WebJars (covered later in this document)). 静态资源文件夹路径和webjars
Automatic registration of Converter
, GenericConverter
, and Formatter
beans.
Converter
, GenericConverter
, and Formatter
beansConverter
转换器:SpringMVC中类型转换使用ConverterFormatter
格式化器:2017-11-3–>Date,不同国家的表示不同,按照一定格式转化过来为转化器 /**
在配置文件中配置日期格式化的规则
*/
@Bean
public FormattingConversionService mvcConversionService() {
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
this.addFormatters(conversionService);
return conversionService;//日期格式化组件
}
Support for HttpMessageConverters
(covered later in this document).
HttpMessageConverters
:SpringMVC中转换Http请求和响应的;User类->jsonHttpMessageConverters
是从容器中确定的;获取所有的HttpMessageConverter;Automatic registration of MessageCodesResolver
(covered later in this document).定义错误代码生成规则
Static index.html
support. 静态首页访问
Custom Favicon
support (covered later in this document).
Automatic use of a ConfigurableWebBindingInitializer
bean (covered later in this document).从容器中取出,故可以自己配一个ConfigurableWebBindingInitializer
来替换默认的
org.springframework.boot.autoconfigure.web :web中所有的自动配置
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration
class of type WebMvcConfigurer
but without @EnableWebMvc
. If you wish to provide custom instances of RequestMappingHandlerMapping
, RequestMappingHandlerAdapter
, or ExceptionHandlerExceptionResolver
, you can declare a WebMvcRegistrationsAdapter
instance to provide such components.
If you want to take complete control of Spring MVC, you can add your own @Configuration
annotated with @EnableWebMvc
.
参考:
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration
class of type WebMvcConfigurer
but without @EnableWebMvc
. If you wish to provide custom instances of RequestMappingHandlerMapping
, RequestMappingHandlerAdapter
, or ExceptionHandlerExceptionResolver
, you can declare a WebMvcRegistrationsAdapter
instance to provide such components.
编写一个配置类(@Configuration),是WebMvcConfigurer
类型;不能标注@EnableWebMvc
If you want to take complete control of Spring MVC, you can add your own @Configuration
annotated with @EnableWebMvc
.
SpringBoot对MVC的自动配置不再需要,所有内容都是自己来配;
add your own @Configuration
annotated with @EnableWebMvc
.
效果:所有的SpringMVC的自动配置都失效了
原理:
源码的原理与SpringMVC中相同,不再编写
模式
1)由于SpringBoot大量使用了Condition注解,SpringBoot在自动配置很多组件的时候,先看容器中有没有这个组件(通常为用户自己配置的@Bean@Component),若没有,再进行自动配置;如果有些组件可以有多个(如ViewResolver)将用户的配置和自己的默认组合起来
2)在SpringBoot 中会有非常多的xxxxConfigure帮助我们进行扩展配置
3)在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置
将页面命名为index.html置于"classpath:/META-INF/resources/", “classpath:/resources/”, “classpath:/static/”, "classpath:/public/"目录下
编写Handler拦截路径"/“和”/index.html"返回到指定的视图
在一个 WebMvcConfigurerAdapter(已经过时)中编写一个addViewControllers完成一个视图映射
相关解决链接
新方法继承WebMvcConfigurer接口,需要@Bean注册到容器(@Configuration中包含了@Bean)
package com.jirath.springboot_learn.config;
import com.jirath.springboot_learn.service.HelloService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* '@Configuration' point out that current object is a Configuration Class,which used to replace the Spring Configuration file before.
*/
@Configuration
public class MyConfig implements WebMvcConfigurer {
/**
* add current function's returned value to the ContextContainer,
* the id of the module in this ContextContainer is the name of function;
* @return the object which used to build a Bean
*/
@Bean
public HelloService helloService(){
return new HelloService();
}
/**
在这里编写一个ViewController
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
}
步骤:
编写国际化配置文件,抽取页面需要显示的国际化信息
SpringBoot自动配置好了管理国际化资源文件的组件
MessageSourceAutoConfiguration
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingBean(
name = {"messageSource"},
search = SearchStrategy.CURRENT
)
@AutoConfigureOrder(-2147483648)
@Conditional({MessageSourceAutoConfiguration.ResourceBundleCondition.class})
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = new Resource[0];
public MessageSourceAutoConfiguration() {
}
@Bean
@ConfigurationProperties(
prefix = "spring.messages"
)
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
//设置国际化管理的基础名(去掉语言,国家代码的)
messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
//我们的配置文件可以直接放在类路径下叫messages.properties;
protected static class ResourceBundleCondition extends SpringBootCondition {
private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap();
protected ResourceBundleCondition() {
}
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
ConditionOutcome outcome = (ConditionOutcome)cache.get(basename);
if (outcome == null) {
outcome = this.getMatchOutcomeForBasename(context, basename);
cache.put(basename, outcome);
}
return outcome;
}
private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, String basename) {
Builder message = ConditionMessage.forCondition("ResourceBundle", new Object[0]);
String[] var4 = StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(basename));
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
String name = var4[var6];
Resource[] var8 = this.getResources(context.getClassLoader(), name);
int var9 = var8.length;
for(int var10 = 0; var10 < var9; ++var10) {
Resource resource = var8[var10];
if (resource.exists()) {
return ConditionOutcome.match(message.found("bundle").items(new Object[]{resource}));
}
}
}
return ConditionOutcome.noMatch(message.didNotFind("bundle with basename " + basename).atAll());
}
private Resource[] getResources(ClassLoader classLoader, String name) {
String target = name.replace('.', '/');
try {
return (new PathMatchingResourcePatternResolver(classLoader)).getResources("classpath*:" + target + ".properties");
} catch (Exception var5) {
return MessageSourceAutoConfiguration.NO_RESOURCES;
}
}
}
}
去页面获取国际化的值;
国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象)
默认的区域信息解析器,根据请求头的信息获取Locale来确定,进行国际化
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(
prefix = "spring.mvc",
name = {"locale"}
)
public LocaleResolver localeResolver() {
//若为固定的就返回固定的,若不是国定的,就从请求头中求得信息,
if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
} else {
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
}
自订的区域信息解析器(使用参数完成区域信息的解析)
可以直接在Configuration中编写一个内部类完成
编写一个用户登录的接口,方法与SpringMVC大致相同,在参数前加@RequestParam("")可以确定该参数对应的数据名,且该参数必备。
idea在运行期间不会修改代码
让编写的页面修改实时生效:
禁用thymeleaf缓存
#禁用thymeleaf缓存
spring.thymeleaf.cache=false
页面修改完成后,使用ctrl+F9重新编译
使用thymeleaf模板引擎中的判断标签
使用thymeleaf中对象的引入,引入String对象,利用String对象中的isEmpty判断是否存在该信息(错误信息)
<p style="color: red" th:text="${mesg}" th:if="${not #strings.isEmpty(msg)}">p>
即利用SpringMVC中的HandlerInterceptor
要求:
1)、RestfulCRUD:CRUD满足Rest风格
操作类型 | 普通CRUD(根据url来区分操作) | RestfulURL |
---|---|---|
查询 | getEmp | emp----GET |
添加 | addEmp?xxx | emp----POST |
修改 | updateEmp?xxx | emp/{id}----PUT |
删除 | deleteEmp?id=1 | emp/{id}----DELETE |
实验设计:
操作 | 请求URL | 请求方式 |
---|---|---|
查询所有员工 | emps | GET |
查询某个员工(来到修改页面) | emps/{id} | GET |
来到添加页面 | emp | GET |
添加员工 | emp | POST |
来到修改页面(对员工进行信息回显) | emp/{id} | PUT |
修改员工 | emp | PUT |
删除员工 | emp/{id} | DELETE |
浏览器访问
postman访问接口,信息使用json的方式返回
浏览器发送的请求头中优先接受text/html,表明优先接受html页面
相关自动配置在AutoConfigure下web中的error中配置
原理参照ErrorMvcAutoConfiguration中的错误自动配置
给容器中添加了以下组件
步骤:
一旦系统出现4xx-5xx之类的错误
为什么浏览器接收的是html页面
2)、定制错误响应:
如何定制错误的页面;
即在templates下新建error文件夹,里面放上错误代码命名的html(b),即可
定制错误的Json数据;
编写一个错误处理Handler,新建Controller类,使用@ControllerAdvice注解,
使用此方法将捕获服务器产生的异常,然后返回设定的内容。
@ControllerAdvice
public class ExceptionHandler{
@ResposeBody
@ExceptionHandler(一个异常类.class)
public Map<String,Object> handlerException(Exception e){
Map<String,Object> map = new HashMap<>();
map.put("code","状态码");
map.put("message",e.getMessage());
return map;
}
}
该方法会设置网页和json访问都返回json数据
将当前错误设置为自适应
让错误控制转发到/error进行自适应效果处理
@ControllerAdvice
public class ExceptionHandler{
@ExceptionHandler(一个异常类.class)
public Map<String,Object> handlerException(Exception e){
Map<String,Object> map = new HashMap<>();
map.put("code","状态码");
map.put("message",e.getMessage());
//转发到/error请求
return "forward:/error";
}
}
ps:Spring的转发与重定向:在返回的视图名前添加forward: 转发,redirect: 重定向
上述代码存在页面返回为空白的问题,思考SpringBoor 的错误处理流程得知,SpringBoot在处理页面 请求的错误时,会从error请求中取出状态码,其对应的参数名在方法中可查,为javax.servlet.error.status_code
protected HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer)request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
} else {
try {
return HttpStatus.valueOf(statusCode);
} catch (Exception var4) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
}
所以我们一定要传入自己的状态码,在请求参数上加javax.servlet.error.status_code的Attribute
改进后的代码如下
@ControllerAdvice
public class ExceptionHandler{
@ExceptionHandler(一个异常类.class)
public Map<String,Object> handlerException(Exception e,HttpServletRequest request){
Map<String,Object> map = new HashMap<>();
//传入自己的状态码,这里设置为777
request.setAttribute("javax.servlet.error.status_code",777)
map.put("code","状态码");
map.put("message",e.getMessage());
//转发到/error请求
return "forward:/error";
}
}
这样就能来到自己定制的777错误页面
问题:无法携带定制数据
将我们定制的数据携带出去
出现错误后,会来到/error请求,被BasicErrorController处理,响应出去可以获取的数据是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法);
完全编写一个ErrorController的实现类【或者继承AbstractErrorController的子类】放在容器中;
两种返回方法(页面与json)都使用了getErrorAttributes来得到数据,我们可以自己编写一个MyErrorAttributes,继承DefaultErrorAttributes(下面方法位于的类),重写getErrorAttributes,先调用父类方法,返回父类方法返回的map,也可以在map中添加自己的信息
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap();
errorAttributes.put("timestamp", new Date());
this.addStatus(errorAttributes, webRequest);
this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);
this.addPath(errorAttributes, webRequest);
return errorAttributes;
}
自定义
@Component
public class MyErrorAttributes extends DefultErrorAttributes{
//返回值的map就是页面和json能获取的所有字段
@Override
public Map<String,Object>......{
Map<String,Object> map=super.getErrorAttributes(....);
map.put("team","ccc");//个性添加字段
return map;
}
}
程序异常–>转发/error–>使用ErrorAttributes获取异常值
ErrorAttributes返回值的map就是页面和json能获取的所有字段,该方法有一参数为requestAttributes可以使用此来获取在异常处理时,放在Attribute中的信息。但是在取参数时有两个参数,第一个为Key第二个为scope,相信见源码中,0为request域。
在之前写web应用时,需要先打包,然后部署在tomcat容器中,
SpringBoot默认使用的是嵌入式Servlet容器(Tomcat)
问题?
1、外置的Servlet容器如何定义和修改Servlet容器的相关配置;
1)修改server相关配置(ServerProperties)
server.port=8080
server.context-path=
#通用的Servlet容器设置
server.xxx
#Tomcat的设置,(属性中有一个为tomcat)
server.tomcat.xxx
编写一嵌入式的servlet容器定制器;来修改Servlet容器的配置,两种方式任选一个,是同一个底层原理。
在MvcConfig中添加一个EmbeddedServletContainerCustomer
传统web应用的目录结构 webapp/WEB-INF/web.xml,在web.xml中注册组件
SpringBoot如何注册?
先编写一个继承HttpServlet的类
添加Bean,返回一个指定的注册Bean,参数为写好的类与映射,完成Servlet注册
实现接口Filter来编写Filter类
添加filter到容器中
注册ServletContextListener
SpringBoot 帮我们启动SpingMvc的时候,自动注册Spring前端控制器;DispatcherServlet通过server.servlet-path来修改SpringMVC前端控制器,默认配置为"/"
Jetty(长连接,如聊天)
Undertow(不支持JSP)
SpringBoot支持三个Servlet服务器
tomcat、Jetty、Undertow,默认使用Tomcat
自动配置jar,web->EmbeddedServletContainerAutoConfiguration(嵌入式Servlet容器自动配置)
SpringBoot 2版本相对于1代在这里去掉了对于容器中是否有用户自定义的Servlet工厂的判断,
/**
*判断当前是否引入Tomcat依赖,若引入就加载Tomcat配置
*/
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
public static class TomcatWebServerFactoryCustomizerConfiguration {
public TomcatWebServerFactoryCustomizerConfiguration() {
}
@Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
Spring Boot是通过检查项目中是否存在相关容器的依赖,然后加载对应的配置,启动对应的Servlet容器。创建对应的工厂对象,加载配置的编码,连接数等等等等参数设置。配置好以后返回并启动
所以必为EmbeddedServletContainerCustomer:定制器帮我们修改了Servlet容器的配置
步骤:
什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器启动Tomcat
嵌入式Servlet:打为可执行jar
优点:简单、便携
缺点:默认不支持JSP、优化定制比较复杂(使用定制器【ServerProerties、自定义EmbeddedServletContainerCustomizer】,自己编写嵌入式Servlet容器的创建工厂)
idea新建项目勾选jdbc,mysql,web
idea生成的项目自动携带了jdbc的stater和jdbc依赖
spring.datasource下进行
使用测试类查看数据源信息
位置在autoconfigure中的jdbc包中
参考DataSourceConfiguration,根据配置创建数据源,默认使用Tomcat(2版本使用hikari)连接池;可以使用spring.datasource.type指定自定义的数据类型
SpringBoot默认支持 Tomcat,hikari,Basic三种DataSource
DataSourceInitializer: ApplicationListener
作用:
默认只需要将文件命名:
schema-*.sql data-*.sql
失败的在配置文件加initialization-mode: always(2版本的改动)
若想配置自己定义的名字,在配置文件中datasource下配置schema,查看关联的properties可以得知改项接收的为list,使用yml的list写法即可 如:- classpath:department.sql
视频已经过时,就自己找来的文档
Druid首先是一个数据库连接池。Druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。Druid是阿里巴巴开发的号称为监控而生的数据库连接池!
Druid是一个JDBC组件,它包括三个部分:
基于Filter-Chain模式的插件体系。
DruidDataSource 高效可管理的数据库连接池。
SQLParser
1、替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。
2、可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
3、数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。
4、SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。
5、扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter机制,很方便编写JDBC层的扩展插件。
1、充当数据库连接池。
2、可以监控数据库访问性能
3、获得SQL执行日志
新的Druid推出了stater进行配置,操作很方便
官方给的介绍
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.10version>
dependency>
spring:
application:
name: springboot-test-exam1
datasource:
# 使用阿里的Druid连接池
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
# 填写你数据库的url、登录名、密码和数据库名
url: jdbc:mysql://localhost:3306/databaseName?useSSL=false&characterEncoding=utf8
username: root
password: root
druid:
# 连接池的配置信息
# 初始化大小,最小,最大
initial-size: 5
min-idle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
# 配置DruidStatFilter
web-stat-filter:
enabled: true
url-pattern: "/*"
exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
# 配置DruidStatViewServlet
stat-view-servlet:
url-pattern: "/druid/*"
# IP白名单(没有配置或者为空,则允许所有访问)
allow: 127.0.0.1,192.168.163.1
# IP黑名单 (存在共同时,deny优先于allow)
deny: 192.168.1.73
# 禁用HTML页面上的“Reset All”功能
reset-enable: false
# 登录名
login-username: admin
# 登录密码
login-password: 123456
Jpa(Spring Data Jpa)一款hibernate发展来的ORM框架,全自动持久层操作,不用书写sql。
SpringBoot整合Spring Data Jpa
mybatis应用程序通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件(也可以用Java文件配置的方式,需要添加@Configuration)来构建SqlSessionFactory(SqlSessionFactory是线程安全的);
然后,SqlSessionFactory的实例直接开启一个SqlSession,再通过SqlSession实例获得Mapper对象并运行Mapper映射的SQL语句,完成对数据库的CRUD和事务提交,之后关闭SqlSession。
说明:SqlSession是单线程对象,因为它是非线程安全的,是持久化操作的独享对象,类似jdbc中的Connection,底层就封装了jdbc连接。
详细流程如下:
1、加载mybatis全局配置文件(数据源、mapper映射文件等),解析配置文件,MyBatis基于XML配置文件生成Configuration,和一个个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着
2、SqlSessionFactoryBuilder通过Configuration对象生成SqlSessionFactory,用来开启SqlSession。
3、SqlSession对象完成和数据库的交互:
a、用户程序调用mybatis接口层api(即Mapper接口中的方法)
b、SqlSession通过调用api的Statement ID找到对应的MappedStatement对象
c、通过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象
d、JDBC执行sql。
e、借助MappedStatement中的结果映射关系,将返回结果转化成HashMap、JavaBean等存储结构并返回。
MyBatis也有SpringBoot的stater可以使用,很方便
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.2.0version>
dependency>
注意这里是dao层接口的位置,不是xml文件的位置
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.sun.houses.mapper")
public class HousesApplication {
public static void main(String[] args) {
SpringApplication.run(HousesApplication.class, args);
}
}
或者可以在接口上注解@Mapper,个人觉得很繁琐
mybatis:
#实体类位置,可以直接进行映射
type-aliases-package: com.jirath.jirathblog2.pojo,com.jirath.jirathblog2.query
#mybatis-mapper xml文件位置
mapper-locations: classpath:mybatis-mapping/*Mapper.xml
#MyBatis配置
configuration:
#开启驼峰,配置有很多,不在多写,使用默认
map-underscore-to-camel-case: true
在接口方法上增加注解
@Select("SELECT * FROM test")
@Update("UPDATE test SET test1=#{data}")
@Delete("DELETE FROM test WHERE id=#{id}")
@Insert("INSERT INTO test(...) values(...)")
为启动main方法打上断点进行调试
启动流程:
Java Cache定义了五个接口
使用JSR107需要导入依赖
<dependency>
<groupId>javax.cachegroupId>
<artifactId>cache-apiartifactId>
dependency>
依赖就提供了注解与上面的接口,接口需要进行实现,但是较为麻烦,Spring提供了自己的缓存抽象,定义了类似的注解与概念
Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术,并支持JCache(JSR-107)注解简化开发
Cache | 缓存接口、定义缓存操作、实现有:RedisCache、EhCacheCahe、ConcurrentMapCache等 |
---|---|
CacheManager | 缓存管理器,管理各种缓存(Cache)组件,对缓存真正CRUD操作在Cache组件中,每一个缓存组件有自己的名字 |
@Cacheable | 主要针对方法配置、能根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用、又希望结果被缓存(缓存更新) |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
针对方法配置、能根据方法的请求参数对其结果进行缓存,以后再查询相同的数据,直接从缓存中获取,不再调用方法
指定缓存组件的名字
缓存的数据使用的key: 可以用他来指定。默认使用方法参数的值
指定key的时候可以使用SpEL表达式
名字 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root object | 当前被调用的方法名 | #root.methodName |
method | root object | 当前被调用的方法 | #root.method.name |
target | root object | 当前被调用的目标对象 | #root.target |
targetClass | root object | 当前被调用的目标对象类 | #root.targetClass |
args | root object | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root object | 当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache | #root.caches[0].name |
argument name | evaluation context | 方法参数的名字. 可以直接 #参数名 ,也可以使用 #p0或#a0 的形式,0代表参数的索引; | #iban 、 #a0 、 #p0 |
result | evaluation context | 方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache put’的表达式 ’cache evict’的表达式beforeInvocation=false) | #result |
key的生成器(keyGenerator),可以指定KeyGenerator的生成器组件id
key与keyGenerator二选一使用
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存,在调用方法之前之后都能判断
#a0>1 第一个参数值大于1
用于否决缓存的,不像condition,该表达式只在方法执行之后判断,此时可以拿到返回值result进行判断。条件为true不会缓存,fasle才缓存
例如:
@Cacheable(value=”testcache”,unless=”#result == null”)
是否开启异步,开启异步则unless不支持
可以利用@CachePut更新@Cacheable控制的缓存,达到实时更新
缓存清除,在清除数据库数据时可以通过@CacheEvict同时清除缓存
为真时清除
清空缓存
缓存的清除是否在方法的之前执行,默认代表方法之后执行,若出现了异常就不会清除
修改为true为在方法之前,无论方法运行情况,都清除
@Caching是为了应对组合缓存注解使用的,相当于上面上注解的总和
Cache的公共配置注解,类上配置了此注解后,类中的缓存注解就有了一个公共配置
自动配置类:CacheAutoConfiguration
哪个配置类默认生效:
配置文件开启debug=true就能在启动的时候打开自动配置报告
可以看到SimpleCacheConfiguration生效了
给容器注册了一个CacheManager:ConcurrentMapManager
可以获取和创建ConcurrentMapCache类型的缓存组件:将数据保存在ConcurrentMap中
@Cacheable标注的方法执行之前先检查缓存中有没有数据,默认按照参数的值作为key去查缓存,如果没有就运行方法并将结果缓存
SpringBoot默认使用的是ConcurrentMapCacheManager:将数据保存在ConcurrentMap中(HashMap)
开发中使用缓存中间件:redis、excache等等
Redis中文网
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis的安装与使用不再赘述
引入stater:spring-boot-stater-data-redis
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-redisartifactId>
dependency>
配置配置文件:
# Redis数据库索引(默认为0 redis有16个库)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379 (取自意大利歌女Alessia Merz的名字)
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
查看RedisAutoConfiguratoin得知,redis会自动向项目注入RedisTemplate
RedisTemplate可以直接使用依赖注入的方法进行调用,操作redis
/**
* Standard Redis configuration.
*/
@Configuration
protected static class RedisConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
RedisTemplate默认保存对象是使用的Java序列化机制
原理:改写RedisTemplate
默认情况下,template.setDefaultSerializer是java序列化器,查看该方法,得知该方法需要的是一个redis序列化器接口的实现类,假如我们使用了Jackson2,该插件会自带一个json序列化器,更换为该序列化器即可。
更多需求,查看源码改写
消息最经典的三个场景异步、削峰、解耦
Tip:这三个场景也是消息队列的经典场景,大家基本上要烂熟于心那种,就是一说到消息队列你脑子就要想到异步、削峰、解耦,条件反射那种。
在普通情况下,我们所做的程序都是流水线形式的,一条一条逐步执行。
在处理需要大量操作的请求时,响应时间会激增。
在在e响应时间)在200ms内,超出的全部优化,我现在所负责的系统QPS也是9W+就是抖动一下网络集群都可能炸锅那种,RT基本上都要求在50ms以内。
你们可以看到这才加了三个,我可以斩钉截铁的告诉你真正的下单流程涉及的系统绝对在10个以上(主流电商),越大的越多。
这个链路这样下去,时间长得一批,用户发现我买个东西你特么要花几十秒,垃圾电商我不在你这里买了,不过要是都像并夕夕这么便宜,真香!
但是我们公司没有夕夕的那个经济实力啊,那只能优化系统了。
Tip:我之前在的电商老东家要求所有接口的Rt(ResponseTime响应时间)在200ms内,超出的全部优化,我现在所负责的系统QPS也是9W+就是抖动一下网络集群都可能炸锅那种,RT基本上都要求在50ms以内。
大家感受一下这个QPS。
嗯不错,链路长了就慢了,那你怎么解决的?
解决方案:异步。
你对比一下是不是发现,这样子最多只用100毫秒用户知道下单成功了,至于短信你迟几秒发给他他根本不在意是吧。
原因就是另一个特性
Tip:P0—PN 是互联网大厂经常用来判定事故等级的机制,P0是最高等级了。
你下单了,你就把你支付成功的消息告诉别的系统,他们收到了去处理就好了,你只用走完自己的流程,把自己的消息发出去,那后面要接入什么系统简单,直接订阅你发送的支付成功消息,你支付成功了我监听就好了。
问题是个好问题,但是没必要考虑,业务系统本身就是自己的开发人员维护的,你积分扣失败关我下单的什么事情?你管好自己下单系统的就好了。
Tip:话是这么说,但是这其实是用了消息队列的一个缺点,涉及到分布式事务的知识点,我下面会提到。
平时流量很低,但是你要做秒杀活动00 :00的时候流量疯狂怼进来,你的服务器,Redis,MySQL各自的承受能力都不一样,你直接全部流量照单全收肯定有问题啊,直接就打挂了。
简单,把请求放到队列里面,然后至于每秒消费多少请求,就看自己的服务器处理能力,你能处理5000QPS你就消费这么多,可能会比正常的慢一点,但是不至于打挂服务器,等流量高峰下去了,你的服务也就没压力了。
设置消息队列容纳数量,超出额度的返回异常页面,同时应用于秒杀,可以判断先后
你看阿里双十一12:00的时候这么多流量瞬间涌进去,他有时候是不是会慢一点,但是人家没挂啊,或者降级给你个友好的提示页面,等高峰过去了服务器可以继续正常工作。
没错面试官,我使用他是因为他带给我们很多好处,但是使用之后问题也是接踵而至。
同样的暖男我呀,也从三个点介绍他主要的缺点:
本来蛮简单的一个系统,我代码随便写都没事,现在你凭空接入一个中间件在那,我是不是要考虑去维护他,而且使用的过程中是不是要考虑各种问题,比如消息重复消费、消息丢失、消息的顺序消费等等,反正用了之后就是贼烦。
这个其实是分布式服务本身就存在的一个问题,不仅仅是消息队列的问题,但是放在这里说是因为用了消息队列这个问题会暴露得比较严重一点。
就像我开头说的,你下单的服务自己保证自己的逻辑成功处理了,你成功发了消息,但是优惠券系统,积分系统等等这么多系统,他们成功还是失败你就不管了?
我说了保证自己的业务数据对的就好了,其实还是比较不负责任的一种说法,这样就像个渣男,没有格局,这样呀你的路会越走越窄的。
所有的服务都成功才能算这一次下单是成功的,那怎么才能保证数据一致性呢?
分布式事务:把下单,优惠券,积分。。。都放在一个事务里面一样,要成功一起成功,要失败一起失败。
Tip:分布式事务在互联网公司里面实在常见,我也不在这里大篇幅介绍了,后面都会专门说的。
你搞个系统本身没啥问题,你现在突然接入一个中间件在那放着,万一挂了怎么办?我下个单MQ挂了,优惠券不扣了,积分不减了,这不是杀一个程序员能搞定的吧,感觉得杀一片。
至于怎么保证高可用,还是那句话也不在这里展开讨论了,我后面一样会写,像写Redis那样写出来的。
放心敖丙我不是渣男来的,我肯定会对你们负责的。点赞!
目前在市面上比较主流的消息队列中间件主要有,Kafka、ActiveMQ、RabbitMQ、RocketMQ 等这几种。
但是,ActiveMQ和RabbitMQ这两者因为吞吐量还有GitHub的社区活跃度的原因,在各大互联网公司都已经基本上绝迹了,业务体量一般的公司会是有在用的,但是越来越多的公司更青睐RocketMQ这样的消息中间件了。
Kafka和RocketMQ一直在各自擅长的领域发光发亮,不过写这篇文章的时候我问了蚂蚁金服,字节跳动和美团的朋友,好像大家用的都有点不一样,应该都是各自的中间件,可能做过修改,也可能是自研的,大多没有开源。
基于Kafka和RocketMQ两者的优点自研的消息队列中间件,吞吐量、可靠性、时效性等都很可观。
我们回归正题,我这里用网上找的对比图让大家看看差距到底在哪里:
大家其实一下子就能看到差距了,就拿吞吐量来说,早期比较活跃的ActiveMQ 和RabbitMQ基本上不是后两者的对手了,在现在这样大数据的年代吞吐量是真的很重要。
比如现在突然爆发了一个超级热点新闻,你的APP注册用户高达亿数,你要想办法第一时间把突发全部推送到每个人手上,你没有大吞吐量的消息队列中间件用啥去推?
再说这些用户大量涌进来看了你的新闻产生了一系列的附带流量,你怎么应对这些数据,很多场景离开消息队列基本上难以为继。
就部署方式而言前两者也是大不如后面两个天然分布式架构的哥哥,都是高可用的分布式架构,而且数据多个副本的数据也能做到0丢失。
我们再聊一下RabbitMQ这个中间件其实还行,但是这玩意开发语言居然是erlang,我敢说绝大部分工程师肯定不会为了一个中间件去刻意学习一门语言的,开发维护成本你想都想不到,出个问题查都查半天。
至于RocketMQ(阿里开源的),git活跃度还可以。基本上你push了自己的bug确认了有问题都有阿里大佬跟你试试解答并修复的,我个人推荐的也是这个,他的架构设计部分跟同样是阿里开源的一个RPC框架是真的很像(Dubbo)可能是因为师出同门的原因吧。
Tip:Dubbo等我写到RPC我会详细介绍的。
Kafka这是个大哥,大数据领域,公司的日志采集,实时计算等场景,都离不开他的身影,他基本上算得上是世界范围级别的消息队列标杆。
消息代理(message broker)
目的地(destination)
消息队列包括两种模式,点对点模式(point to point, queue)和发布/订阅模式(publish/subscribe,topic)。
点对点模式下包括三个角色:
消息发送者生产消息发送到queue中,然后消息接收者从queue中取出并且消费消息。消息被消费以后,queue中不再有存储,所以消息接收者不可能消费到已经被消费的消息。
点对点模式特点:
发布/订阅模式下包括三个角色:
发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。
发布/订阅模式特点:
由erlang开发的AMQP(Advanced Message Queue Protocol)的开源实现
发送者给消息代理(消息服务器)发送的内容
消息的生产者,也是一个向交换器发布消息的客户端程序。
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
Exchange有4中类型:direct(默认, 点对点)、fanout,topic和headers,不同类似转发策略不同
消息队列,用来保存消息直到发给消费者。它是消费者的容器,也是消息的终点。一个消息可以投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表
消息队列与交换器可以是多对多的
网络连接,比如一个TCP连接。
信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP命令都是通过信道发出去的。所有的动作都通过信道完成
对于操作系统来说建立和销毁TCP都是非常昂贵的开销,所有引入信道的概念,以复用一条TCP连接
消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。
表示消息队列服务器实体
就是服务器
消息中的路由键(routing key)如果和 Binding 中的 binding key 一致, 交换器就将消息发到对应的队列中。路由键与队列名完全匹配,如果一个队列绑定到交换机要求路由键为“dog”,则只转发 routing key 标记为“dog”的消息,不会转发“dog.puppy”,也不会转发“dog.guard”等等。它是完全匹配、单播的模式。
每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的
topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号“#”和符号“”。#匹配0个或多个单词,*匹配一个单词。
用docker搜索镜像安装,带有management的是含有管理界面的RabbitMQ
使用管理界面操作
简单操作
自定序列化机制
SpringAPI:RabbitTemplate
@EnableRabbit注解在启动类上,开启RabbitMQ注解
@RabbitListener(queues = “name”) 监听固定队列
下面是官方API,有耐心可以仔细看看参考
下面是AmqpAdmin的api文档
我们的应用经常需要添加检索功能,开源的 ElasticSearch 是目前全文搜索引擎的首选。他可以快速的存储、搜索和分析海量数据。Spring Boot通过整合Spring Data ElasticSearch为我们提供了非常便捷的检索功能支持;Elasticsearch是一个分布式搜索服务,提供Restful API,底层基于Lucene,采用多shard(分片)的方式保证数据安全,并且提供自动resharding的功能,github等大型的站点也是采用了ElasticSearch作为其搜索服务,
ElasticSearch官方文档
ES需要额外运行项目,暂时先不用
默认情况下,程序一步步完成所有操作然后响应请求。为了让任务互不影响,使用多线程来处理复杂的任务。
项目开发中经常需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息。Spring为我们提供了异步执行任务调度的方式,提供TaskExecutor 、TaskScheduler 接口。
两个注解:@EnableScheduling、@Scheduled
邮件发送需要引入spring-boot-starter-mail
spring-boot-starter-mail
QQ邮箱密码使用的是临时授权码,可以防止密码泄露
QQ的邮件需要ssl安全连接,需要在配置文件中开启
查看自动配置类,SpringBoot默认配置了JavaMailSenderImpl类,直接注入此类就能利用发送文件
setText有一个参数是是否为html,传送Html需要设置为true,
发送文件中参数可以为File或流,文件名可以新起。
两个安全框架Shiro与SpringSecurity,
Shiro简单,SpringSecurity复杂但功能强大,Spring Boot底层使用Spring Security
Shiro暂时不再赘述
WebSecurityConfigurerAdapter:自定义Security策略
AuthenticationManagerBuilder:自定义认证策略
@EnableWebSecurity:开启WebSecurity模式
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.springframework.securitygroupId>
<artifactId>spring-security-testartifactId>
<scope>testscope>
dependency>
package com.example.securingweb;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()//无权限403 access deined
.antMatchers("/", "/home").permitAll()
.antMatchers("/secret", "/allll").hasRole("MySelf")//需要角色MySelf才能访问两个目录
.anyRequest().authenticated()
.and()
//开启登录功能,如果未登录,就会来到登录页面,Spring Security可以自动生成登录页面
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
//配置用户信息,默认保存在内存中
@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
//jdbc形式的认证
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// ensure the passwords are encoded properly
UserBuilder users = User.withDefaultPasswordEncoder();
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser(users.username("user").password("password").roles("USER")) .withUser(users.username("admin").password("password").roles("USER","ADMIN"));
}
}
更多功能查看官方文档
在分布式系统中,国内常用zookeeper+dubbo组合,而Spring Boot推荐使用全栈的Spring,Spring Boot+Spring Cloud。
大型项目不断升级,改动越来越麻烦,采用了分离的办法,将不同的模块部署在不同的机器运行
一个项目希望得到另一个项目的数据,就需要RPC远程过程调用
但是,使用代码调用过于麻烦,不能解耦,就需求一个RPC框架,也就是Dubbo和SpringCloud
有了RPC框架,又牵扯到一个概念——注册中心,帮助不同模块进行联系,就像我们的通讯录,服务可以通过查询这个通讯录来得知该使用哪个模块。Zookeeper解决了这一需求
ZooKeeper
ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
Dubbo
Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer)两个角色。
使用docker进行安装,
参考文章
https://blog.csdn.net/myNameIssls/article/details/81561975
Zookeeper+Dubbo 生产者、消费者 都要引入
名称:自定的模块名
地址:Zookeeper服务器地址
@Service是Dubbo的Service用于发布服务,项目启动后,dubbo就会将配置的包下所有的@Service注解的服务注册在配置的Zookeeper注册中心
同第二步配置文件,不需要扫描包配置
复制服务接口到消费者项目,要求全类名相同
编写实现类调用接口,接口注解**@Reference** Dubbo会自动根据全类名搜索服务
完成。
l单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
l垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
l分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
l流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
Docker是一种容器技术,
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
使用Docker的步骤
步骤:
更换国内的源
安装 epel
yum install -y epel-release
wget -O /etc/yum.repos.d/epel-7.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum clean all
yum makecache
yum update //更新yum
yum install docker //安装docker
systemctl enable docker //开机自动启动docker
service docker start //开启docker
service docker stop //停止docker
docker build -t python-test . 构造镜像
docker run 创建并启动一个容器,在run后面加上-d参数,则会创建一个守护式容器在后台运行。
docker ps -a 查看已经创建的容器
docker ps -s 查看已经启动的容器
docker start con_name 启动容器名为con_name的容器
docker stop con_name 停止容器名为con_name的容器
docker rm con_name 删除容器名为con_name的容器
docker rm (docker images -q) 删除所有镜像
docker rename old_name new_name 重命名一个容器
docker attach con_name 将终端附着到正在运行的容器名为con_name的容器的终端上面去,前提是创建该容器时指定了相应的sh
执行这个命令后,按下回车键,会进入容器的命令行Shell中。
docker logs con_name 获取容器名为con_name的容器日志
docker inspect 查看容器的详细信息
docker top con_name 查看容器名为con_name的容器内部的进程
docker exec 可以用来在容器中运行一个进程
docker exec -it con_name /bin/bash //进入容器
docker run -di --name test -p 8000:8000 django-test映射端口