[java SpringBoot]Bean对象注册

前置条件:

创建一个Spring项目,有Spring框架的支持

Spring中启动类的@SpringBootApplication实现了包扫描。在Spring项目构建时,Spring会扫描所在目录下的包,扫描包下@Component注解管理的Bean对象,将Bean对象添加到IOC容器中。

好处是什么呢?

在项目中如果使用到这个Bean对象,可以直接通过依赖注入来使用,而不是需要一个一个new出来。

通过下述代码,我们在操作数据库的时候,可以直接通过@Autowired依赖注入来调用Mapper的方法,非常的方便。

// @Mapper注解是@Component注解的拓展,同样可以实现标记Bean对象的作用
@Mapper
public interface DeptLogMapper {
    // 实现操作后插入日志到数据库
    @Insert("insert into dept_log(create_time, description) values (#{createTime}, #{description})")
    void insert(DeptLog deptLog);
}




@Service
public class DeptLogServiceImpl implements DeptLogService {
    // 依赖注入
    @Autowired
    DeptLogMapper deptLogMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insert(DeptLog deptLog) {
        deptLogMapper.insert(deptLog);
    }
}

但是通常启动类只能扫描当前包下的Bean对象,如果是第三方的Bean对象,又该如何添加到IOC容器中注册为Bean对象呢?

假如我想这个第三方包中的Country和Province导入。是否直接在类上添加@Component注解就可以呢?

而通常第三方的包都是只读文档,所以这种方法是局限性很大的。

通过@Bean实现第三方扫描Bean对象

我们测试一下在启动类中使用@Bean注解

@SpringBootApplication
public class SpringbootRegisterApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringbootRegisterApplication.class, args);
        Country country = context.getBean(Country.class);
        System.out.println(country);
    }
    
    @Bean
    public Country country() {
        return new Country();
    }
}

我们通过@Bean注解获得Country函数的返回值,将返回值添加到IOC容器中,注册为Bean对象。

通过getBean来获得

查看Bean对象(控制台右边),发现country已经被注册了。

但是在启动类注册Bean不符合单一功能原则。启动类就是启动用的,而不应该掺杂注册的功能。

// 配置类
@Configuration
public class CommonConfig {
    @Bean
    public Country country() {
        return new Country();
    }
    // 可以通过参数的形式,访问之前注册的Bean
    @Bean
    public Province province(Country country) {
        System.out.println(country);
        return new Province();
    }
}



// 启动类
@SpringBootApplication
@Import(CommonConfig.class)
public class SpringbootRegisterApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringbootRegisterApplication.class, args);
        Country country = context.getBean(Country.class);// 通过类名获取
        System.out.println(country);
        System.out.println(context.getBean("province"));// 通过名字获取,默认为类名
    }
}

新建一个CommonConfig使用@Configuration注解

通过Import导入,获取Configuration中的Bean对象注册。

这样就不用在启动类中写Bean了。

但是如果我要导入很多的Bean呢?这样会造成在启动类中Import一个很长的都是类名的数组。代码看起来十分臃肿。

可以通过重写ImportSelector中的selectImports方法来集中注册
// ImportSelector 
public class CommonImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return String[]{"config.CommonConfig"};
    }
}



// 启动类
@SpringBootApplication
@Import(CommonImportSelector.class)
public class SpringbootRegisterApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringbootRegisterApplication.class, args);
        Country country = context.getBean(Country.class);
        System.out.println(country);
        System.out.println(context.getBean("province"));
    }
}

现在又出现了一个问题,要导入的Bean都在CommonImportSelector 中,这会使得代码耦合,如果要增加还需要去修改CommonImportSelector

所以不妨直接把所有要注册的Bean都添加到配置文件中

public class CommonImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 读取配置文件的内容
        List<String> imports = new ArrayList<>();
        InputStream is = CommonImportSelector.class.getClassLoader().getResourceAsStream("common.imports");
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String line = null;
        try {
            while ((line = br.readLine()) != null) {
                imports.add(line);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        finally {
            try {
                br.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return imports.toArray(new String[0]);
    }
}

这样的话,只需要修改配置文件就可以了。

还有个更加优雅的写法

自定义一个注释

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(CommonImportSelector.class)
public @interface EnableCommonConfig {

}

在启动类添加这个Enable注释

@SpringBootApplication
@EnableCommonConfig
public class SpringbootRegisterApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SpringbootRegisterApplication.class, args);
        Country country = context.getBean(Country.class);
        System.out.println(country);
        System.out.println(context.getBean("province"));
    }
}

启动类的导入其实就是这样写的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LinSir_JNU

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

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

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

打赏作者

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

抵扣说明:

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

余额充值