探索 SpringBoot (一) 自动装配
文章目录
1 缘起
随着 微服务的兴起,越来越多的人开始用 SpringBoot ,但是大部分人仅仅停留在使用的 层面 对于 SpringBoot 的实现缺少了解。所以跟着楼主一起揭开 SpringBoot 的面纱吧。
2 SpringBoot 自动装配 & Spring 手动装配
SpringBoot 最大的特点就少了各种各样的配置。遵守约定优于配置的原则。封装了许多底层实现。这是也 SpringBoot 为啥易学难精的原因了。
2.1 @Configuration 配置类模式注解
我们先来看看 @Configuration ,想必大家都不陌生,底层通过 继承 Component 标明是一个配置组件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
2.2 Spring @Enable 模块装配
@Enable 模块装配, 有许多 像 @EnableWebMvc,@EnableAsync…等
@Enable 模块装配 主要有两种实现方式
- 1 注解驱动方式
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldConfiguration.class)
public @interface EnableHelloWorld {
String value() default "";
}
@Configuration
public class HelloWorldConfiguration {
@Bean
public String helloWorld() {
return "this is hello word bean";
}
}
这种方式会 直接加载 HelloWorldConfiguration 类,并且初始化里面的 @Bean 对象
- 2 接口编程方式
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloworldImportSelector.class)
public @interface EnableHelloWorld {
String value() default "";
}
public class HelloworldImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[] {HelloWorldConfiguration.class.getName()};
}
}
1 这种方式 先加载 HelloworldImportSelector ,并且必须得实现 ImportSelector
2 执行 selectImports 获取 要加载的类数组
3 加载 HelloWorldConfiguration 并且初始化里面的 Bean 同上
2.3 Spring 条件装配 @Profile & @Conditional
Spring 条件装配分成两块 @Profile(配置化条件装配) & @Conditional(编程条件装配)
- 1 @Profile
被 @Profile(“xxx”) 标注的 bean 在注入之前 会获取系统的配置(profiles(“xxx”)), 如果 找到对应配置就装载,如果没找到对应配置就不装载
- 2 @Conditional
满足条件才装配的 bean
@Bean
@ConditionOnSystemProperty(name = "user.name", value = "jun")
public String helloWorld() {
return "hello word, junjun";
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionOnSystemProperty {
/**
* java 系统属性名称
* @return
*/
String name();
/**
* Java 系统属性值
* @return
*/
String value();
}
public class OnSystemPropertyCondition implements Condition{
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(ConditionOnSystemProperty.class.getName());
String propertyName =String.valueOf(annotationAttributes.get("name"));
String propertyVale =String.valueOf(annotationAttributes.get("value"));
String javaPropertyVale = System.getProperty(propertyName);
return propertyVale.equals(javaPropertyVale);
}
}
流程说明
1 用 @ConditionOnSystemProperty(name = "user.name", value = "jun") 注解标注 bean, 当条件满足时才执行注入
2 加载 OnSystemPropertyCondition 类,并且 OnSystemPropertyCondition 必须实现 Condition
3 执行 matches, 通过 注解名 ConditionOnSystemProperty 从 annotatedTypeMetadata, 获取该注解成员变量的 map
3 执行具体的业务逻辑 返回是否,如果返回 true 则注入 bean, 否则 不注入
3 SpringBoot 自动装配
好了 上面的铺垫说的 差不多了, 现在我们手写一个 SpringBoot 自动装配的例子
- 实现步骤
1 激活自动装配 @EnableAutoConfiguration
2 实现自动装配
3 配置自动装配的实现 META-INF/spring.factories
- 1 写一个起动类,并且激活自动装配 @EnableAutoConfiguration
@EnableAutoConfiguration
public class EnableAutoConfigurationBootstrap {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableAutoConfigurationBootstrap.class)
.web(WebApplicationType.NONE)
.run(args);
String helloWorld = context.getBean("helloWorld", String.class);
System.out.println("检查 bean:" + helloWorld);
context.close();
}
}
- 2 实现自动装配
@Configuration // Spring 模式注解
@EnableHelloWorld // Spring @Enable 模块装配
@ConditionOnSystemProperty(name = "user.name", value = "jun") // 条件装配
public class HelloWorldAutoConfiguration {
}
2.1 ConditionOnSystemProperty 实现
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionOnSystemProperty {
/**
* java 系统属性名称
* @return
*/
String name();
/**
* Java 系统属性值
* @return
*/
String value();
}
public class OnSystemPropertyCondition implements Condition{
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(ConditionOnSystemProperty.class.getName());
String propertyName =String.valueOf(annotationAttributes.get("name"));
String propertyVale =String.valueOf(annotationAttributes.get("value"));
String javaPropertyVale = System.getProperty(propertyName);
return propertyVale.equals(javaPropertyVale);
}
}
2.2 EnableHelloWorld
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloworldImportSelector.class)
public @interface EnableHelloWorld {
String value() default "";
}
public class HelloworldImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[] {HelloWorldConfiguration.class.getName()};
}
}
- 3 在 resources 下创建 META-INF/spring.factories 添加如下配置
# 配置 HelloWorldAutoConfiguration 自动装配
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.imooc.configuration.HelloWorldAutoConfiguration
4 自动装配流程分析
1 springboot 扫描 META-INF/spring.factories 下面的 EnableAutoConfiguration 配置。
2 根据配置 加载 HelloWorldAutoConfiguration
3 先进行 ConditionOnSystemProperty 进行条件判断
4 如果满足条件 执行 EnableHelloWorld 自动装配 bean
5 总结
因为写文章的时间比较短, 所以难免有疏漏之处。如果大家发现那里写的不对,欢迎在评论里面评论哈。