spring boot 2源码系列(六)- 自动装配

《Spring Boot源码博客》

在老版本的spring项目中,需要通过xml配置IOC Bean,新版本的spring支持自动装配bean。在spring boot项目中只需要在配置文件中写几行配置,就能实现bean的自动装配。

spring注解支持派生

1、新建一个MyComponent注解,注解加上@Component注解。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyComponent {
    String value() default "";
}

2、将@MyComponent加到bean上

@MyComponent
public class MyBean {
}

MyBean会注入到IOC容器中,@MyBean使用了@Component注解,@MyBean也具备了@Component的功能。

@Import注解注入Bean

1、新建一个Bean01

public class Bean01 {
    
    private String name;

    public Bean01() {
    }

    public Bean01(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Bean01{" +
                "name='" + name + '\'' +
                '}';
    }
}

2、新建MyBeansConfiguration,在MyBeansConfiguration中配置Bean

public class MyBeansConfiguration {
    @Bean
    public String helloWorld() { // 方法名即 Bean 名称
        return "Hello,World 2018";
    }

    @Bean
    public Bean01 bean01() {
        return new Bean01("名字01");
    }
}

3、新建注解EnableMyBeans

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
/**
 * 将MyBeansConfiguration实例导入IOC容器中
 */
@Import(MyBeansConfiguration.class)
public @interface EnableMyBeans {
}

4、新建启动类,加上@EnableMyBeans。

@EnableMyBeans
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(SpringBootDemoApplication.class)
                .web(WebApplicationType.NONE)
                .run(args);

        MyBeansConfiguration myBeansConfiguration = context.getBean(MyBeansConfiguration.class);
        System.out.println("获取myBeansConfiguration: "+myBeansConfiguration);

        Bean01 bean01 = context.getBean(Bean01.class);
        System.out.println("获取到bean " + bean01);

        context.close();
    }
}

最终bean01被注入到了IOC容器中。整个注入流程如下:

1、SpringBootDemoApplication使用了@EnableMyBeans注解,

2、@EnableMyBeans使用@Import将MyBeansConfiguration导入到IOC容器

3、MyBeansConfiguration使用@Bean注解配置了Bean01。所以Bean01就注入了IOC容器中。

实现ImportSelector接口注入Bean

@Import是直接导入单个Bean,ImportSelector接口可以导入多个类到IOC容器中,实现ImportSelect的同时还可以实现EnvironmentAware、BeanFactoryAware等接口,增加判断条件。

1、新增MyImportSelector类实现ImportSelector接口。

public class MyImportSelector implements ImportSelector{
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        Map<String, Object> enableMyBeansAttributes = annotationMetadata.getAnnotationAttributes(EnableMyBeans.class.getName());
        System.out.println("根据EnableMyBeans属性执行自定义逻辑,比方说不将MyBeansConfiguration注入到IOC容器中");
        boolean enableMyImportSelector = Boolean.parseBoolean(String.valueOf(enableMyBeansAttributes.get("enableMyImportSelector")));
        // 可以同时实现EnvironmentAware、BeanFactoryAware等接口,增加判断条件
        return enableMyImportSelector ? new String[]{MyBeansConfiguration.class.getName()} : new String[]{};
    }
}

2、修改EnableMyBeans注解,增加注解属性

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
/**
 * 将MyImportSelector导入IOC容器中
 */
@Import(MyImportSelector.class)
public @interface EnableMyBeans {

    String value() default "";

    // 增加注解属性
    boolean enableMyImportSelector() default true;

}

3、修改启动类

@EnableMyBeans
// 设置 enableMyImportSelector = false 将不会导入MyBeansConfiguration到IOC容器
//@EnableMyBeans(enableMyImportSelector = false)
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(SpringBootDemoApplication.class)
                .web(WebApplicationType.NONE)
                .run(args);

        MyBeansConfiguration myBeansConfiguration = context.getBean(MyBeansConfiguration.class);
        System.out.println("获取myBeansConfiguration: "+myBeansConfiguration);

        Bean01 bean01 = context.getBean(Bean01.class);
        System.out.println("获取到bean " + bean01);

        context.close();
    }
}

4、前面的Bean01、MyBeansConfiguration不变。

运行程序,MyBeansConfiguration、Bean01注入到了IOC容器中。如果启用@EnableMyBeans(enableMyImportSelector = false)注解,则MyBeansConfiguration、Bean01不会注入IOC容器。

通过profile选择Bean

1、新建一个求和接口CalculateService

public interface CalculateService {
    Integer sum(Integer... values);
}

2、新建两个实现类,分别使用for循环、lambda实现加法

//当profile是Java7的时候使用这个类
@Profile("Java7")
@Service
public class Java7CalculateService implements CalculateService {
    @Override
    public Integer sum(Integer... values) {
        System.out.println("Java 7 for 循环实现 ");
        int sum = 0;
        for (int i = 0; i < values.length; i++) {
            sum += values[i];
        }
        return sum;
    }
}
//当profile是Java8的时候使用这个类
@Profile("Java8")
@Service
public class Java8CalculateService implements CalculateService {
    @Override
    public Integer sum(Integer... values) {
        System.out.println("Java 8 Lambda 实现");
        int sum = Stream.of(values).reduce(0, Integer::sum);
        return sum;
    }
}

3、新建启动类,并设置profiles。

@SpringBootApplication
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(SpringBootDemoApplication.class)
                .web(WebApplicationType.NONE)
                .profiles("Java7")  //传入profiles
                .run(args);
        CalculateService calculateService = context.getBean(CalculateService.class);
        System.out.println("calculateService.sum(1...10) : " +
                calculateService.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
        // 关闭上下文
        context.close();
    }
}

当profiles设置为Java7,Java7CalculateService被注入到IOC容器。当profiles是Java8,Java8CalculateService被注入到IOC容器。

条件注解

如果想在满足指定条件的时候才将某个bean加载到IOC容器中,可以使用条件注解。下面使用@ConditionalOnProperty介绍条件注解的用法。

1、给Bean01加上条件注解

@Component
// application.properties配置 enable.bean01=true 才将这个bean加载到IOC容器
@ConditionalOnProperty(value = "enable.bean01", havingValue = "true")
public class Bean01 {
    private String name;
    public Bean01() {
    }
    public Bean01(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Bean01{" +
                "name='" + name + '\'' +
                '}';
    }
}

2、application.properties 配置 enable.bean01=true

3、在启动类中尝试获取Bean01

@SpringBootApplication
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringBootDemoApplication.class, args);

        Bean01 bean01 =  context.getBean(Bean01.class);
        System.out.println("获取bean01   "+ bean01.toString());

        // 关闭上下文
        context.close();
    }
}

如果把 application.properties 配置改为 enable.bean01=false,Bean01将不会加载到IOC容器。

springboo提供了很多条件注解,下面简单介绍几个常见的:

@ConditionalOnProperty               根据property属性判断是否加载bean
@ConditionalOnBean                    IOC容器中存在某个bean,才加载使用了@ConditionalOnBean注解的bean
@ConditionalOnMissingBean        IOC容器中不存在某个bean,才加载使用了@ConditionalOnMissingBean注解的bean
@ConditionalOnClass                    classpath路径下存在某个类,才加载使用了@ConditionalOnClass注解的bean
@ConditionalOnMissingClass        classpath路径下不存在某个类,才加载使用了@ConditionalOnMissingClass注解的bean
@ConditionalOnExpression           满足spel表达式,加载bean
@ConditionalOnResource              classpath路径下存在某个资源文件,加载bean
@ConditionalOnWebApplication        web环境加载bean
@ConditionalOnNotWebApplication      非web环境加载bean

点开@ConditionalOnProperty的源码,发现类上使用了@Conditional(OnPropertyCondition.class)注解,OnPropertyCondition继承SpringBootCondition,SpringBootCondition是Condition的实现类。

如果想自己写一个条件装配注解需要写一个Condition接口的实现类,然后搭配@Conditional注解。下面是一个例子。

1、新建OnSystemAndPropertyCondition实现Condition接口。

public class OnSystemAndPropertyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 注解属性
        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSystemAndProperty.class.getName());
        String propertyName = String.valueOf(attributes.get("name"));
        String propertyValue = String.valueOf(attributes.get("value"));

        // 配置文件属性
        String property = context.getEnvironment().getProperty(propertyName, "");

        // 系统属性
        String systemUserName = System.getProperty("user.name");

        // ConditionalOnSystemProperty注解属性等于配置文件属性,并且系统用户名是Administrator,则注解条件成立
        return property.equals(propertyValue) && "Administrator".equals(systemUserName);
    }
}

2、新建ConditionalOnSystemAndProperty注解,注解上添加@Conditional(OnSystemAndPropertyCondition.class)

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnSystemAndPropertyCondition.class)
public @interface ConditionalOnSystemAndProperty {
    String name() default "";

    String value() default "";
}

3、Bean01使用@ConditionalOnSystemAndProperty(name = "enable.bean01", value = "true")和@Component组成条件装配

@Component
@ConditionalOnSystemAndProperty(name = "enable.bean01", value = "true")
public class Bean01 {
    private String name;
    public Bean01() {
    }
    public Bean01(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Bean01{" +
                "name='" + name + '\'' +
                '}';
    }
}

4、application.properties 配置 enable.bean01=true

5、在启动类中获取Bean01

@SpringBootApplication
public class SpringBootDemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringBootDemoApplication.class, args);

        Bean01 bean01 =  context.getBean(Bean01.class);
        System.out.println("获取bean01   "+ bean01.toString());

        // 关闭上下文
        context.close();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值