SpringBoot学习(下)

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类(类名)对应的值,然后把他们加载到容器中。
SpringBoot学习(下)
每一个这样的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,
LogbackLog4j2)
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");
  }
}

SpringBoot学习(下)
每一个日志的实现框架都有自己的配置文件,使用slf4j之后,配置文件还是做成日志实现框架的配置文件。

遗留问题:
系统依赖:spring(commons-logging),hibernate(jboss-logging),mybatis,xxx
统一日志记录,即使识别的框架,统一使用slf4j进行输出?

SpringBoot学习(下)

如何让系统所有日志统一到slf4j?
1,先将其他日志框架排除
2,用中间包替换原有的日志框架
3,我们导入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

SpringBoot学习(下)
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>
上一篇:【Logback日志级别】动态调整Logback的日志级别


下一篇:移动距离|2015年蓝桥杯B组题解析第八题-fishers