在Spring中,Bean的扫描有两种写法
第一种是在xml配置文件中用标签扫描
第二种是是注解扫描
@ComponentScan(basePackages="com.cacb")
而在SpringBoot中,我们在启动类中使用了
@SpringBootApplication
注解,该注解实际上包含如下注解
故只要调用该注解Spring就能扫描启动类所在的包及其子包中的Bean,在我们使用时就实现了自动扫描,同时我们也应注意所有的Bean应该放在启动类所在的包及其子包中。
如果要注册的Bean来自第三方(不是自定义的),是无法使用@Component及衍生注解声明Bean的,可以使用如下两个注解
譬如,我们在一个jar包中有一个实体类Book,需要在当前工程中导入Book实体,首先我们就需要在启动类中使用@Bean注解来注入Book对象
@SpringBootApplication
public class SpringBootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMybatisApplication.class, args);
}
@Bean
public Book book(){
return new Book();
}
}
但是这种方法需要在启动类中注解,不推荐,更好的方法是单独创建一个配置类来集中管理注册第三方Bean,该配置类需要在启动类同一个包及其子包中,并且要有@Configuration注解
@Configuration
public class CommonConfig {
@Bean
public Book book(){
return new Book();
}
}
如果需要改名,例如,要将Book对象名改为user,可以在Bean注释后更改(默认对象名为方法名):
@Bean("user")
public Book book(){
return new Book();
}
如果一个第三方对象内需要注入另一个第三方对象,可以在参数内自动注入,如下例在BookStore中注入Book:
@Bean
public Book book(){
return new Book();
}
@Bean
public BookStore bookstore(Book book){
return new BookStore();
}
例如,上文所写的CommonConfig不与启动类放在同一个包中,启动类就无法自动扫描到该配置类,此时就需要在启动类中使用@Import来完成手动注解
@SpringBootApplication
@Import(CommonConfig.class)
public class SpringBootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMybatisApplication.class, args);
}
}
注:如果有多个配置类需要注解,可以使用{}数组的形式来完成注解,同时,也可以通过导入ImportSelector接口来完成该工作。
例如此时,config文件与启动类不在同一个文件,我们也可以通过导入ImportSelector接口实现类来手动注解。
首先我们需要制作一个ImportSelector接口实现类
public class CommonImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.cacb.config.CommonConfig"};
}
}
然后再去启动类中使用@Import注解来完成手动注解
@SpringBootApplication
@Import(CommonImportSelector.class)
public class SpringBootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMybatisApplication.class, args);
}
}
当然,需要注入的Bean一般是从配置文件中读取的,方法如下,先构建配置文件
注:每行指书写一个Bean的全类名
com.cacb.config.CommonConfig
然后去接口实现类中加载配置文件
public class CommonImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
List 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 {
if(br != null){
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 {
}
完成后就可以在启动类中使用@EnableCommonConfig注解来代替@Import注解使用
@SpringBootApplication
@EnableCommonConfig
public class SpringBootMybatisApplication {
SpringBoot提供了设置注册生效条件的注解@Conditional
自动配置
遵循约定大于配置的原则,在boot程序启动后,起步依赖中的一些Bean对象会自动注入到IOC容器
程序引入spring-boot-starter-web起步依赖,启动后,会自动往IOC容器中注入DispatcherServlet
在主启动类上添加了SpringBootApplication注解,这个注解组合了EnableAutoConfiguration注解
EnableAutoConfiguration注解又组合了Import注解,导入了AutoConfigurationImportSelector类
实现selectImports方法,这个方法经过多层调用,最终会读取META-INF目录下的后缀名为imports的文件,boot2.7以前的版本读取的是spring.factories文件
读取到imports文件中的全类名之后,会解析注册条件,也就是@Conditional及其衍生注解,把满足注册条件的Bean对象自动注入到IOC容器中
以Mybatisstarter为例
我们需要构建两个模块
在自动配置模块中首先要导入相应依赖坐标
org.springframework.boot
spring-boot-starter
3.2.2
org.springframework.boot
spring-boot-starter-data-jdbc
3.0.0
org.mybatis
mybatis
3.5.14
junit
junit
3.8.1
test
org.mybatis
mybatis-spring
3.0.3
compile
然后提供自动配置类
@AutoConfiguration
public class MyBatisAutoConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(BeanFactory beanFactory){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
//扫描的包:启动类所在的包及其子包
List packages = AutoConfigurationPackages.get(beanFactory);
String p = packages.get(0);
mapperScannerConfigurer.setBasePackage(p);
//扫描的注解
mapperScannerConfigurer.setAnnotationClass(Mapper.class);
return mapperScannerConfigurer;
}
}
自建import文件,并将自动配置类完整路径写入
注意imports文件存储路径一定要在META-INF.spring下
com.cacb.config.MyBatisAutoConfig
下面开始配置stater模块,该模块只需要导入相应坐标,故src文件可以直接删除掉
org.springframework.boot
spring-boot-starter
3.2.2
org.springframework.boot
spring-boot-starter-data-jdbc
3.0.0
org.mybatis
mybatis
3.5.14
junit
junit
3.8.1
test
org.mybatis
mybatis-spring
3.0.3
compile
最重要的是导入我们写的自动装配模块,最好也要将自装配模块所依赖的坐标一并导入