SpringBoot
1. springboot多环境配置
1.1 properties文件
spring.profiles.active=dev【激活dev文件】
创建三个文件,然后在默认文件application中激活某个文件
-
application.properties 默认
-
application-dev.properties 开发环境
-
application-test.properties 测试环境
1.2 yaml文件
yaml可以直接给实体类赋值
@Value 可以赋值
server:
port: 8081
spring:
profiles:
active: dev(激活dev)
–(三条横线)【分割线将文件分割成多个文件】
server:
port: 8082
spring:
profiles: dev(dev)
–
server:
port:8083
spring:
profiles:test
# 对空格的要求十分高!
# 普通的key-value
name: qinjiang
# 对象
student:
name: qinjiang
age: 3
# 行内写法
student: {name: qinjiang,age: 3}
#数组
pets:
- cat
- dog
- pig
pets: [cat,dog,pig]
1.3 配置文件内容
在我们配置文件中能配置的东西,都存在一个固有的规律。
2.springboot自动装配原理
- springboot启动会加载大量的类。
- 我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中。
- 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在其中,我们就不需要再手动配置了)
- 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性值。我们只需要在配置文件中指定这些属性的值即可;
xxxxAutoConfiguration:自动配置类;给容器中添加组件
xxxxProperties:封装配置文件的相关属性
ApplicationContext:容器,里面可以找到所有的bean
2.1 原理初探
自动配置
自动配置原理
1)、springboot启动的时候加载主配置类,开启了自动配置动能@EnableAutoConfiguration
2)、@EnableAutoConfiguration作用:
-
利用EnableAutoConfigurationImportSelector给容器中导入一些组件?
-
可以插件selectimports()方法的内容:
-
List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置
-
SpringFactoriesLoader.loadFactoryNames() 扫描所有jar包类路径下 META-INFO/spring.factories 把扫描到的这些文件的内容包装成properties对象 从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中
将类路径下 META-INFO/spring.factories里面配置的所有EnableAutoConfiguration的值加入到了容器中;
每一个这样的xxxAutoConfiguration类都是容器中的一个组件,都加入到了容器-中;用他们来做自动配置;
-
3)、每一个自动配置类进行自动配置功能
4)、以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;
@Configuration //表示这是一个配置类,与以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncondingProperties绑定起来;
@ConditionalOnWebApplication //spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否web应用,如果是,当前配置类生效
@ConditionOnClass //判断当前项目有没有这个类CharacterEncodingFilter:SpringMVC中进行乱码解决的过滤器;
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) //判断配置文件中是否存在某个配置 spring.http.encoding.enabled:如果不存在,判断也是成立的
//即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的
public class HttpEncodingAutoConfiguration{
@Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
根据当前不容的条件判断,决定这个类是否生效?
一旦这个配置类生效:这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的。
5)、所有配置文件中能配置的属性都是在xxxProperties类中封装着;配置文件能配置什么就可以参照某个功能对应的这个属性类
精髓:
1)、SpringBoot启动会加载大量的自动配置类
2)、我们看我们需要的功能有没有SpringBoot默认写好的自动配置类;
3)、我们再来看这个自动配置类中到底配置了哪些组件;(只要我们组件有,我们就不需要再来配置了)
4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,我们就可以在配置文件中指定这些属性的值。
xxxAutoConfiguration:自动配置类
给容器中添加组件
xxxProperties:封装配置文件中的相关属性
1、@Conditional派生注解(@Conditional作用)
作用必须是@Conditional指定的条件成立,才给容器中添加组件,配置里面的所有内容才生效;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oNTf4fY0-1605000337773)(images\15953136071202.png)]
自动配置类必须在一定条件下才能生效;
通过在application.properties中配置debug=true属性,来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:(自动配置类启用的)
-----------------
AopAutoConfiguration matched:
- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
AopAutoConfiguration.ClassProxyingConfiguration matched:
- @ConditionalOnMissingClass did not find unwanted class'org.aspectj.weaver.Advice' (OnClassCondition)
- @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)
DataSourceAutoConfiguration matched:
- @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType' (OnClassCondition)
- @ConditionalOnMissingBean (types: io.r2dbc.spi.ConnectionFactory; SearchStrategy: all) did not find any beans (OnBeanCondition)
Negative matches:(没有启动,没有配置成功的自动配置类)
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
AopAutoConfiguration.AspectJAutoProxyingConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.aspectj.weaver.Advice' (OnClassCondition)
pom.xml
spring-boot-dependencies:核心依赖在父工程中!
我们在写或者引入一些springboot依赖的时候,不需要指定版本号,就是因为有这些版本仓库
spring-boot-stater:场景启动器
启动器
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
-
启动器:说白了就是springboot的启动场景
-
比如spring-boot-starter-web,他就会帮我们自动导入web环境所有的依赖!
-
springboot会将所有的功能场景,都变成一个个启动器
-
我们要使用什么功能,就只需要找到对应的启动器就可以了
starter
。
主程序
-
@SpringBootApplication public class HelloworldApplication { public static void main(String[] args) { //将springboot应用启动 SpringApplication.run(HelloworldApplication.class, args); } }
-
注解
-
@SpringBootConfiguration: springboot配置 @Configuration: spring配置类 @Component: 说明这也是spring的一个组件 @EnableAutoConfiguration:自动配置 @AutoConfigurationPackage:自动配置包 @Import({Registrar.class}):spring的底层注解,给容器中导入一个组件 @Import({AutoConfigurationImportSelector.class}):自动配置导入选择 //获取所有的配置 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
获取候选的配置
META-INF/spring.factories:自动配置的核心文件
结论:springboot所有自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效。
- springboot在启动的时候,从类路径下/META_INF/spring.factories获取指定的值!
- 将这些自动配置的类导入容器,自动配置类就会生效,帮我们进行自动配置!
- 以前我们需要自动配置的东西,现在springboot帮我们做了!
- 整合JavaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.2.0.RELEASE.jar
- 它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器。
- 容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,@JavaConfig!
- 有了自动配置类,免去了我们手动编写配置文件的工作!
SpringApplication
这个类主要做了以下四件事情:
- 推断应用的类型是普通的项目还是web项目
- 查找并加载所有可用初始化器,设置到initializers属性中
- 找出所有的应用程序监听器,设置到listeners属性中
- 推断并设置main方法的定义类,找到运行的主类
-
2.2 配置文件加载位置
springboot启动会扫描以下位置的application.properties或者application.yml文件作为SpringBoot默认配置文件。
优先级:
- 项目路径下的config文件夹配置文件
- 项目路径下配置文件
- 资源路径(resource)下的config文件夹配置文件
- 资源路径(resource)下配置文件
优先级从高到低,高优先级的配置会覆盖低优先级的配置;
SpringBoot会从这四个位置加载主配置文件,互补配置【高优先级配置的用高优先级的,高优先级没有配置的,用低优先级的】;
我们还可以用spring.config.location来改变默认文件的位置
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;【运维的时候用的比较多】
2.3 外部配置加载顺序
SpringBoot也可以从以下位置加载配置;优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置
-
命令行参数 java -jar xxxx.jar --server.port=8082
-
来自java:comp/env的NDI属性
-
java系统属性(System.getProperties())
-
操作系统环境变量
-
RandomValuePropertySource配置的random.*属性值
由jar包外向jar包内进行寻找
优先加载带profile
-
jar包外部的application-{profile}.properties或application.yml(带spring-profile)配置文件
-
jar包内部的application-{profile}.properties或application.yml(带spring-profile)配置文件
再来加载不带profile
-
jar包外部的application.properties或application.yml(不带spring-profile)配置文件
-
jar包内部的application.properties或application.yml(不带spring-profile)配置文件
-
@configuration注解类上的@PropertySource
-
通过SpringApplication.setDefaultProperties指定的默认属性
3.springboot web开发
使用SpringBoot:
1)、创建SpringBoot应用,选中我们需要的模块
2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来
3)、自己编写业务代码
自动装配
springboot到底帮我们配置了什么?我们能不能进行修改?能修改哪些东西?能不能扩展
- xxxxAutoConfiguration:向容器中自动配置文件
- xxxxproperties:自动配置类,装配配置文件中自定义的一些内容。
要解决的问题:
- 导入静态资源…
- 首页
- jsp,模板引擎
- 装配扩展springMVC
- 增删改查
- 拦截器
- 国际化
总结:
- 在springboot,我们可以使用以下方式处理静态资源
- webjars --> localhost:8080/webjars/
- public,static,/**,resources -->localhost:8080/
- 优先级:resources>static>public
4. SpringBoot常用注解
4.1 @SpringBootApplication注解
1.@SpringBootApplication:包含了@ComponentScan、@SpringBootConfiguration和@EnableAutoConfiguration。一般@SpringBootApplication注解放在项目的启动类上,用来把启动类注入到容器中,用来定义容器的扫描范围,用来加载classpath环境一些bean。【@SpringBootApplication同时拥有三种注解的功能】
1)@SpringBootConfiguration主要用来把bean注入到容器中,因为该注解又封装了@Configuration注解。@Configuration注解又封装了@Component注解,@Component注解主要用来把一个bean注入到容器中。因此,@SpringBootConfiguration注解就拥有了@Component注解的功能,用来把一个bean注入到容器中。
2)@EnableAutoConfiguration这个注释告诉SpringBoot“猜”你将如何配置Spring,基于你已经添加jar依赖项。如果spring-boot-starter-web已经添加Tomcat和SpringMVC,这个注解自动将假设您在开发一个web应用程序并添加相应的spring设置。自动设置被设计用来和“Starters”一起更好的工作,但是这两个概念并不直接相关。您可以自由挑选starter依赖意外的jar包,springboot仍将尽力自动配置您的应用程序。
3)@ComponentScan注解主要用来指定扫描容器的范围。
4)@SpringBootApplication的用法
package net.evecom.beans;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Configuration;
/*@SpringBootConfiguration注解包装了@Configuration注解,而@Configuration注解又包装了@Component注解,所以此地用三者注解中的任何一个都可以*/
@SpringBootConfiguration
//@Configuration
public class Person{
}
在启动类中获取Person类对应的bean
package net.evecom.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import net.evecom.beans.Person;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
SpringApplication app = new SpringApplication(App.class);
ConfigurableApplicationContext context = app.run(args);
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}
运行启动类,会报一个找不到person bean的异常。因为@SpringBootApplication注解默认只扫描该注解注解类所在的包,即默认只扫描net.evecom.demo包下的bean,但是person bean对应的类在net.evecom.beans下,所以在启动类中获取不到person。
用@SpringbootApplication注解中的scanBasePackages属性显示指定要扫描的包的范围。
package net.evecom.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import net.evecom.beans.Person;
/*指定扫描net.evecom包下的所有bean*/
@SpringBootApplication(scanBasePackages="net.evecom")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
SpringApplication app = new SpringApplication(App.class);
ConfigurableApplicationContext context = app.run(args);
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}
运行启动类,会获取到person的bean
可以用@SpringBootApplication注解中的scanBasePackageClasses属性指定要扫描的类。
package net.evecom.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import net.evecom.beans.Person;
/*显示指定只扫描Person类,程序中只能加载Person类对应的bean*/
@SpringBootApplication(scanBasePackageClasses=Person.class)
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
SpringApplication app = new SpringApplication(App.class);
ConfigurableApplicationContext context = app.run(args);
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}
运行启动类,会获取到person的bean
可以用@SpringBootApplication注解中的exclude属性显示指定要排除的类。再建一个Animal类。
package net.evecom.beans;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Configuration;
@SpringBootConfiguration
public class Animal{
}
在启动类中获取bean
package net.evecom.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import net.evecom.beans.Animal;
import net.evecom.beans.Person;
/*exclude排除掉了扫描的类*/
@SpringBootApplication(scanBasePackages="net.evecom", exclude=Animal.class)
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
SpringApplication app = new SpringApplication(App.class);
ConfigurableApplicationContext context = app.run(args);
/*可以获取到person的bean*/
Person person = (Person) context.getBean("person");
System.out.println(person);
/*获取不到Animal类型的bean*/
Animal animal = context.getBean(Animal.class);
System.out.println(animal);
}
}
运行启动类,只能获取到person的bean,不能获取关于Animal类型的bean。
可以使用@SpringBootApplication注解中的excludeName属性显示指定要排除的类的名字。
package net.evecom.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import net.evecom.beans.Animal;
import net.evecom.beans.Person;
@SpringBootApplication(scanBasePackages="net.evecom", excludeName="net.evecom.beans.Animal")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
SpringApplication app = new SpringApplication(App.class);
ConfigurableApplicationContext context = app.run(args);
/*可以获取perosn的bean*/
Person person = (Person) context.getBean("person");
System.out.println(person);
/*获取不到Animal类型的bean*/
Animal animal = context.getBean(Animal.class);
System.out.println(animal);
}
}
运行启动类,可以获取到person的bean,但是获取不到Animal类型的bean。
4.2 @EnableDiscoveryClient注解和@EnableEurekaClient注解
SpringCloud中的“Discovery Service”有多种实现,比如:Eureka,consul,zookeeper。
- @EnableDiscoverClient注解是基于spring-cloud-commons依赖,并且在classpath中实现;
- @EnableEurekaClient注解是基于spring-cloud-netflix依赖,只能为eureka作用;
- @EnableDiscoveryClient和@EnableEurekaClient共同点就是:都是能够让注册中心能够发现,扫描到该服务。
- @EnableEurekaClient只适用于Eureka作为注册中心,@EnableDiscoveryClient可以是其他注册中心。
4.3 @MapperScan和@Mapper的使用
-
使用@Mapper注解
为了让DemoMapper类能够让别的类进行引用,我们可以在DemoMapper类上添加@Mapper注解:
@Mapper public interface DemoMapper{ @Insert("insert into Demo(name) values(#{name})") @Options(keyProperty="id", keyColumn="id",useGeneratedKeys=true) public void save(Demo demo); }
直接在Mapper类上面添加注解@Mapper,但是这种方式要求每一个mapper类都需要添加此注解,非常麻烦
-
使用@MapperScan注解
通过使用@MapperScan可以指定要扫描的Mapper类的包路径,比如:
@MapperScan("net.evecom.*.mapper") public class App{ public static void main(String[] args){ SpringApplication.run(App.class,args); } }
或者:
@SpringBootApplication @MapperScan("net.evecom.mapper") public class App{ public static void main(String[] args){ SpringApplication.run(App.class,args); } }
-
使用@MapperScan注解多个包
可以根据包的结构指定不同的表达式。
使用@MapperScan注解多个包,可以使用如下的方式指定多个包:
@SpringBootApplication @MapperScan({"net.evecom.mapper","net.evecom.demo"}) public class App{ public static void main(String[] args){ SpringApplication.run(App.class,args); } }
4.4 @EnableFeignClients客户端详细
在Spring Cloud应用中,当我们要使用feign客户端时,一般要做以下三件事情。
-
使用注解@EnableFeignClients启用feign客户端;
@SpringBootApplication /*@EnableFeignClients默认扫描并创建所在工程下的包。如果在SpringBoot分布式中需要用到别的微服务的工程的接口实例,那么就要写成如下形式:【此注解并不管理bean的依赖关系】 @EnableFeignClients( basePackages = { "com.wisdombud.dth.boss.customer","com.wisdombud.dth.boss.his""com.wisdombud.dth.boss.product.srv"}) */ @EnableFeignClients public class TestApplication{ public static void main(String[] args){ SpringApplication.run(TestApplication.class,args); } }
-
使用注解@FeignClient定义feign客户端;
该例子定义了一个feign客户端,将远程服务http://test-service/test/echo映射为一个本地java方法调用。
@FeignClient(name = "test-service", path = "/test") public interface TestService { @RequestMapping(value = "/echo", method = RequestMethod.GET) TestModel echo(@RequestParam("parameter") String parameter); }
-
使用注解@Autowired使用上面所定义的feign的客户端
@Autowired TestService testService; public void run() { // 这里的使用本地Java API的方式调用远程的Restful接口 TestModel dto = testService.echo("Hello,你好!"); log.info("echo : {}", dto); }
上面三个步骤,前面两个步骤可以理解为定义feign客户端,第三步是使用所定义的feign客户端。通过调试发现,上面第三步所注入的testService是一个代理对象
- 注解@EnableFeignClients:扫描和注册feign客户端bean定义
- 注解@EnableFeignClients用于告诉框架扫描所有通过注解@FeignClient定义的feign客户端.
4.5 @EnableScheduling注解
要实现计划任务,首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是一个计划任务。
-
@EnableScheduling注解开启对计划任务的支持
-
@Scheduled注解
package cn.hncu.p3.p3_taskscheduler; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.text.SimpleDateFormat; import java.util.Date; /** * Explain:计划任务执行类 */ @Service public class ScheduledTaskService { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); @Scheduled(fixedRate = 5000) //通过@Scheduled声明该方法是计划任务,使用fixedRate属性每隔固定时间执行 public void reportCurrentTime(){ System.out.println("每隔5秒执行一次" + dateFormat.format(new Date())); } @Scheduled(cron = "0 07 20 ? * *" ) //使用cron属性可按照指定时间执行,本例指的是每天20点07分执行; //cron是UNIX和类UNIX(Linux)系统下的定时任务 public void fixTimeExecution(){ System.out.println("在指定时间 "+dateFormat.format(new Date())+" 执行"); } }
4.6 注解(annotations)列表
- @Controller:用于定义控制器类,在spring项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层),一般这个注解在类中,通常方法需要配合注解@RequestMapping。
- @RestController:用于标注控制层组件(如struts中的action),@ResponseBody和@Controller的合集。
- @RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射。
- @EnableAutoConfiguration:SpringBoot自动配置(auto-configuration):尝试根据你添加的jar依赖自动配置你的Spring应用。例如,如果你的classpath下存在HSQLDB,并且你没有手动配置任何数据库连接beans,那么我们将自动配置一个内存型(in-memory)数据库”。你可以将@EnableAutoConfiguration或者@SpringBootApplication注解添加到一个@Configuration类上来选择自动配置。如果发现应用了你不想要的特定自动配置类,你可以使用@EnableAutoConfiguration注解的排除属性来禁用它们。
- @ComponentScan:其实很简单,@ComponentScan主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中,你一定都有用过@Controller,@Service,@Repository注解,查看其源码你会发现,他们中有一个共同的注解@Component,没错@ComponentScan注解默认就会装配标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中。当然,这个的前提就是你需要在所扫描包下的类上引入注解。
- @Configuration:相当于传统的xml配置文件,如果有些第三方库需要用到xml文件,建议仍然通过@Configuration类作为项目的配置主类——可以使用@ImportResource注解加载xml配置文件。
- @Import:用来导入其他配置类。
- @ImportResource:用来加载xml配置文件。
- @Autowired:自动导入依赖的bean
- @Service:一般用于修饰service层的组件
- @Repository:使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要为它们提供XML配置项。
- @Bean:用@Bean标注方法等价于XML中配置的bean。
- @Value:注入Spring boot application.properties配置的属性的值。
- @Inject:等价于默认的@Autowired,只是没有required属性;
- @Component:泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
- @Bean:相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
- @AutoWired:自动导入依赖的bean。byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。当加上(required=false)时,就算找不到bean也不报错。
- @Qualifier:当有多个同一类型的Bean时,可以用@Qualifier(“name”)来指定。与@Autowired配合使用。@Qualifier限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者。
- @Resource(name=”name”,type=”type”):没有括号内内容的话,默认byName。与@Autowired干类似的事。
5、日志
5.1、日志框架
市面上的日志框架:JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j…
日志门面(日志的抽象层) | 日志实现 |
---|---|
JCL(Jakarta Commons Logging) 、SLF4j(Simple Logging Facade for java) jboss-logging | Log4j、JUL(java.util.logging)、Log4j2、Logback |
SLF4j(Simple Logging Facade for java)、Log4j、Logback出自同一人之手
左边选一个门面(抽象层)、右边来选一个实现:
日志门面:SLF4J;
日志实现:Logback;
SpringBoot选用SLF4j和Logback
5.2 SLF4J使用
1、如何在系统中使用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");
}
}
图示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5kAaZS92-1605000337779)(images\concrete-bindings.png)]
每一个日志的实现框架都有自己的配置文件,使用slf4j以后,配置文件还是用日志实现框架本身的配置文件。
2、遗留问题
统一日志记录,即使是别的框架也和我一起使用slf4j进行输出。
图示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tlGcqRDl-1605000337791)(images\legacy.png)]
如何让系统中所有的日志都统一到slf4j:
1、将系统中其他日志框架先排除出去;
2、用中间包来替换原有的日志框架;
3、我们导入slf4j其他的实现
5.3 SpringBoot日志关系
SpringBoot使用它来做日志功能:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
底层依赖关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oNnAzj5y-1605000337806)(images/企业微信截图_15953192527105.png)]
总结;
1)、SpringBoot底层也是使用slf4j+logback方式进行日志记录;
2)、SpringBoot也把其他的日志都替换成了slf4j;
3)、中间替换包
4)、如果我们要引入其他框架?一定要把这个框架的默认日志依赖移除掉。
SpringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式巨鹿日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉
5.4 日志使用
1、默认配置
SpringBoot默认帮我们配置好了日志:
//记录器
Logger logger = LoggerFactory.getLogger(getClass());
@Test
void hcontextLoads() {
//日志的级别,由低到高
/*
* 由低到高 trance<debug<info<warn<error
* 可以调整输出的日志级别,日志只会在这个级别以后的高级别生效
* */
logger.trace("这是跟踪,trace日志");
logger.debug("这是debug日志");
/*
* SpringBoot默认给我们使用的是info级别的,没有指定就用
* SpringBoot默认规定的级别:root级别
* */
logger.info("这是info日志");
logger.warn("这是警告,warn日志");
logger.error("这是错误日志");
}
2、指定配置
给类路径下放上每个日志框架自己的配置文件即可;SpringBoot就不使用它的默认配置了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K1ItTRKv-1605000337809)(images/企业微信截图_1595322554721.png)]
logback.xml:直接被日志框架识别了;
logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能。
<springProfile name="staging">
<!-- configuration to be enabled when the "staging" profile is active -->
可以指定某段配置只在某个环境下生效
</springProfile>
否则就会报错,找不到[springProfile].
5.5 切换日志框架
可以按照slf4j的日志适配图,进行相关切换;
6、错误处理机制
1)、SpringBoot默认的处理机制
默认效果:
1]、返回一个默认的错误页面
2]、如果是其他客户端,默认相应一个json数据
原理:
可以参照ErrorMvcAutoConfiguration:错误处理的自动配置
给容器中添加了以下组件
1、DefaultErrorAttributes
帮我们在页面共享attributes值
2、BasicErrorController:处理默认/error请求
public ModelAndView errorHtml(){} //产生html类型的数据;浏览器发送的请求来到这个方法处理
public ResponseEntity<Map<String,Object>> error(){} // 产生json数据;其他客户端来到这个方法处理
3、ErrorPageCustomizer
4、DefaultErrorViewResolver
步骤:
一旦系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则),就会来到/error请求,就会被BasicErrorController处理。
1)、响应页面;去哪个页面是由DefaultErrorViewResolver解析得到的
2)、如果定制错误的页面:
1]、如何定制错误的页面
1> 、有模板引擎的情况下:error/状态码:【将错误页面命名为错误状态码html放在模板引擎下的文件夹下的error文件中】
我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确状态码.html)
页面能获取的信息:
timestamp:时间戳
status:状态码
error:错误提示
exception:异常对象
message:异常消息
errors:jsr303数据校验的错误都在这里
2>、没有模板引擎(模板引擎找不到这个错误的页面)的情况下,静态资源文件夹下找;
3>、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面
2]、如何定制错误的json数据
1>、自定义异常处理&返回固定列json数据
2>、转发到/error进行自适应响应效果处理
3>、将我们的定制数据携带出去
出现错误以后,回来到/error请求,会被BaseErrorController处理,响应出去可以获取的数据是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法);
1、完全编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中。
2、页面上能用的数据或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes();默认进行数据局出炉
7、配置嵌入式Servlet容器
SpringBoot默认使用Tomcat作为嵌入式的Servlet容器
问题?
1)、如何定制和修改Servlet容器相关的配置
1>、修改和server有关的配置
# 通用的servlet配置
serve.xxx
# tomcat的配置
server.tomcat.xxx
2>、注册servlet三大组件[Servlet、Filter、Listener]
SpringBoot帮我们配置SpringMVC的时候,自动注册了SpringMVC的前端控制器DispatcherServlet
2)、Springboot能不能支持其他的serverlet容器
支持jetty、Undertow
步骤:
1)、SpringBoot根据导入的依赖的情况,给容器中添加相应的EmbeddedServletContainerFactory【TomcatEmbeddedServletContainerFactory】
2)、容器中某个组件要创建对象就会惊动后置处理器;EmbeddedServletContainerCustomizerBeanPostProcessor;
只要是嵌入式的Servlet容器工厂,后置处理器就开始工作;
3)、后置处理器,从容器中获取所有的EmbeddedServletContainerCustomizer,调用定制器的定制方法