@Import是一个非常有用的注解,它的长处在于你可以通过配置来控制是否注入该Bean,也可以通过条件来控制注入哪些Bean到Spring容器中。
比如我们熟悉的:@EnableAsync
、@EnableCaching
、@EnableScheduling
等等统一采用的都是借助@Import注解来实现的。
下面我们就通过示例来了解@Import三种用法!
一、引入普通类
有个用户类如下
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Data</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserConfig</span> {
<span style="color:#75715e">/** 用户名*/</span>
<span style="color:#f92672">private</span> String username;
<span style="color:#75715e">/**手机号*/</span>
<span style="color:#f92672">private</span> String phone;
}
</code></span></span>
那么如何通过@Import注入容器呢?
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Import(UserConfig.class)</span>
<span style="color:#75715e">@Configuration</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserConfiguration</span> {
}
</code></span></span>
当在@Configuration
标注的类上使用@Import引入了一个类后,就会把该类注入容器中。
当然除了@Configuration 比如@Component、@Service等一样也可以。
测试
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@SpringBootTest</span>
<span style="color:#75715e">@RunWith(SpringRunner.class)</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserServiceTest</span> {
<span style="color:#75715e">@Autowired</span>
<span style="color:#f92672">private</span> UserConfig userConfig;
<span style="color:#75715e">@Test</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">getUser</span><span style="color:#f8f8f2">()</span> {
<span style="color:#e6db74">String</span> <span style="color:#e6db74">name</span> <span style="color:#ab5656">=</span> userConfig.getClass().getName();
System.out.println(<span style="color:#e6db74">"name = "</span> + name);
}
}
</code></span></span>
控制台输出
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-ini"><span style="color:#f92672">name</span> = com.jincou.importselector.model.UserConfig
</code></span></span>
如果@Import的功能仅仅是这样,那其实它并没什么特别的价值,我们可以通过其它方式实现?
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Configuration</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserConfiguration</span> {
<span style="color:#75715e">@Bean</span>
<span style="color:#f92672">public</span> UserConfig <span style="color:#a6e22e">userConfig</span><span style="color:#f8f8f2">()</span> {
<span style="color:#f92672">return</span> <span style="color:#f92672">new</span> <span style="color:#a6e22e">UserConfig</span>();
}
}
</code></span></span>
再比如直接添加@Configuration注解
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Configuration</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserConfig</span> {
<span style="color:#75715e">// ......</span>
}
</code></span></span>
确实如果注入静态的Bean到容器中,那完全可以用上面的方式代替,但如果需要动态的带有逻辑性的注入Bean,那才更能体现@Import的价值。
二、引入ImportSelector的实现类
说到ImportSelector这个接口就不得不说这里面最重要的一个方法:selectImports()
。
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">interface</span> <span style="color:#a6e22e">ImportSelector</span> {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
</code></span></span>
这个方法的返回值是一个字符串数组
,只要在配置类被引用了,这里返回的字符串数组中的类名
就会被Spring容器new出来,然后再把这些对象注入IOC容器中。
所以这有啥用呢?我们还是用一个例子演示一下。
1、静态import场景(注入已知的类)
我们先将上面的示例改造下:
自定义MyImportSelector实现ImportSelector接口,重写selectImports方法
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">MyImportSelector</span> <span style="color:#f92672">implements</span> <span style="color:#a6e22e">ImportSelector</span> {
<span style="color:#75715e">@Override</span>
<span style="color:#f92672">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) {
<span style="color:#75715e">//这里目的是将UserConfig 注入容器中</span>
<span style="color:#f92672">return</span> <span style="color:#f92672">new</span> <span style="color:#a6e22e">String</span>[]{<span style="color:#e6db74">"com.jincou.importselector.model.UserConfig"</span>};
}
}
</code></span></span>
然后在配置类引用
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Import(MyImportSelector.class)</span>
<span style="color:#75715e">@Configuration</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserConfiguration</span> {
}
</code></span></span>
这样一来同样可以通过成功将UserConfig注入容器中。
如果看到这,你肯定会有疑问。我这又是新建MyImportSelector类,又是实现ImportSelector重写selectImports方法,然后我这么做有个卵用呢?
直接把类上加个@Component
注入进去不香吗?这个ImportSelector
把简单的功能搞这么复杂。
接下来就要说说如何动态注入Bean了。
2、动态import场景(注入指定条件的类)
我们来思考一种场景,就是你想通过开关来控制是否注入该Bean,或者说通过配置来控制注入哪些Bean,这个时候就有了ImportSelector的用武之地了。
我们来举个例子,通过ImportSelector的使用实现条件选择是注入本地缓存还是Redis缓存
。
1)、定义缓存接口和实现类
顶层接口
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">interface</span> <span style="color:#a6e22e">CacheService</span> {
<span style="color:#f92672">void</span> <span style="color:#a6e22e">setData</span><span style="color:#f8f8f2">(String key)</span>;
}
</code></span></span>
本地缓存
实现类
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">LocalServicempl</span> <span style="color:#f92672">implements</span> <span style="color:#a6e22e">CacheService</span> {
<span style="color:#75715e">@Override</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">setData</span><span style="color:#f8f8f2">(String key)</span> {
System.out.println(<span style="color:#e6db74">"本地存储存储数据成功 key= "</span> + key);
}
}
</code></span></span>
redis缓存
实现类
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">RedisServicempl</span> <span style="color:#f92672">implements</span> <span style="color:#a6e22e">CacheService</span> {
<span style="color:#75715e">@Override</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">setData</span><span style="color:#f8f8f2">(String key)</span> {
System.out.println(<span style="color:#e6db74">"redis存储数据成功 key= "</span> + key);
}
}
</code></span></span>
2)、定义ImportSelector实现类
以下代码中根据EnableMyCache
注解中的不同值来切换缓存的实现类再spring中的注册。
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">MyCacheSelector</span> <span style="color:#f92672">implements</span> <span style="color:#a6e22e">ImportSelector</span> {
<span style="color:#75715e">@Override</span>
<span style="color:#f92672">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableMyCache.class.getName());
<span style="color:#75715e">//通过 不同type注入不同的缓存到容器中</span>
<span style="color:#e6db74">CacheType</span> <span style="color:#e6db74">type</span> <span style="color:#ab5656">=</span> (CacheType) annotationAttributes.get(<span style="color:#e6db74">"type"</span>);
<span style="color:#f92672">switch</span> (type) {
<span style="color:#f92672">case</span> LOCAL: {
<span style="color:#f92672">return</span> <span style="color:#f92672">new</span> <span style="color:#a6e22e">String</span>[]{LocalServicempl.class.getName()};
}
<span style="color:#f92672">case</span> REDIS: {
<span style="color:#f92672">return</span> <span style="color:#f92672">new</span> <span style="color:#a6e22e">String</span>[]{RedisServicempl.class.getName()};
}
<span style="color:#f92672">default</span>: {
<span style="color:#f92672">throw</span> <span style="color:#f92672">new</span> <span style="color:#a6e22e">RuntimeException</span>(MessageFormat.format(<span style="color:#e6db74">"unsupport cache type {0}"</span>, type.toString()));
}
}
}
}
</code></span></span>
3)、定义注解
@EnableMyCache
注解就像一个开关,通过这个开关来是否将特定的Bean注入容器。
定义一个枚举
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Target(ElementType.TYPE)</span>
<span style="color:#75715e">@Retention(RetentionPolicy.RUNTIME)</span>
<span style="color:#75715e">@Documented</span>
<span style="color:#75715e">@Import(MyCacheSelector.class)</span>
<span style="color:#f92672">public</span> <span style="color:#75715e">@interface</span> EnableMyCache {
CacheType <span style="color:#a6e22e">type</span><span style="color:#f8f8f2">()</span> <span style="color:#f92672">default</span> CacheType.REDIS;
}
</code></span></span>
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">enum</span> <span style="color:#a6e22e">CacheType</span> {
LOCAL, REDIS;
}
</code></span></span>
4)、测试
这里选择本地缓存。
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@EnableMyCache(type = CacheType.LOCAL)</span>
<span style="color:#75715e">@SpringBootTest</span>
<span style="color:#75715e">@RunWith(SpringRunner.class)</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserServiceTest</span> {
<span style="color:#75715e">@Autowired</span>
<span style="color:#f92672">private</span> CacheService cacheService;
<span style="color:#75715e">@Test</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">test</span><span style="color:#f8f8f2">()</span> {
cacheService.setData(<span style="color:#e6db74">"key"</span>);
}
}
</code></span></span>
控制台输出
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java">本地存储存储数据成功 key= key
</code></span></span>
切换成redis缓存
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@EnableMyCache(type = CacheType.REDIS)</span>
<span style="color:#75715e">@SpringBootTest</span>
<span style="color:#75715e">@RunWith(SpringRunner.class)</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserServiceTest</span> {
<span style="color:#75715e">@Autowired</span>
<span style="color:#f92672">private</span> CacheService cacheService;
<span style="color:#75715e">@Test</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">test</span><span style="color:#f8f8f2">()</span> {
cacheService.setData(<span style="color:#e6db74">"key"</span>);
}
}
</code></span></span>
控制台输出
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java">redis存储数据成功 key= key
</code></span></span>
这个示例不是就是Bean的动态注入了吗?
3、Spring如何使用ImportSelector的场景
SpringBoot有两个常用注解 @EnableAsync @EnableCaching 其实就是通过ImportSelector来动态注入Bean
看下@EnableAsync注解,它有通过@Import({AsyncConfigurationSelector.class})
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Target({ElementType.TYPE})</span>
<span style="color:#75715e">@Retention(RetentionPolicy.RUNTIME)</span>
<span style="color:#75715e">@Documented</span>
<span style="color:#75715e">@Import({AsyncConfigurationSelector.class})</span>
<span style="color:#f92672">public</span> <span style="color:#75715e">@interface</span> EnableAsync {
Class<? <span style="color:#f92672">extends</span> <span style="color:#a6e22e">Annotation</span>> annotation() <span style="color:#f92672">default</span> Annotation.class;
<span style="color:#e6db74">boolean</span> <span style="color:#a6e22e">proxyTargetClass</span><span style="color:#f8f8f2">()</span> <span style="color:#f92672">default</span> <span style="color:#ae81ff">false</span>;
AdviceMode <span style="color:#a6e22e">mode</span><span style="color:#f8f8f2">()</span> <span style="color:#f92672">default</span> AdviceMode.PROXY;
<span style="color:#e6db74">int</span> <span style="color:#a6e22e">order</span><span style="color:#f8f8f2">()</span> <span style="color:#f92672">default</span> <span style="color:#ae81ff">2147483647</span>;
}
</code></span></span>
AsyncConfigurationSelector.class
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">AsyncConfigurationSelector</span> <span style="color:#f92672">extends</span> <span style="color:#a6e22e">AdviceModeImportSelector</span><EnableAsync> {
<span style="color:#f92672">private</span> <span style="color:#f92672">static</span> <span style="color:#f92672">final</span> <span style="color:#e6db74">String</span> <span style="color:#e6db74">ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME</span> <span style="color:#ab5656">=</span> <span style="color:#e6db74">"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"</span>;
<span style="color:#f92672">public</span> <span style="color:#a6e22e">AsyncConfigurationSelector</span><span style="color:#f8f8f2">()</span> {
}
<span style="color:#75715e">@Nullable</span>
<span style="color:#f92672">public</span> String[] selectImports(AdviceMode adviceMode) {
<span style="color:#f92672">switch</span>(adviceMode) {
<span style="color:#f92672">case</span> PROXY:
<span style="color:#f92672">return</span> <span style="color:#f92672">new</span> <span style="color:#a6e22e">String</span>[]{ProxyAsyncConfiguration.class.getName()};
<span style="color:#f92672">case</span> ASPECTJ:
<span style="color:#f92672">return</span> <span style="color:#f92672">new</span> <span style="color:#a6e22e">String</span>[]{<span style="color:#e6db74">"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"</span>};
<span style="color:#f92672">default</span>:
<span style="color:#f92672">return</span> <span style="color:#ae81ff">null</span>;
}
}
}
</code></span></span>
是不是和我上面写的示例一样。
总之,向这种还不能决定去注入哪个处理器(如果你能决定,那就直接@Import那个类好了,没必要实现接口了),就可以实现此接口,写出一些判断逻辑,不同的配置情况注入不同的处理类。
三、引入ImportBeanDefinitionRegister的实现类
当配置类实现了 ImportBeanDefinitionRegistrar 接口,你就可以自定义往容器中注册想注入的Bean。
这个接口相比与 ImportSelector 接口的主要区别就是,ImportSelector接口是返回一个类,你不能对这个类进行任何操作,但是 ImportBeanDefinitionRegistrar 是可以自己注入 BeanDefinition,可以添加属性之类的。
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">MyImportBean</span> <span style="color:#f92672">implements</span> <span style="color:#a6e22e">ImportBeanDefinitionRegistrar</span> {
<span style="color:#75715e">/**
* <span style="color:#808080">@param</span> importingClassMetadata 当前类的注解信息
* <span style="color:#808080">@param</span> registry 注册类,其registerBeanDefinition()可以注册bean
*/</span>
<span style="color:#75715e">@Override</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">registerBeanDefinitions</span><span style="color:#f8f8f2">(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span> {
}
}
</code></span></span>
1、举一个简单的示例
我们通过先通过一个简单的小示例,来理解它的基本使用
假设有个用户配置类如下
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Data</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserConfig</span> {
<span style="color:#75715e">/** 用户名*/</span>
<span style="color:#f92672">private</span> String username;
<span style="color:#75715e">/**手机号*/</span>
<span style="color:#f92672">private</span> String phone;
}
</code></span></span>
我们通过实现ImportBeanDefinitionRegistrar的方式来完成注入。
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">MyImportBean</span> <span style="color:#f92672">implements</span> <span style="color:#a6e22e">ImportBeanDefinitionRegistrar</span> {
<span style="color:#75715e">/**
* <span style="color:#808080">@param</span> importingClassMetadata 当前类的注解信息
* <span style="color:#808080">@param</span> registry 注册类,其registerBeanDefinition()可以注册bean
*/</span>
<span style="color:#75715e">@Override</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">registerBeanDefinitions</span><span style="color:#f8f8f2">(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span> {
<span style="color:#75715e">//构建一个 BeanDefinition , Bean的类型为 UserConfig,这个Bean的属性username的值为后端元宇宙</span>
<span style="color:#e6db74">AbstractBeanDefinition</span> <span style="color:#e6db74">beanDefinition</span> <span style="color:#ab5656">=</span> BeanDefinitionBuilder.rootBeanDefinition(UserConfig.class)
.addPropertyValue(<span style="color:#e6db74">"username"</span>, <span style="color:#e6db74">"后端元宇宙"</span>)
.getBeanDefinition();
<span style="color:#75715e">//把 UserConfig 这个Bean的定义注册到容器中</span>
registry.registerBeanDefinition(<span style="color:#e6db74">"userConfig"</span>, beanDefinition);
}
}
</code></span></span>
通过配置类 中引入MyImportBean对象。
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Import(MyImportBean.class)</span>
<span style="color:#75715e">@Configuration</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserImportConfiguration</span> {
}
</code></span></span>
我们再来测试下
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@EnableMyCache(type = CacheType.REDIS)</span>
<span style="color:#75715e">@SpringBootTest</span>
<span style="color:#75715e">@RunWith(SpringRunner.class)</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserServiceTest</span> {
<span style="color:#75715e">@Autowired</span>
<span style="color:#f92672">private</span> UserConfig userConfig;
<span style="color:#75715e">@Test</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">test</span><span style="color:#f8f8f2">()</span> {
<span style="color:#e6db74">String</span> <span style="color:#e6db74">username</span> <span style="color:#ab5656">=</span> userConfig.getUsername();
System.out.println(<span style="color:#e6db74">"username = "</span> + username);
}
}
</code></span></span>
控制台输出
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java">username = 后端元宇宙
</code></span></span>
说明通过ImportBeanDefinitionRegistrar方式,已经把UserConfig注入容器成功,而且还为给bean设置了新属性。
然后我们再来思考一个问题,就比如我们在其它地方已经将UserConfig注入容器,这里会不会出现冲突,或者不冲突的情况下,属性能不能设置成功?
我们来试下
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Import(MyImportBean.class)</span>
<span style="color:#75715e">@Configuration</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserImportConfiguration</span> {
<span style="color:#75715e">/**
* 这里通过<span style="color:#808080">@Bean</span>注解,将UserConfig注入Spring容器中,而且名称也叫userConfig
*/</span>
<span style="color:#75715e">@Bean</span>
<span style="color:#f92672">public</span> UserConfig <span style="color:#a6e22e">userConfig</span><span style="color:#f8f8f2">()</span> {
<span style="color:#f92672">return</span> <span style="color:#f92672">new</span> <span style="color:#a6e22e">UserConfig</span>();
}
}
</code></span></span>
然后我们再来跑下上面的测试用例,发现报错了。
2、举一个复杂点的例子
Mybatis的@MapperScan就是用这种方式实现的,@MapperScan注解,指定basePackages,扫描Mybatis Mapper接口类注入到容器中。
这里我们自定义一个注解@MyMapperScan
来扫描包路径下所以带@MapperBean
注解的类,并将它们注入到IOC容器中。
1)、先定义一个@MapperBean
注解,就相当于我们的@Mapper注解
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">/**
* 定义包路径。(指定包下所有添加了MapperBean注解的类作为bean)
* 注意这里 <span style="color:#808080">@Import</span>(MyMapperScanImportBean.class) 的使用
*/</span>
<span style="color:#75715e">@Retention(RetentionPolicy.RUNTIME)</span>
<span style="color:#75715e">@Target(ElementType.TYPE)</span>
<span style="color:#75715e">@Documented</span>
<span style="color:#f92672">public</span> <span style="color:#75715e">@interface</span> MapperBean {
}
</code></span></span>
2)、一个需要注入的bean,这里加上@MapperBean
注解。
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">package</span> com.jincou.importselector.mapperScan;
<span style="color:#f92672">import</span> com.jincou.importselector.config.MapperBean;
<span style="color:#75715e">@MapperBean</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">User</span> {
}
</code></span></span>
3)、再定一个扫描包路径的注解@MyMapperScan
就相当于mybatis的@MapperScan注解。
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@Retention(RetentionPolicy.RUNTIME)</span>
<span style="color:#75715e">@Target(ElementType.TYPE)</span>
<span style="color:#75715e">@Documented</span>
<span style="color:#75715e">@Import(MyMapperScanImportBean.class)</span>
<span style="color:#f92672">public</span> <span style="color:#75715e">@interface</span> MyMapperScan {
<span style="color:#75715e">/**
* 扫描包路径
*/</span>
String[] basePackages() <span style="color:#f92672">default</span> {};
}
</code></span></span>
4)、MyMapperScanImportBean实现ImportBeanDefinitionRegistrar接口
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">MyMapperScanImportBean</span> <span style="color:#f92672">implements</span> <span style="color:#a6e22e">ImportBeanDefinitionRegistrar</span>, ResourceLoaderAware {
<span style="color:#f92672">private</span> <span style="color:#f92672">final</span> <span style="color:#f92672">static</span> <span style="color:#e6db74">String</span> <span style="color:#e6db74">PACKAGE_NAME_KEY</span> <span style="color:#ab5656">=</span> <span style="color:#e6db74">"basePackages"</span>;
<span style="color:#f92672">private</span> ResourceLoader resourceLoader;
<span style="color:#75715e">/**
* 搜索指定包下所有添加了MapperBean注解的类,并且把这些类添加到ioc容器里面去
*
* <span style="color:#808080">@param</span> importingClassMetadata 当前类的注解信息
* <span style="color:#808080">@param</span> registry 注册类,其registerBeanDefinition()可以注册bean
*/</span>
<span style="color:#75715e">@Override</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">registerBeanDefinitions</span><span style="color:#f8f8f2">(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span> {
<span style="color:#75715e">//1. 从BeanIocScan注解获取到我们要搜索的包路径</span>
<span style="color:#e6db74">AnnotationAttributes</span> <span style="color:#e6db74">annoAttrs</span> <span style="color:#ab5656">=</span> AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName()));
<span style="color:#f92672">if</span> (annoAttrs == <span style="color:#ae81ff">null</span> || annoAttrs.isEmpty()) {
<span style="color:#f92672">return</span>;
}
String[] basePackages = (String[]) annoAttrs.get(PACKAGE_NAME_KEY);
<span style="color:#75715e">// 2. 找到指定包路径下所有添加了MapperBean注解的类,并且把这些类添加到IOC容器里面去</span>
<span style="color:#e6db74">ClassPathBeanDefinitionScanner</span> <span style="color:#e6db74">scanner</span> <span style="color:#ab5656">=</span> <span style="color:#f92672">new</span> <span style="color:#a6e22e">ClassPathBeanDefinitionScanner</span>(registry, <span style="color:#ae81ff">false</span>);
scanner.setResourceLoader(resourceLoader);
<span style="color:#75715e">//路径包含MapperBean的注解的bean</span>
scanner.addIncludeFilter(<span style="color:#f92672">new</span> <span style="color:#a6e22e">AnnotationTypeFilter</span>(MapperBean.class));
<span style="color:#75715e">//扫描包下路径</span>
scanner.scan(basePackages);
}
<span style="color:#75715e">@Override</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">setResourceLoader</span><span style="color:#f8f8f2">(ResourceLoader resourceLoader)</span> {
<span style="color:#e6db74">this</span>.resourceLoader = resourceLoader;
}
}
</code></span></span>
5)测试
这里扫描的路径就是上面User实体的位置
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java"><span style="color:#75715e">@RunWith(SpringRunner.class)</span>
<span style="color:#75715e">@SpringBootTest</span>
<span style="color:#75715e">@MyMapperScan(basePackages = {"com.jincou.importselector.mapperScan"})</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">class</span> <span style="color:#a6e22e">UserServiceTest</span> {
<span style="color:#75715e">@Autowired</span>
<span style="color:#f92672">private</span> User user;
<span style="color:#75715e">@Test</span>
<span style="color:#f92672">public</span> <span style="color:#f92672">void</span> <span style="color:#a6e22e">test</span><span style="color:#f8f8f2">()</span> {
System.out.println(<span style="color:#e6db74">"username = "</span> + user.getClass().getName());
}
}
</code></span></span>
运行结果
<span style="color:#4b4b4b"><span style="background-color:#ffffff"><code class="language-java">username = com.jincou.importselector.mapperScan.User
</code></span></span>
完美,成功!