Spring中@Import的各种用法以及ImportAware接口
@import 注解
@import
注解提供了和xml中<import/>
元素等价的功能,实现导入的一个或多个配置类。@import
即可以在类上使用,也可以作为元注解使用。
@target(elementtype.type) @retention(retentionpolicy.runtime) @documented public @interface import { /** * {@link configuration}, {@link importselector}, {@link importbeandefinitionregistrar} * or regular component classes to import. */ class<?>[] value(); }
注解中只有一个value();
。支持导入@configuration
标注的配置类,实现importselector
接口的类、实现importbeandefinitionregistrar
接口的类和普通的@component
类。
作为元注解使用
@import
可以作为元注解使用,可以在@import
的继承上封装一层。我的理解是,这样做不会对外(使用方)暴露我内部的具体实现细节。
举个例子:例如@enableaspectjautoproxy
注解。
@import(aspectjautoproxyregistrar.class) public @interface enableaspectjautoproxy {
@enableaspectjautoproxy
就是被@import
这个元注解所标志了,我们(程序员)通过使用@enableaspectjautoproxy
来开启aspectjautoproxy,而spring底层是通过@import
导入相应的配置类来实现的。
导入实现importselector接口的类
先来看一下importselector
接口,该接口中只有一个方法:
public interface importselector { string[] selectimports(annotationmetadata importingclassmetadata); }
importselector,输入选择器。该接口就是用来根据给定的条件,选择导入哪些配置类。
举个例子:例如@enabletransactionmanagement
注解。
@import(transactionmanagementconfigurationselector.class) public @interface enabletransactionmanagement {
在@enabletransactionmanagement
注解中使用了@import(transactionmanagementconfigurationselector.class)
注解,其中transactionmanagementconfigurationselector
类就是实现了importselector
接口。
public class transactionmanagementconfigurationselector extends advicemodeimportselector<enabletransactionmanagement> { @override protected string[] selectimports(advicemode advicemode) { switch (advicemode) { case proxy: return new string[] {autoproxyregistrar.class.getname(), proxytransactionmanagementconfiguration.class.getname()}; case aspectj: return new string[] { transactionmanagementconfigutils.transaction_aspect_configuration_class_name}; default: return null; } } }
方法的内部实现逻辑也很简单,就是根据不同的advicemode
导入不同的配置类,来实现事务管理。
导入实现importbeandefinitionregistrar接口的类
importbeandefinitionregistrar
接口中也只有一个方法:
public interface importbeandefinitionregistrar { void registerbeandefinitions(annotationmetadata importingclassmetadata, beandefinitionregistry registry); }
该接口允许我们根据所给的注解元数据,按需注册额外的beandefinition
。
举个例子:例如@enableaspectjautoproxy
注解。
@import(aspectjautoproxyregistrar.class) public @interface enableaspectjautoproxy {
@enableaspectjautoproxy
注解引入了aspectjautoproxyregistrar.class
类,这个类就是实现了importbeandefinitionregistrar
接口。
class aspectjautoproxyregistrar implements importbeandefinitionregistrar { @override public void registerbeandefinitions( annotationmetadata importingclassmetadata, beandefinitionregistry registry) { aopconfigutils.registeraspectjannotationautoproxycreatorifnecessary(registry); annotationattributes enableaspectjautoproxy = annotationconfigutils.attributesfor(importingclassmetadata, enableaspectjautoproxy.class); if (enableaspectjautoproxy != null) { if (enableaspectjautoproxy.getboolean("proxytargetclass")) { aopconfigutils.forceautoproxycreatortouseclassproxying(registry); } if (enableaspectjautoproxy.getboolean("exposeproxy")) { aopconfigutils.forceautoproxycreatortoexposeproxy(registry); } } } }
registerbeandefinitions
中调用了aopconfigutils.registeraspectjannotationautoproxycreatorifnecessary(registry);
方法,这个方法就是在往传入的beandefinitionregistry registry
中注册beandefinition
。注册了beandefinition
之后,spring就会去实例化这个bean,从而达到aspectjautoproxy作用。
导入@configuration类
这次@import
最常见是使用方法。我们可以拆分配置类,然后在程序中按需导入相应的配置。
举个例子:例如@enableretry
注解。使用这个注解可以开启retry功能。
@enableaspectjautoproxy(proxytargetclass = false) @import(retryconfiguration.class) public @interface enableretry {
其内部就是导入了retryconfiguration
这个配置类。
importaware接口
importaware
接口是需要和@import
一起使用的。在@import
作为元注解使用时,通过@import
导入的配置类如果实现了importaware
接口就可以获取到导入该配置类接口的数据配置。有点绕,我们直接上代码。
举个例子:@enableasync
注解。
@import(asyncconfigurationselector.class) public @interface enableasync { //asyncconfigurationselector源码 public class asyncconfigurationselector extends advicemodeimportselector<enableasync> { private static final string async_execution_aspect_configuration_class_name = "org.springframework.scheduling.aspectj.aspectjasyncconfiguration"; @override @nullable public string[] selectimports(advicemode advicemode) { switch (advicemode) { case proxy: return new string[] {proxyasyncconfiguration.class.getname()}; case aspectj: return new string[] {async_execution_aspect_configuration_class_name}; default: return null; } } }
默认情况下使用advicemode
为proxy
,导入了proxyasyncconfiguration
类。
@configuration @role(beandefinition.role_infrastructure) public class proxyasyncconfiguration extends abstractasyncconfiguration { @bean(name = taskmanagementconfigutils.async_annotation_processor_bean_name) @role(beandefinition.role_infrastructure) public asyncannotationbeanpostprocessor asyncadvisor() { assert.notnull(this.enableasync, "@enableasync annotation metadata was not injected"); asyncannotationbeanpostprocessor bpp = new asyncannotationbeanpostprocessor(); class<? extends annotation> customasyncannotation = this.enableasync.getclass("annotation"); if (customasyncannotation != annotationutils.getdefaultvalue(enableasync.class, "annotation")) { bpp.setasyncannotationtype(customasyncannotation); } if (this.executor != null) { bpp.setexecutor(this.executor); } if (this.exceptionhandler != null) { bpp.setexceptionhandler(this.exceptionhandler); } bpp.setproxytargetclass(this.enableasync.getboolean("proxytargetclass")); bpp.setorder(this.enableasync.<integer>getnumber("order")); return bpp; } }
在proxyasyncconfiguration
的asyncadvisor
方法中需要获取到@enableasync
上的一些设置值,例如:this.enableasync.getboolean("proxytargetclass")
,this.enableasync.<integer>getnumber("order")
。
this.enableasync
是其父类abstractasyncconfiguration
的属性。abstractasyncconfiguration
实现了importaware
接口,从而就可以获取到@enableasync
上的信息了。
// abstractasyncconfiguration#setimportmetadata 源码 public void setimportmetadata(annotationmetadata importmetadata) { this.enableasync = annotationattributes.frommap( importmetadata.getannotationattributes(enableasync.class.getname(), false)); if (this.enableasync == null) { throw new illegalargumentexception( "@enableasync is not present on importing class " + importmetadata.getclassname()); } }
可能这个例子有点复杂的,还有一个稍微简单一点的例子:enableredishttpsession
。关于这个,读者可以自己去看一下源码debug学习一下。
欢迎关注公众号,大家一起学习成长。