Spring Boot中@Import三种使用方式

@Import注解在Spring Boot中用于动态控制Bean注入。本文通过实例详细讲解了三种用法:1) 引入普通类;2) 引入ImportSelector实现类,实现动态注入,包括静态和动态import场景;3) 引入ImportBeanDefinitionRegistrar实现类,自定义注册Bean,包括冲突处理。示例涵盖了从简单到复杂的应用场景。
摘要由CSDN通过智能技术生成

@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>

完美,成功!

 

Spring Boot,@Import注解用于导入其他配置类或者配置文件。它的作用是将其他配置类或者配置文件的bean导入到当前的配置类。通常情况下,我们可以使用@Import注解来导入以下类型的配置类或者配置文件: 1.其他@Configuration注解标注的配置类 2.@Enable*注解标注的配置类 3.@ImportSelector接口的实现类 4.@ImportBeanDefinitionRegistrar接口的实现类 使用@Import注解的语法如下: ``` @Configuration @Import({OtherConfig.class, AnotherConfig.class}) public class AppConfig { // ... } ``` 在上面的例子,我们使用@Import注解来导入了两个其他的配置类:OtherConfig和AnotherConfig。这些配置类定义的bean将会被自动注册到当前的配置类。 除此之外,@Import注解还可以用来动态装配bean,例如: ``` @Configuration public class AppConfig { @Bean public MyBean myBean() { return new MyBean(); } @Import(MyImportSelector.class) public static class MyConfig {} public static class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[] { "com.example.AnotherBean" }; } } } ``` 在上面的例子,我们定义了一个MyBean的bean,并且使用@Import注解来导入了一个实现了ImportSelector接口的MyImportSelector类。这个类会动态地返回一个字符串数组,包含了要导入的bean的类名。在这个例子,我们导入了另一个名为AnotherBean的bean。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jh035

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值