Spring Boot 手动配置@Enable的秘密

Spring Boot中,我们会经常遇到@Enable***用来激活我们某一个功能性的模块,通过类注解激活后我们就能使用所激活的配置给我们带来的功能。接下来我们就来探究一下这个@Enable***给我们做了哪些工作,或者我们应该怎么通过自定义的方式开发我们自己的功能模块。

演示环境

  • IntelliJ IDEA 2018.2.1 (Community Edition)
  • Maven 3.5.4
  • Spring Boot 2.1.1.RELEASE

走进源码

SpringBoot@Enable的实现方式用两种。一种是注解驱动的方式,我们以@EnableWebMvc为例进行探究;另外一种是接口编程的方式,我们以@EnableCaching为例进行探究。

1、注解驱动方式(@EnableWebMvc

Spring Boot项目中,当我们可以使用@EnableWebMvc注解用来激活我们的Spring MVC相关的配置,接下来进入源码一探究竟。

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

通过观察上面的源码,我们就可以大胆的猜测,其实使用@EnableWebMvc注解的作用就是导入DelegatingWebMvcConfiguration.class这个类,接下来我们就进入这个类看看。

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    ........
	@Override
	protected void addInterceptors(InterceptorRegistry registry) {
		this.configurers.addInterceptors(registry);
	}

	@Override
	protected void addResourceHandlers(ResourceHandlerRegistry registry) {
		this.configurers.addResourceHandlers(registry);
	}
    ........
}

进入到这个类中我们发现了这个@Configuration注解,到这儿我们好像明白了写什么。首先这个DelegatingWebMvcConfiguration类继承了WebMvcConfigurationSupport类,重写了里面的关于WebMvc的相关配置,然后作为一个配置类加载到我们的Spring容器中。至此来实现启动(激活)WebMvc模块。

2、接口编程的方式(@EnableCaching

Spring Boot项目中,当我们可以使用@EnableCaching注解用来激活我们的缓存相关的配置,接着进入源码看看到底做了什么。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {

	boolean proxyTargetClass() default false;
	AdviceMode mode() default AdviceMode.PROXY;
	int order() default Ordered.LOWEST_PRECEDENCE;

}

这里@EnableCaching同样是使用@Import导入了一个配置类,而它导入的是CachingConfigurationSelector,接着我进入这个类看一看。

public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {

	.....
        
	@Override
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return getProxyImports();
			case ASPECTJ:
				return getAspectJImports();
			default:
				return null;
		}
	}
    .....
}

发现其实这个类没有被注解标注,但是它继承了AdviceModeImportSelector<EnableCaching>,而这个类又继承了ImportSelector,并且我们可以看看ImportSelector的代码:

public interface ImportSelector {

	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);

}

这个类中只用一个方法,那就是selectImports。也就是说当我们重写了这个方法之后,我们可以在方法中添加自己的逻辑判断,来决定最后导入哪些配置类。这样就可以实现灵活的加载配置。这个方法的返回值String[]里面存放的是所有复合条件的配置类的全路径信息

自定义实现

通过上面的分析我们已经完成了对@Enable***套路的了解,接下来我们自己动手实现下。首先需要准备一个Spirng Boot的项目,这里我已经准备好了。

1、注解驱动方式的自定义实现

根据我们分析源码的步骤我们首先需要准备一个配置类。接下来在configuration包下,创建一个HelloConfiguration。代码如下:

/**
 * Hello模块的配置
 *
 * @author Jerome Zhu
 */
@Configuration
public class HelloConfiguration {

    @Bean
    public String hello() { // method name is bean name
        System.out.println("Bean : hello is loading.");
        return "hello word !";
    }

}

这里被@Bean标注的方法,方法名会作为bean对象的名称。而且当该bean被加载的时候我们还会在控制台输出一段话Bean : hello is loading.

配置类我们已经准备好了,接下来我们在annotation包中编写激活配置的注解。

/**
 * 激活Hello模块配置
 *
 * @author Jerome Zhu
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HelloConfiguration.class) // 模块装配:注解驱动实现
public @interface EnableHello {
}

最后我们在bootstrap包中编写验证我们@EnableHello模块的启动类。

/**
 * 验证自定义 {@link EnableHello} 模块化装配
 *
 * @author Jerome Zhu
 */
@EnableHello
public class EnableHelloBootstrap {

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

        String helloBean = context.getBean("hello", String.class);
        System.out.println("hello Bean: " + helloBean);
        context.close();
    }
}

我们在我们启动类上标注了@EnableHello来激活我们的Hello模块,并且在Spring Boot项目启动后,获取到了应用的上下文ConfigurableApplicationContext。然后我们根据我们注入的bean的名字hello来获取bean,接着打印bean的内容,最后关闭上下文。

我们启动程序后,可以在控制台中看到这样两行输出:

Bean : hello is loading.
hello Bean: hello word !

这刚好就是我们配置的内容,到这儿我们就完成了基于注解驱动方式激活(启动)配置。

2、接口编程方式自定义实现

根据我们读过源码后,我们首先最重要的是有一个继承ImportSelector的实现类。在annotation包中创建一个HelloImportSelector配置类。

/**
 * Hello {@link ImportSelector} 的实现
 *
 * @author Jerome Zhu
 */
public class HelloImportSelector implements ImportSelector {

    private final String key = "jerome";

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        if ("jerome".equals(key)) {
            return new String[]{HelloJeromeConfiguration.class.getName()};
        }

        return new String[]{HelloConfiguration.class.getName()};
    }
}

这里为了体现出selectImports方法的作用,我们在本类中添加一个key字段。当key的值为jerome的时候我们去加载HelloJeromeConfiguration这个配置类。首先看一个这个配置类的内容。

/**
 * Hello模块的配置
 *
 * @author Jerome Zhu
 */
@Configuration
public class HelloJeromeConfiguration {

    @Bean
    public String hello() { // method name is bean name
        System.out.println("Bean : hello is loading.");
        return "hello jerome !";
    }

}

加载这个配置类后bean的名字还是hello但是内容变成了hello jerome !。接着我们对@EnableHello注解进行改造改造成@EnableHellos

/**
 * 激活Hellos模块配置
 *
 * @author Jerome Zhu
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HelloImportSelector.class) // 模块装配:接口驱动实现 优点可选bean
public @interface EnableHellos {
}

这里使用了HelloImportSelector来实现我们的模块装配。

这里@EnableHellos可能不符合命名规范,这里只做演示使用,不要喷我。

最后我们在bootstrap包中编写验证我们@EnableHello模块的启动类。

/**
 * 验证自定义 {@link EnableHellos} 模块化装配
 *
 * @author Jerome Zhu
 */
@EnableHellos
public class EnableHellosBootstrap {

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

        String helloBean = context.getBean("hello", String.class);
        System.out.println("hello Bean: " + helloBean);
        context.close();
    }
}

这里和基于注解驱动方式激活(启动)配置的验证方法一样,我就是直接拷贝的,主要修改了@EnableHellos这个激活注解。

我们启动程序后,可以在控制台中看到这样两行输出:

Bean : hello is loading.
hello Bean: hello jerome !

这刚好就是我们配置的内容,到这儿我们就完成了基于接口编程方式激活(启动)配置。

总结

我们通过上面了解了@Enable***的两种实现方式。其中基于注解驱动方式激活(启动)配置的方式是相对方便的一个实现,需要激活什么就直接导入该配置类即可;而基于接口编程方式激活(启动)配置的方式相对更加灵活,可以根据自定义的规则来选择激活哪些配置类,不激活哪些配置类,这是一种比较灵活的方式,方便我们对其进行扩展。总之这两种方式都是手动激活的方式,换言之我们需要在启动类上添加@Enable***的注解,来手动激活配置,这种方式在某些场景并不是很方便。

具体的代码可以参考GitHub:spring-boot-demo-autoconfigure小节。

原文链接:Spring Boot 手动配置@Enable的秘密

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值