SpringBoot学习(下)
五:配置文件-02
第二种person类属性注入:
@Component
//@ConfigurationProperties(prefix = "person")
public class Person {
/**
* <bean id="person">
* <property name="Name" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}">
* </property>
* </bean>
* value就是bean里面的value
* @Value是spring底层注解
*/
@Value("${person.Name}")
private String Name;
@Value("#{11*2}")
private Integer Age;
@Value("false")
private Boolean crippled;
两种方法对比:
@ConfigurationProperties | @Value | |
---|---|---|
SpEL | 不支持 | 支持 |
功能 | 批量注入配置文件中的属性(默认从全局配置文件中获取值) | 一个个指定 |
复杂类型封装(map等) | 支持 | 不支持(只能基本类型) |
松散绑定(松散语法) person.lastName person.lastname person.last_name PERSON_LAST_NAME |
支持 | 不支持 |
JSR303数据校验 | 支持 @ConfigurationProperties(prefix = “person”) @Validated 属性上面写: 比如@Email,校验注入的属性值为email格式 |
不支持 |
如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;
如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperty;
其他注解: |
@PropertySource
加载指定配置文件;@PropertySource(value={“classpath:person.properties”})
@ImportResource
导入spring的配置文件,让配置文件的内容生效;
springboot里面没有spring配置文件,我们自己编写的配置文件也不能自动识别;
想要配置文件生效,加载进来,需要此注解标注在配置类上;
@ImportResource(locations={“classpath:beans.xml”})
springboot推荐给容器中添加组件的方式;推荐使用全注解方式
配置类====spring 配置文件
不编写spring配置文件。
@Configuration 指明此类为配置类,相当于以前的配置文件;
@Bean添加bean标签 标在方法或者元注解上
将方法的返回值添加到容器中;容器中这个组件默认的id为方法名。
@Configuration
public class HeiMa {
private String color="mlgb";
private Float legnum =1.52f;
@Bean
public Pet riNa(){
System.out.println(color+"------->"+legnum);
return new Pet("mikasa",18);
}
}
@Autowired
private ApplicationContext ic;
@Test
public void test004(){
System.out.println(ic.getBean("riNa"));
}
占位符
${random.uuid/int}
${person.Name}
如果主配置文件里面没有配置,则原样打印出来。还可以通过冒号来进行默认值赋值。
Profile
多profile文件:在主配置文件编写时候,文件名可以是application-{profile}.properties/yml
默认使用application.properties的配置。
如何切换激活指定配置文件环境:
1:在主配置文件里面设置:spring.profiles.active=dev
2:命令行方式激活(programs arguments) --spring.profiles.active=prod 这个优先级更高或者打包完之后cmd里面指定。(ctrl+c停掉运行的命令,可以在所在文件夹的地址栏输入cmd回车,类似于在当前文件夹路径下打开cmd)
3:虚拟机参数VM Options
-Dspring.profiles.active=prod
如果主配置文件为yml,更简单:
yml的语法:文档块(三个短横杠)
spring:
profiles:
active: dev
server:
port: 8081
---
server:
port: 8082
spring:
profiles: dev
---
server:
port: 8083
spring:
profiles: prod
spring boot配置文件加载位置:classpath就是resources文件夹
下面优先级从高到低:
–file: ./config/
–file: ./ 项目包的根目录
–classpath:/config/
–classpath:/
不仅会覆盖,还会形成互补配置
配置项目的访问路径:
server.context-path=/boot3
运维时候还可以通过spring.config.location来改变默认的配置文件位置:
项目打包后,可以通过cmd命令行输入这个配置来加载配置文件。
ps:依然是覆盖与互补配置。
外部配置的加载顺序:(高优先级覆盖低优先级,不同内容互补配置)
配置文件不止可以写在类路径下等,还可以写在外部:(优先级从高到低)
1:命令行参数
打包之后cmd --server.port=8087
2:这个jar包外部可以理解为跟.jar包在同一文件夹下。
jar包外application-{profile}.properties或application.yml(带spring.profile)配置文件
jar包内application-{profile}.properties或application.yml(带spring.profile)配置文件
jar包外application.properties或application.yml(不带spring.profile)配置文件
jar包外application.properties或application.yml(不带spring.profile)配置文件
总结:优先加载带profile,再加载不带profile。都是由jar包外向jar包内寻找。
六:自动配置原理
配置文件能写什么?怎么写?
自动配置原理:
1)springboot启动时加载主配置类并且开启了自动配置功能
@EnableAutoConfiguration
||
||
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
2)@EnableAutoConfiguration的作用:
利用EnableAutoConfigurationImportSelector给容器中导入一些组件。
public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector
|
v
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware
这个方法:public String[] selectImports(AnnotationMetadata annotationMetadata)
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
|
v
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
|
v
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories")
SpringFactoriesLoader.loadFactoryNames扫描所有jar包类路径下的META-INF/spring.factories
把扫描到的这些文件的内容包装成properties对象。
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
传回result即List configurations的内容为通过factoryClassName获得的内容,factoryClassName内容是什么?
String factoryClassName = factoryClass.getName();
List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)
即:
SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader())
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
即:
EnableAutoConfiguration.class
从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们加载到容器中。
每一个这样的xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用它们做自动配置。
3)每一个自动配置类进行自动配置功能
以HttpEncodingAutoConfiguration(Http编码自动配置)自动配置类为例来解释自动配置原理:
@Configuration //配置类,也可以给容器中添加组件
@EnableConfigurationProperties({HttpEncodingProperties.class})
//启用指定类的ConfigurationProperties功能
//将配置文件中对应的值和HttpEncodingProperties属性绑定起来。
//***并把HttpEncodingProperties加入到容器中。
@ConditionalOnWebApplication //@Conditional根据不同的条件进行条件判断,
//如果满足指定条件,整个配置类里面的配置就会生效;判断当前应用是否是web应用。
@ConditionalOnClass({CharacterEncodingFilter.class})
//判断当前项目有无CharacterEncodingFilter这个类
//springmvc中进行乱码解决的过滤器。
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)//判断配置文件中是否存在spring.http.encoding=enable这个配置,
//不存在也默认生效。
public class HttpEncodingAutoConfiguration {
4)所有能在配置文件中配置的属性都是在xxxProperties类中封装着;配置文件能配置什么就可以参照某个功能对应的这个属性类。
根据不同条件判断决定这个配置类是否生效?
@ConfigurationProperties(prefix = "spring.http.encoding")//从配置文件中获取指定的值和bean
//的属性进行绑定
public class HttpEncodingProperties {
public class HttpEncodingAutoConfiguration {
//它已经和springboot的配置文件映射了
private final HttpEncodingProperties properties;
//仅有的有参构造器
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
@Bean//给容器中添加一个组件,组件的某些值需要从Properties中获取
@ConditionalOnMissingBean(CharacterEncodingFilter.class)
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;
}
ps:只有一个有参构造器时,参数的值就会从容器中拿。
一旦自动配置类生效,这个自动配置类就会给容器中添加bean(各种组件),
这些组件的属性是从对应的Properties类中获取的,而这些类里面的每一个属性
又是和配置文件绑定的。
SpringBoot的精髓: |
1:启动时候加载大量自动配置类
2:看我们需要的功能有没有springboot默认写好的自动配置类
3:再来看这个自动配置类配置了哪些组件;只要我们要用的组件有,
我们就不需要再配置。
4:给容器中自动配置类添加组件的时候会从properties类中获取某些属性,
这些属性可以在配置文件中指定它们的值。
@Conditional派生注解
作用:必须指定条件成立才能给容器中添加组件,配置类里面的所有内容才生效。
@ConditionalOnJava(java版本是否符合要求),@ConditionalOnBean,@ConditionalOnMissingBean
@ConditionalOnClass,@ConditionalOnProperty,@ConditionalOnWebApplication(web环境)
自动配置类只有在一定条件下才生效;如何看哪些生效了,可以在配置文件中打上“debug=true”
运行时候就会输出自动配置报告。
=========================
AUTO-CONFIGURATION REPORT
=========================
Positive matches:
Negative matches:
七:日志一
日志分为日志门面与日志实现:
门面,日志的一个抽象层(JCL(Jakarta commons logging),SLF4j(simple logging facade for java),jboss-logging)
实现(JUL(java.util.logging),Log4j,Logback,Log4j2)
Log4j2是Apache 重新设计的日志框架,不是log4j的延续。
spring使用的是jcl;
springboot使用slf4j+logback;
spring使用的是jcl;
springboot使用slf4j+logback;
举个例子,如果使用mvn新建项目,也不涉及spring,那么我们需要引入slf4j-api的实现包(靠齐springboot可以使用logback):
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
以后开发的时候不应该直接调用日志的实现类,而是调用日志抽象层里面的方法。
给系统里面导入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之后,配置文件还是做成日志实现框架的配置文件。
遗留问题:
系统依赖:spring(commons-logging),hibernate(jboss-logging),mybatis,xxx
统一日志记录,即使识别的框架,统一使用slf4j进行输出?
如何让系统所有日志统一到slf4j? |
spring-boot-starter依赖了spring-boot-starter-logging
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
spring-boot-starter-logging依赖了几个偷天换日包,还有logback-classic
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
logback-classic依赖了slf4j-api与logback-core
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
如果我们要引入其他框架,一定要把这个框架的默认日志依赖移除掉。
总之:springboot可以自动适配所有日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉;
八:日志二
日志级别由低到高:trace<debug<info<warn<error
Logger logger = LoggerFactory.getLogger(getClass());
logger.trace(“这是trace日志。。。。”);
logger.debug(“这是debug日志。。。。”);
logger.info(“这是info日志。。。。”);
logger.warn(“这是warn日志。。。。”);
logger.error(“这是error日志。。。。”);
可以调整输出的日志级别:日志只会在这个级别及以后的高级别生效。
springboot默认使用info级别;在主配置文件里写:
logging.level.包名/类名/方法=trace //设定为trace级别
logging.level.TTST=trace
logging.file与logging.path的区别:
//听说后来变为logging.file.name了。
//可以给file设置日志名(在根目录下生成)
logging.file=hh.log
//指定file的路径与文件名称
logging.file=G:/mimimi.txt
//设定loggingpath是设置目录,生成的日志名默认为spring.log;
logging.path=G:/
logging.path=/hxh/errorlog
logging.file | logging.path | Example | Description |
---|---|---|---|
NULL | 指定目录 | /var/log | 输出到指定目录的spring.log文件中 |
指定文件名 | NULL | my.log | 输出日志到my.log文件中 |
NULL | NULL | 只在控制台输出 |
二者一同设置的时候只有logging.file起作用。 |
设置logging控制台与文件输出的日志格式:
上下两种一样:%-5p与%-5level日志级别左对齐 %msg与%m为消息
#控制台输出的日志格式
logging.pattern.console=%d{yyyy-MM-dd} === ${PID:-} === [%thread] === %-5p === %logger{50} === %m%n
#指定文件中输出的日志格式
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} === ${PID:-} === [%thread] === %-5plevel === %logger{50} === %msg%n
%logger为source class name,通常缩写。
2021-03-12 === 9576 === [main] === INFO === TTST === Started TTST in 4.151 seconds (JVM running for 5.94)
2021-03-12 === 9576 === [main] === TRACE === TTST === 这是trace日志。。。。
2021-03-12 === 9576 === [main] === DEBUG === TTST === 这是debug日志。。。。
2021-03-12 === 9576 === [main] === INFO === TTST === 这是info日志。。。。
2021-03-12 === 9576 === [main] === WARN === TTST === 这是warn日志。。。。
2021-03-12 === 9576 === [main] === ERROR === TTST === 这是error日志。。。。
2021-03-12 === 9576 === [Thread-2] === INFO === o.s.w.context.support.GenericWebApplicationContext === Closing org.springframe
springboot对日志的默认配置在哪?
springboot包下面的logging–logback–base.xml与defaults.xml
里面有对级别以及彩色日志,默认格式的设置;如何开启彩色日志?
1,在全局配置文件中加上:
spring.output.ansi.enabled=always
2,自定义日志的时候设置颜色,可参考默认颜色:
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS}===%magenta(${PID})===%boldYellow([%thread])===%highlight(%-5p)===%cyan(%logger{50}) %msg%n"
指定配置:
给类路径下放上每个日志框架自己的配置文件即可;springboot就不使用自己默认的了。
Logging System | Customization |
---|---|
Log4j2 | log4j2-spring.xml,log4j2.xml |
Logback |
logback-spring.xml,logback-spring.groovy logback.xml,logback.groovy |
JUL | logging.properties |
推荐使用-spring扩展名的,因为这样可以使用标签,允许切换日志对应环境。
<!-- 开发环境 -->
<springProfile name="dev">
<!-- 控制台日志输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
</springProfile>
如何切换日志框架:
比如将logback切换为log4j,首先将logback-classic排除掉(自动排除logback-core),再把偷天换日包log4j-over-slf4j排除掉,再添加进去日志适配器:slf4j-log4j12(自动导入了log4j)。
比如切换log4j2,排除掉spring-boot-starter-logging,替换为spring-boot-starter-log4j2。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>