美文网首页
SpringBoot手动装配Bean

SpringBoot手动装配Bean

作者: 茶还是咖啡 | 来源:发表于2019-12-13 07:42 被阅读0次

Spring Framework 3.1 开始支持”@Enable 模块驱动“。所谓“模块”是指具备相同领域的功能组件集合, 组合所形成一个独立的单元。比如 Web MVC 模块、AspectJ代理模块、Caching(缓存)模块、JMX(Java 管 理扩展)模块、Async(异步处理)模块等。

  • 定义:具备相同领域的功能组件集合,组合所形成的一个独立的单元
框架实现 @Enable 注解模块 激活模块
Spring Framework @EnableWebMvc Web MVC 模块
@EnableTransactionManagement 事务管理模块
@EnableCaching Caching模块
@EnableMBeanExport JMX 模块
@EnableAsync 异步处理模块
@EnableWebFlux Web Flux模块
@EnableAspectJAutoProxy AspectJ 代理模块
Spring Boot @EnableAutoConfiguration 自动装配模块
@EnableManagementContext Actuator 管理模块
@EnableConfigurationProperties 配置属性绑定模块
@EnableOAuth2Sso OAuth2 单点登录模块
SpringCloud @EnableEurekaServer Eureka服务器模块
@EnableConfigServer 配置服务器模块
@EnableFeignClients Feign客户端模块
@EnableZuulProxy 服务网关 Zuul 模块
@EnableCircuitBreaker 服务熔断模块

手动装配实现原理,以@EnableWebMvc为例

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

该注解中的关键是使用了@Import注解,@Import是这样的。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
    Class<?>[] value();
}

java doc 给出这样一段注释:
Indicates one or more {@link Configuration @Configuration} classes to import.
大概意思就是:指示要导入的一个或多个{@link Configuration@Configuration}类。
可以大概的理解为向SpringIOC容器中注入一个或者多个配置类,就像我们下面这样

@Configuration
public class XConfigure {
    @Bean
    public xxx(){
        return new Xxx();
    }
}

自定义@EnableXXX模块

基于注解驱动实现

  1. 创建一个需要被装载的类
public class HelloWorld {
    public HelloWorld(){
        System.out.println("hello world");
    }
}
  1. 创建一个配置类,装载HelloWorld.
public class HelloWorldConfig {
    @Bean
    public HelloWorld helloWorld(){
        return new HelloWorld();
    }
}
  1. 自定义模块驱动注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({HelloWorldConfig.class})
public @interface EnableHelloWorld {
}
  1. 测试,在启动类/配置类上写@EnableHelloWorld装载该组件,启动SpringBoo项目,控制台打印helloworld,说明自定义注解生效,HelloWorld类被装载至SpringIOC容器中。
    image.png

接口编程方式

  1. 新建配置类,并实现ImportSelector接口
public class HelloWorldSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{HelloWorld.class.getName()};
    }
}
  1. 修改@EnableHelloWorld注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({HelloWorldSelector.class})
public @interface EnableHelloWorld {
}
  1. 启动容器,控制台打印HelloWorld,说明接口生效,基于注解驱动实现HelloWorldImportSelector--->HelloWorldConfiguration--->HelloWorld
    这样通过HelloWorldImportSelector间接的进行装配更加的灵活
    image.png

条件装配

从Spring Framework3.1开始,允许Bean装配时增加前置条件判断

配置方式-@Profile

编程方式-@Condifional

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
    /**
     * The classes that must be present. Since this annotation is parsed by loading class
     * bytecode, it is safe to specify classes here that may ultimately not be on the
     * classpath, only if this annotation is directly on the affected component and
     * <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
     * use this annotation as a meta-annotation, only use the {@link #name} attribute.
     * @return the classes that must be present
     */
    Class<?>[] value() default {};
    /**
     * The classes names that must be present.
     * @return the class names that must be present.
     */
    String[] name() default {};
}

自定义条件装配

eg:实现一个方法,java 7使用for循环的形式实现累加,java 8使用Lambda的形式实现累加。

基于配置方式实现-@Profile
  • 接口:
public interface CalculateService {

    /**
     * 从多个整数sum求和
     * @param values 多个整数
     * @return sum累加值
     */
    Integer sum(Integer ...values);
}
  • java7实现
@Profile("Java7")
@Service
public class Java7CalculateServiceImpl implements CalculateService {
    @Override
    public Integer sum(Integer... values) {
        System.out.println("Java7 for 循环实现{@link CalculateService}");
        int sum=0;
        for (Integer value : values) {
            sum += value;
        }
        return sum;
    }
}
  • java8实现
@Profile("Java8")
@Service
public class Java8CalculateServiceImpl implements CalculateService {
    @Override
    public Integer sum(Integer... values) {
        System.out.println("Java8 Lambda 循环实现{@link CalculateService}");
        return Stream.of(values).reduce(0,Integer::sum);
    }
}
  • 启动类

    只扫描service包~

@SpringBootApplication(scanBasePackages = "org.ywb.service")
public class CalculateServiceBootStrap {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(CalculateServiceBootStrap.class)
                .web(WebApplicationType.NONE)
                .profiles("Java8")
                .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();
    }
}
基于编程方式实现

场景:只有name的值符合要求才返回相应的Bean

  1. 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionOnSystemProperty {
    /**
     * Java 系统属性名称
     * @return
     */
    String name();

    /**
     * Java 系统属性名称
     * @return
     */
    String value();
}
  1. 创建Condition类
public class OnSystemPropertyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionOnSystemProperty.class.getName());

        String propertyName = String.valueOf(attributes.get("name"));
        String propertyValue = String.valueOf(attributes.get("value"));

        return "happy".equals(propertyName);
    }
}
  • 启动类
public class ConditionOnSystemPropertyBootstrap {

    @Bean
    @ConditionOnSystemProperty(name="happy",value = "balabala") // 只有name的值等于happy的时候,该“hello,world:-)”才会被装载
    public String helloWorld(){
        return "hello,world :-)";
    }

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

        String helloWorld = context.getBean("helloWorld", String.class);
        System.out.println(helloWorld);

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

    }
}

相关文章

网友评论

      本文标题:SpringBoot手动装配Bean

      本文链接:https://www.haomeiwen.com/subject/zkjxnctx.html