了解Spring Boot2自动配置原理
1、SpringBoot特点
1.1、依赖管理
1.父项目做依赖管理
依赖管理 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> </parent> 他的父项目 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.4.RELEASE</version> </parent> 里面几乎声明了所有开发中常用的依赖的版本号,所以有自动版本仲裁机制
2.开发导入starter场景启动器
1、见到很多 *spring-boot-starter- **: * 就某种场景
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
4、见到的 *-spring-boot-starter : 第三方为我们提供的简化开发的场景启动器。
5、所有场景启动器最底层的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.3.4.RELEASE</version> <scope>compile</scope> </dependency>
3.无需关注版本号,自动版本仲裁
1、引入依赖默认都可以不写版本.Maven会根据就近原则,如果自己修改了版本号就使用修改后的,如果没有则使用父亲默认的.
2、引入非版本仲裁的jar,要写版本号。
4.可以修改默认版本号
1、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
2、在当前pom.xml项目里面重写配置
<properties> <mysql.version>5.1.43</mysql.version> </properties>
1.2、自动配置
1.自动配好Tomcat
- 引入Tomcat依赖。
- 配置Tomcat
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <version>2.3.4.RELEASE</version> <scope>compile</scope> </dependency>
2.自动配好SpringMVC
- 引入SpringMVC全套组件
- 自动配好SpringMVC常用组件(功能)
3.自动配好Web常见功能
- SpringBoot帮我们配置好了所有web开发的常见场景,如字符编码,dispatcherServlet…
4.默认的包结构
主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来,这样就无需之前的包扫描配置
想要改变扫描路径,可以 @SpringBootApplication(scanBasePackages=“com.atguigu”) 或者@ComponentScan 指定扫描路径(后期再讲如何做)
@SpringBootApplication 等同于 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan("com.rg.boot")
5.各种配置拥有默认值
默认配置最终都是映射到某个类的属性上,如:spring.servlet.multipart.max-file-size=10MB ,将对应MultipartProperties中的maxFileSize属性.
配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
6.按需加载所有自动配置项
-springboot非常多的starter, 引入了哪些场景这个场景的自动配置才会开启 ,如在没有使用批处理时,批处理里面有的类是报错的,引入之后才不会报错.
SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里.
…
2、容器功能
2.1、组件添加
1、@Configuration
1.配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
2.配置类本身也是组件
#############################实体类############################## public class User { private String name; private Integer age; 无参,有参,构造,toString... } public class Pet { private String name; 无参,有参,构造,toString... } #############################配置类############################## /** * * 2.配置类本身也是组件 * */ @Configuration //告诉SpringBoot这是一个配置类 == 配置文件 public class MyConfig { @Bean//给容器中添加组件.以方法名作为组件的id.返回类型就是组件类型.返回的值,就是组件在容器中的实例. public User user01(){ return new User("zhangsan", 18); } @Bean("tom") public Pet tomcatPet(){ return new Pet("tomcat"); } } #############################测试类############################## public static void main(String[] args) { //1.返回我们的IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); //2.查看容器里面的组件 String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } //3.从容器中获取组件 Pet tom01 = run.getBean("tom", Pet.class); Pet tom02 = run.getBean("tom", Pet.class); System.out.println(tom01==tom02); //4.MyConfig本身也是一个组件 MyConfig bean = run.getBean(MyConfig.class); System.out.println(bean); }
3. proxyBeanMethods :代理bean的方法
@Configuration(proxyBeanMethods=true) 时:调用MyConfig里面的类的注册方法,将在IOC容器中注册该类的代理对象.之后无论调用多少次注册方法,获取到的都是之前注册在容器中的单例对象.
MyConfig bean = run.getBean(MyConfig.class); System.out.println(bean);//com.rg.boot.config.MyConfig$$EnhancerBySpringCGLIB$$bc5b911c@22d6cac2 获取到的MyConfig就是一个CGlib类型的代理对象. //代理对象调方法,springboot总会检查这个组件是否在容器中,如果没有就创建;如果有则直接拿来用.保持组建的单例性. User user = bean.user01(); User user1 = bean.user01(); System.out.println(user==user1);//运行结果为true;
@Configuration(proxyBeanMethods=false) ,获取到的是普通对象每次调用都要进行创建.上述运行结果为false.
4.Full模式(全配置)与Lite(轻量级)模式
Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
可以利用Full模式解决组件依赖的问题.
举例:用户要依赖Pet的组件
//domain类 public class User { private String name; private Integer age; private Pet pet; ... } //配置类 @Configuration(proxyBeanMethods = true) //告诉SpringBoot这是一个配置类 == 配置文件 public class MyConfig { @Bean//给容器中添加组件.以方法名作为组件的id.返回类型就是组件类型.返回的值,就是组件在容器中的实例. public User user01(){ User user = new User("zhangsan", 18); //user组件依赖了pet组件 user.setPet(tomcatPet()); return user; } @Bean("tom") public Pet tomcatPet(){ return new Pet("tomcat"); } } //测试方法 public static void main(String[] args) { //1.返回我们的IOC容器 ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); User user01 = run.getBean("user01", User.class); Pet tom = run.getBean("tom", Pet.class); System.out.println(user01.getPet()==tom);//运行结果为true. }
总结:
配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
2、@Bean、@Component、@Controller、@Service、@Repository
@Bean常用在方法上,其他四个常用在自定义类上.
注:注解注册组件调用的是无参构造方法,如果没有则将报错.
3、@ComponentScan、@Import
@ComponentScan:进行组件扫描.
@Import:进行组件导入.
* @Import({User.class, DBHelper.class}) * 给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名 */ @Import({User.class, DBHelper.class}) @Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件 public class MyConfig { }
@Import 高级用法: https://www.bilibili.com/video/BV1gW411W7wy?p=8
4、@Conditional
条件装配:满足Conditional指定的条件,则进行组件注入
5.@ImportResource
@ImportResource(“classpath:xxx.xml”):导入并执行SpringBoot的配置文件
======================ApplicationContext.xml========================= <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="pet" class="com.rg.boot.bean.Pet"> <property name="name" value="tomcat"/> </bean> <bean id="user" class="com.rg.boot.bean.User"> <property name="name" value="zhangsan"/> <property name="age" value="18"/> </bean> </beans> @ImportResource("classpath:ApplicationContext.xml") public class MyConfig {}
2.2、配置绑定
传统方式:使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;
public class getProperties { public static void main(String[] args) throws FileNotFoundException, IOException { Properties pps = new Properties(); pps.load(new FileInputStream("a.properties")); Enumeration enum1 = pps.propertyNames();//得到配置文件的名字 while(enum1.hasMoreElements()) { String strKey = (String) enum1.nextElement(); String strValue = pps.getProperty(strKey); System.out.println(strKey + "=" + strValue); //封装到JavaBean。 } } }