1.了解Spring的bean如何装入IOC
1.XML方式
<!-- 手动配置bean对象 -->
<bean id="person" class="pojo.Person">
<property name="name" value="dzzhyk"/>
<property name="age" value="20"/>
<property name="sex" value="true"/>
</bean>
2.注解扫描
@ComponetScan->@Service @Component @Controller ...
3.配置类
@Comfiguration+@Bean
2.三种方式如何选型
1.xml方式
SpringBoot 的诞生就是为了减少繁杂的配置,所以第一种不符合
2.注解扫描
每次需要注入都是需要只知道包名才能进行扫描到IOC,假如多个依赖包名都不一样,开发人员记不住
3.配置类方式
@Configuration
public class MyConfig {
@Bean
public A a(){
return new A();
}
@Bean
public B b(){
return new B();
}
}
4.如何将配置类告诉ioc容器
约定 启动的时候去扫描classpath*:/META-INF/spring-factories
这个文件
5.如何动态的加载
第一种形式: @Import(A.class)
@Import(A.class) ,简单易懂的形式 ,哪里需要就在哪里定定义这个bean,直接Import他的class即可
如果多个的话就会爆炸,所以不推荐使用,少个确实可以
第二种形式:@Import(B.class)
传递了一个bean定义注册器,这个注册器的具体内容如下:
public class B implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition aDef = new RootBeanDefinition(A.class);
registry.registerBeanDefinition("a", aDef);
}
}
这个注册器实现了ImportBeanDefinitionRegistrar接口,并且重写了里面的registerBeanDefinitions方法
看他做了什么事:创建了一个新的bean定义,他的类型就是A,然后把这个bean定义注册到BeanDefinitionMap(还记得吧!)里面,key值我们可以人为设置,这里就设置成"a"
这样在传递一个注册器的时候,我们就可以把注册器中新增的bean定义注册进来使用
第三种形式:@Import(C.class)
可以看到,这种使用方式就是我们刚才的注解中使用的方式
他传递了一个叫C的类,这个类依然是我们自己定义的,具体内容如下:
public class C implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 导入配置类
return new String[]{"config.MyConfig"};
}
}
这个类实现了ImportSelector接口,并且重写了selectImports方法,返回一个字符串数组
我们可以看到,返回的字符串数组中是我们要导入类的全类名
这个Importer返回的类如果是组件bean对象,就会被加载进来使用;如果是一个配置类,就会加载这个配置类
第三种和第二种的区别是第三种可以一次性写很多类,而且比较简洁,只需要清楚类的全包名即可。而第二种方式需要自己清楚包类名,手动创建bean定义,然后手动加入BeanDefinitionMap。
然后我们找到config.MyConfig类,发现这个类竟然就是我们刚才写的JavaConfig版本的配置文件:
@Configuration
public class MyConfig {
@Bean
public A a(){
return new A();
}
@Bean
public B b(){
return new B();
}
}
可以看出加载这个配置类相当于A和B的两个定义
3.自动装配源码分析
1.点击启动类注解
@SpringBootApplication
可以看出这个是复合注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
2.点击@SpringBootConfiguration注解
可以看到SpringBootConfiguration底层也是@Configuration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
3.点击@EnableAutoConfiguration注解
可以看到@Import注解这就是如何实现动态加载的步骤
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
4.点击@Import(AutoConfigurationImportSelector.class)里面的类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
再点击DeferredImportSelector
public interface DeferredImportSelector extends ImportSelector {
可以看到AutoConfigurationImportSelector最终实现的是ImportSelector接口
5.找到selectImports方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
可以看到代码的返回值getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);由这个方法生成,点进去
查看源码技巧,从后面往前面看
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
可以看到源码返回值configurations
由这个方法生成getCandidateConfigurations(annotationMetadata, attributes);
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
在这个 List里打一个断点
6.查看查看启动器下的文件
7.查看启动的配置类
4自定义启动器
1.创建项目
当然springboot官方建议对于非官方的starter命名方式为
xxx-spring-boot-starter
项目名为:spring-boot-starter-hello
2.POM文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-boot-starter-hello</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.创建一个配置类加载配置文件
package com.xjggb.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "com")
public class HelloServiceProperties {
private String name;
private int age;
public HelloServiceProperties() {
}
public HelloServiceProperties(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "HelloServiceProperties{name = " + name + ", age = " + age + "}";
}
}
4.自动装配自动装配类
package com.xjggb.config;
import com.xjggb.entity.HelloUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@EnableConfigurationProperties(HelloServiceProperties.class)
@Configuration
@ConditionalOnClass(HelloUser.class)
@ConditionalOnProperty(prefix = "com", value = "enabled", matchIfMissing = true)
public class HelloServiceAutoConfiguration {
@Autowired
private HelloServiceProperties helloServiceProperties;
@Bean
@ConditionalOnMissingBean
public HelloUser helloUser(){
HelloUser helloUser = new HelloUser();
helloUser.setAge(helloServiceProperties.getAge());
helloUser.setName(helloServiceProperties.getName());
return helloUser;
}
}
@Configuration:表明此类是一个配置类,将变为一个bean被spring进行管理。
@EnableConfigurationProperties:启用属性配置,将读取HelloServiceProperties里面的属性。
@ConditionalOnClass:当类路径下面有HelloUser此类时,自动配置。
@ConditionalOnProperty:判断指定的属性是否具备指定的值。
@ConditionalOnMissingBean:当容器中没有指定bean时,创建此bean
5.在resources下创建META-INF文件夹
在创建spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xjggb.config.HelloServiceAutoConfiguration
6.点击mvaen命令
让这个项目形成jar包
7.新建一个SpringBoot项目
1.导入依赖
<dependency>
<groupId>org.example</groupId>
<artifactId>spring-boot-starter-hello</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2.controller
package com.xjggb.controller;
import com.xjggb.entity.HelloUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private HelloUser helloUser;
@GetMapping("hello")
public HelloUser hello(){
return helloUser;
}
}
3.yml
com:
name: 你好啊小老弟
age: 12
4.点开超级man访问
5.总结
1.复习Spring把bean添加到IOC的三种方式
1.xml方式
2.包扫描方式
3.@Configuration +@Bean配置类方式
2.知道SpringBoot的自动装配流程
3.自定义SpringBoot启动器
重点就是在resources下创建META-INF文件夹下创建spring.factories文件,交给
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());进行解析
然后在通过@Import()进行实例化进入ioc容器中