@import注解的三种用法

@Import是一个非常有用的注解,它的长处在于你可以通过配置来控制是否注入该Bean,也可以通过条件来控制注入哪些Bean到Spring容器中。

比如我们熟悉的:@EnableAsync 、@EnableCaching、@EnableScheduling等等统一采用的都是借助@Import注解来实现的。

一、引入普通类

有个用户类如下

public class Test implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("注入成功");
    }
}

那么如何通过@Import注入容器呢?

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Import({Test.class})
@Configuration
public class Myclass2  {
   
}

当在@Configuration标注的类上使用@Import引入了一个类后,就会把该类注入容器中。

当然除了@Configuration 比如@Component、@Service等一样也可以。
测试效果如下:
在这里插入图片描述

二、引入ImportSelector的实现类

1、静态import场景(注入已知的类)
我们先将上面的示例改造下:

自定义MyImportSelector实现ImportSelector接口,重写selectImports方法

public class MyImportSelector implements ImportSelector {


    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.example.eureka.config.Test"};
    }
}

然后在配置类引用

package com.example.eureka.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Import({MyImportSelector.class})
@Configuration
public class Myclass2  {

}

2、动态import场景(注入指定条件的类)
我们来思考一种场景,就是你想通过开关来控制是否注入该Bean,或者说通过配置来控制注入哪些Bean,这个时候就有了ImportSelector的用武之地了。
我们来举个例子,通过ImportSelector的使用实现条件选择是注入本地缓存还是Redis缓存。

1)、定义缓存接口和实现类

package com.example.eureka.cache;

public interface CacheService {
    void setData(String key);
}

本地缓存 实现类

package com.example.eureka.cache.impl;

import com.example.eureka.cache.CacheService;

public class LocalServicempl implements CacheService {
    @Override
    public void setData(String key) {
        System.out.println("本地存储数据成功,key="+key);
    }
}

redis缓存实现类

public class RedisServicempl implements CacheService {

    @Override
    public void setData(String key) {
        System.out.println("redis存储数据成功 key= " + key); 
    }
}

2)、定义ImportSelector实现类

以下代码中根据EnableMyCache注解中的不同值来切换缓存的实现类再spring中的注册。

public class MyCacheSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(EnableMyCache.class.getName());
        CacheType  type = (CacheType)annotationAttributes.get("type");

       switch (type){
           case LOCAL:
               return new String[]{
               LocalServicempl.class.getName()
           };
           case REDIS:
               return new String[]{
                       RedisServiceImpl.class.getName()
               };
           default:
               throw new RuntimeException();
       }

    }
    
}

3)、定义注解

package com.example.eureka.cache.Annotion;

import com.example.eureka.cache.Enums.CacheType;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Import({com.example.eureka.cache.MyCacheSelector.class})
public @interface EnableMyCache {
    CacheType type() default  CacheType.REDIS;
}

4)定义枚举

package com.example.eureka.cache.Enums;

public enum CacheType {
    LOCAL,REDIS;
}

三、引入ImportBeanDefinitionRegister的实现类

当配置类实现了 ImportBeanDefinitionRegistrar 接口,你就可以自定义往容器中注册想注入的Bean。
这个接口相比与 ImportSelector 接口的主要区别就是,ImportSelector接口是返回一个类,你不能对这个类进行任何操作,但是 ImportBeanDefinitionRegistrar 是可以自己注入 BeanDefinition,可以添加属性之类的。
1.定义实体

public class UserConfig implements InitializingBean {

    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println(this.username+"-"+this.password);
    }
}

我们通过实现ImportBeanDefinitionRegistrar的方式来完成注入。

package com.example.eureka.config;

import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

@Configuration
@Import({MyImportBean.class})
public class MyImportBean implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(UserConfig.class).addPropertyValue("username", "fengmin").addPropertyValue("password", "12222").getBeanDefinition();
        registry.registerBeanDefinition("userconfig",beanDefinition);

    }
}

四、复杂运行

Mybatis的@MapperScan就是用这种方式实现的,@MapperScan注解,指定basePackages,扫描Mybatis Mapper接口类注入到容器中。

  1. 这里我们自定义一个注解@MyMapperScan来扫描包路径下所以带@MyMapperBean注解的类,并将它们注入到IOC容器中。
package com.example.eureka.exmple;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MyMapperBeanImport.class)
public @interface MyMapperScan {
    /**
     * 扫描包路径
     */
    String[] basePackages() default {};
}

  1. 定义MyMapperBean注解
package com.example.eureka.exmple;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MyMapperBean {

}

  1. 定义ImportBeanDefinitionRegister的实现类
package com.example.eureka.exmple;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;

public class MyMapperBeanImport implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    private final static String PACKAGE_NAME_KEY="basePackages";
    private ResourceLoader resourceLoader;
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MyMapperScan.class.getName()));
        if(annotationAttributes ==null||annotationAttributes.isEmpty() ){
            return;
        }
        String[] basePackages  =(String[]) annotationAttributes.get(PACKAGE_NAME_KEY);
        ClassPathBeanDefinitionScanner scanner=new ClassPathBeanDefinitionScanner(registry,false);
        scanner.setResourceLoader(resourceLoader);
        //路径包含MapperBean的注解的bean
        scanner.addIncludeFilter(new AnnotationTypeFilter(MyMapperBean.class));
        scanner.scan(basePackages);
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
      this.resourceLoader=resourceLoader;
    }
}

  1. 测试
package com.example.eureka.exmple;

import com.example.eureka.exmple.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class TestRunnerApi2 implements ApplicationRunner {
    @Autowired
    private UserMapper userMapper;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        userMapper.select("user");
    }
}

效果如下:
在这里插入图片描述

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@Import注解是Spring中的注解之一,用于导入其他的配置类或者普通的类,从而使它们成为当前配置类中的一个Bean。可以用在@Configuration注解的类中,或者用在普通的类中作为一个注解。 使用方法: 1. 导入配置类:可以将一个或多个@Configuration注解的配置类导入到当前配置类中,以便使用其中的Bean。示例代码如下: ```java @Configuration @Import({Config1.class, Config2.class}) public class AppConfig { // ... } ``` 2. 导入普通类:可以将一个或多个非@Configuration注解的普通类导入到当前配置类中,以便使用其中的Bean。示例代码如下: ```java @Configuration @Import({Service1.class, Service2.class}) public class AppConfig { // ... } ``` 3. 导入ImportSelector实现类:可以将一个实现了ImportSelector接口的类导入到当前配置类中,该类可以动态地选择要导入的类。示例代码如下: ```java @Configuration @Import(MyImportSelector.class) public class AppConfig { // ... } ``` 4. 导入ImportBeanDefinitionRegistrar实现类:可以将一个实现了ImportBeanDefinitionRegistrar接口的类导入到当前配置类中,该类可以动态地注册Bean定义。示例代码如下: ```java @Configuration @Import(MyImportBeanDefinitionRegistrar.class) public class AppConfig { // ... } ``` 5. 导入FactoryBean:可以将一个实现了FactoryBean接口的类导入到当前配置类中,以便使用其中的Bean。示例代码如下: ```java @Configuration @Import(MyFactoryBean.class) public class AppConfig { // ... } ``` 需要注意的是,@Import注解只是将其他的类导入到当前配置类中,而并不会自动将其实例化为Bean。如果需要使用导入的类中的Bean,需要在当前配置类中通过@Bean注解手动创建相应的Bean。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值