Bean管理
Bean扫描
在Spring中,Bean的扫描有两种写法
第一种是在xml配置文件中用标签扫描
<context:component-scan basepackage="com.cacb"/>
第二种是是注解扫描
@ComponentScan(basePackages="com.cacb")
而在SpringBoot中,我们在启动类中使用了
@SpringBootApplication
注解,该注解实际上包含如下注解
故只要调用该注解Spring就能扫描启动类所在的包及其子包中的Bean,在我们使用时就实现了自动扫描,同时我们也应注意所有的Bean应该放在启动类所在的包及其子包中。
Bean注册
如果要注册的Bean来自第三方(不是自定义的),是无法使用@Component及衍生注解声明Bean的,可以使用如下两个注解
@Bean
譬如,我们在一个jar包中有一个实体类Book,需要在当前工程中导入Book实体,首先我们就需要在启动类中使用@Bean注解来注入Book对象
@SpringBootApplication
public class SpringBootMybatisApplication {public static void main(String[] args) {SpringApplication.run(SpringBootMybatisApplication.class, args);}@Beanpublic Book book(){return new Book();}}
但是这种方法需要在启动类中注解,不推荐,更好的方法是单独创建一个配置类来集中管理注册第三方Bean,该配置类需要在启动类同一个包及其子包中,并且要有@Configuration注解
@Configuration
public class CommonConfig {@Beanpublic Book book(){return new Book();}
}
如果需要改名,例如,要将Book对象名改为user,可以在Bean注释后更改(默认对象名为方法名):
@Bean("user")public Book book(){return new Book();}
如果一个第三方对象内需要注入另一个第三方对象,可以在参数内自动注入,如下例在BookStore中注入Book:
@Beanpublic Book book(){return new Book();}@Beanpublic BookStore bookstore(Book book){return new BookStore();}
@Import
导入配置类
例如,上文所写的CommonConfig不与启动类放在同一个包中,启动类就无法自动扫描到该配置类,此时就需要在启动类中使用@Import来完成手动注解
@SpringBootApplication
@Import(CommonConfig.class)
public class SpringBootMybatisApplication {public static void main(String[] args) {SpringApplication.run(SpringBootMybatisApplication.class, args);}}
注:如果有多个配置类需要注解,可以使用{}数组的形式来完成注解,同时,也可以通过导入ImportSelector接口来完成该工作。
导入ImportSelector接口实现类
例如此时,config文件与启动类不在同一个文件,我们也可以通过导入ImportSelector接口实现类来手动注解。
首先我们需要制作一个ImportSelector接口实现类
public class CommonImportSelector implements ImportSelector {@Overridepublic 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 {@Overridepublic 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 {if(br != null){try {br.close();} catch (IOException e){throw new RuntimeException(e);}}}return imports.toArray(new String[0]);}
}
组合类注解@EnableXxxx注解封装@Import注解
@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容器中
自定义starter
以Mybatisstarter为例
我们需要构建两个模块
在自动配置模块中首先要导入相应依赖坐标
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>3.2.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId><version>3.0.0</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.14</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.3</version><scope>compile</scope></dependency></dependencies>
然后提供自动配置类
@AutoConfiguration
public class MyBatisAutoConfig {@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dataSource);return sqlSessionFactoryBean;}@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(BeanFactory beanFactory){MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();//扫描的包:启动类所在的包及其子包List<String> 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文件可以直接删除掉
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>3.2.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId><version>3.0.0</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.14</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>3.0.3</version><scope>compile</scope></dependency></dependencies>
最重要的是导入我们写的自动装配模块,最好也要将自装配模块所依赖的坐标一并导入