SpringBoot原理
1.pom文件
springboot-boot-starter-xxx:就是spring-boot的场景启动器
spring-boot-starter-web:帮我们导入了web模块正常运行所依赖的组件;
SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;
2.主启动类
@SpringBootApplication //来标注一个主程序类,说明这是一个Spring Boot应用
2.1@SpringBootApplication
作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
进入这个注解:可以看到上面还有很多其他注解!
@ComponentScan
这个注解在Spring中很重要 ,它对应XML配置中的元素。
作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
- 默认的包结构
- 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
- 无需以前的包扫描配置
- 想要改变扫描路径
- @SpringBootApplication(scanBasePackages=“com.lun”)
- @ComponentScan 指定扫描路径
2.2底层注解-@Configuration详解
- 基本使用
- Full模式与Lite模式
- 示例
这里的 @Configuration,说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件;
里面的 @Component 这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用!
-
最佳实战
-
配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
-
配置 类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式(默认)
-
/**
* 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
* 2、配置类本身也是组件
* 3、proxyBeanMethods:代理bean的方法
* Full(proxyBeanMethods = true)(保证每个@Bean方法被调用多少次返回的组件都是单实例的)(默认)
* Lite(proxyBeanMethods = false)(每个@Bean方法被调用多少次返回的组件都是新创建的)
*/
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
/**
* Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
* @return
*/
@Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
public User user01(){
User zhangsan = new User("zhangsan", 18);
//user组件依赖了Pet组件
zhangsan.setPet(tomcatPet());
return zhangsan;
}
@Bean("tom")
public Pet tomcatPet(){
return new Pet("tomcat");
}
}
@Configuration测试代码如下:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.atguigu.boot")
public class MainApplication {
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、com.atguigu.boot.config.MyConfig$$EnhancerBySpringCGLIB$$51f1e1ca@1654a892
MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
//如果@Configuration(proxyBeanMethods = true)代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。
//保持组件单实例
User user = bean.user01();
User user1 = bean.user01();
System.out.println(user == user1);
User user01 = run.getBean("user01", User.class);
Pet tom = run.getBean("tom", Pet.class);
System.out.println("用户的宠物:"+(user01.getPet() == tom));
}
}
简而言之:proxyBeanMethods = true,直接扫描看看有没有相同的,有相同的就直接使用(保证每个@Bean方法被调用多少次返回的组件都是单实例的)(默认)
Lite(proxyBeanMethods = false)(每个@Bean方法被调用多少次返回的组件都是新创建的)
@EnableAutoConfiguration
@EnableAutoConfiguration :开启自动配置功能
以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;
@AutoConfigurationPackage :自动配置包
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
@import :Spring底层注解@import , 给容器中导入一个组件
Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;
底层注解-@ImportResource导入Spring配置文件
比如,公司使用bean.xml文件生成配置bean,然而你为了省事,想继续复用bean.xml,@ImportResource粉墨登场。
bean.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans ...">
<bean id="haha" class="com.lun.boot.bean.User">
<property name="name" value="zhangsan"></property>
<property name="age" value="18"></property>
</bean>
<bean id="hehe" class="com.lun.boot.bean.Pet">
<property name="name" value="tomcat"></property>
</bean>
</beans>
使用方法:添加路径即可
@ImportResource("classpath:beans.xml")
public class MyConfig {
...
}
2.3自动装配
(1)@Component+@ConfigurationProperties(prefix = “student”)配置绑定
在配置文件里面有:
student.name=lisi
student.age=20
实体类:
//只有在容器中的组件,才会拥有springboot提供的功能
@Component
@ConfigurationProperties(prefix = "student")
public class User {
private String name;
private int age;
}
测试: @Autowired 自动装配
@Autowired
User user;
@RequestMapping("/user")
public User user(){
return user;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CMs7P4uW-1658477618384)(C:\Users\xhd\AppData\Roaming\Typora\typora-user-images\image-20220722091417918.png)]
(2)@EnableConfigurationProperties + @ConfigurationProperties(prefix = “student”)自动装配
-
开启Car配置绑定功能
-
把这个Car这个组件自动注册到容器中
@EnableConfigurationProperties(Car.class) public class MyConfig { ... }
2.4@EnableAutoConfiguration
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() de fault {}; }
(1)@AutoConfigurationPackage
标签名直译为:自动配置包,指定了默认的包规则。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)//给容器中导入一个组件
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
- 利用Registrar给容器中导入一系列组件
- 将指定的一个包下的所有组件导入进MainApplication所在包下。
(2)@Import({AutoConfigurationImportSelector.class})
给容器导入组件 ;默认组件的名字就是全类名
AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?
发现一个多次出现的文件:spring.factories,全局搜索它 “META-INF/spring.factories”
```java
利用getAutoConfigurationEntry(annotationMetadata);给容器中批量导入一些组件
调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
利用工厂加载 Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
从META-INF/spring.factories位置来加载一个文件。
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories
```
底层注解-@Conditional条件装配
条件装配:满足Conditional指定的条件,则进行组件注入
spring.factories
我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q2wrHE2S-1658477618386)(C:\Users\xhd\AppData\Roaming\Typora\typora-user-images\image-20220721203609203.png)]
所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
结论
- SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
- 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
- 整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
- 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
- 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;
自动配置流程
SpringBoot默认会在底层配好所有的组件,但是如果用户自己配置了以用户的优先。
- SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行失效,默认绑定配置文件指定的值。(xxxxProperties里面读取,xxxxProperties和配置文件进行了绑定)
- 生效的配置类就会给容器中装配很多的组件
- 只要有这些组件,相当于这些功能就有了
- 定制化配置
- 用户直接自己@Bean替换组件
- 用户可以看这个组件获取的配置文件什么值去修改就可以了
xxxxxAutoConfiguration–>组件–>xxxxProperties里面读取值–>appliaction.properties
1.最佳实践-Lombok简化开发
Lombok用标签方式代替构造器、getter/setter、toString()等鸡肋代码。
spring boot已经管理Lombok。引入依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
@AllArgsConstructor//全参构造器
@NoArgsConstructor//无惨构造器
@Data//get set方法
@ToString//tostring方法
@Component
@ConfigurationProperties(prefix = "student")
public class User {
private String name;
private int age;
}
2.最佳实践-dev-tools
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
在IDEA中,项目或者页面修改以后:Ctrl+F9。
= “student”)
public class User {
private String name;
private int age;
}
## 2.最佳实践-dev-tools
```xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
在IDEA中,项目或者页面修改以后:Ctrl+F9。