Spring的后处理器
- BeanFactoryPostProcessor
- BeanPostProcessor
Bean的生命周期
具体可见图解:点击这里
Bean的加载方式
1.xml方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--声明自定义bean-->
<bean id="bookService" class="com.azy.service.impl.BookServiceImpl"
scope="singleton"/>
<!--声明第三方开发bean-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/>
</beans>
2.xml文件+Component注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--扫描bean-->
<context:component-scan base-package="com.azy"/>
</beans>
类上面加上@Component注解
@Component
//@Service,@Controller,@Repository为@Component衍生注解,效果相同
public class BookServiceImpl implements BookService {
}
//第三方Bean
@Component
public class DruidBean {
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("");
dataSource.setUrl("");
dataSource.setUsername("");
dataSource.setPassword("");
return dataSource;
}
}
3.纯注解
//配置类:
//注解开发,替换配置文件xml文件
@Configuration
@ComponentScan("com.azy") //包扫描
public class SpringConfig {
}
扩展:
-
FactoryBean
接口产生Bean -
ImportSource
注解可以加载旧的配置文件@Configuration @ComponentScan("com.azy") //包扫描 @ImportResource("applicationContext-config.xml") public class SpringConfig { }
-
Configuration
注解默认有个参数proxyBeanMethods=true
可以保障调用此方法得到的对象是从容器中获取的而不是重新创建的
4.Import注解导入
//配置类:
//注解开发,替换配置文件xml文件
@Configuration
@ComponentScan("com.azy") //包扫描
@Import(DruidBean.class)
public class SpringConfig {
}
Import可以导入普通类或者配置类,导入的普通类或者配置类不用再加@Component注解:
//第三方Bean
//@Component
public class DruidBean {
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("");
dataSource.setUrl("");
dataSource.setUsername("");
dataSource.setPassword("");
return dataSource;
}
}
5.Spring容器注册
public class AppImport {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(SpringConfig.class);
ctx.register(Cat.class);//注册
//……
}
}
6.Import接口
ImportSelector
接口:
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[0];
}
}
//可以根据importingClassMetadata参数来判断导入该实现类所在的类的条件,
//从而返回要进行创建的Bean的类名
ImportBeanDefinitionRegistrar
接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
//通过BeanDefinition的注册器注册实名bean,实现对容器中bean的裁定,
//例如对现有bean的覆盖,进而达成不修改源代码的情况下更换实现的效果
BeanDefinitionRegistryPostProcessor
接口
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = BeanDefinitionBuilder
.rootBeanDefinition(BookServiceImpl.class)
.getBeanDefinition();
registry.registerBeanDefinition("bookService", beanDefinition);
}
}
//通过BeanDefinition的注册器注册实名bean,实现对容器中bean的最终裁定
Bean的加载控制
1.Spring容器注册
2.Import注解导入接口
ImportSelector
接口ImportBeanDefinitionRegistrar
接口BeanDefinitionRegistryPostProcessor
接口
3.Conditional衍生注解
这些衍生注解都是SpringBoot实现的
@Component
//@ConditionalOnClass(Book.class)
//ConditionalOnClass可以直接导入类,但是ConditionalOnMissingClass不可以,建议使用name
@ConditionalOnClass(name="com.azy.pojo.Book")//有这个类才加载下面的BookService
@ConditionalOnMissingClass("com.azy.pojo.Book")//没有这个类才加载下面的BookService
public class BookServiceImpl implements BookService {
}
第三方Bean:
//有数据库驱动的时候才加载DataSource
@Component
public class DruidBean {
@Bean
@ConditionalOnClass(name="com.mysql.jdbc.Driver")
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("");
dataSource.setUrl("");
dataSource.setUsername("");
dataSource.setPassword("");
return dataSource;
}
}
Spring注解的解析原理
Spring注解的解析主要包含两种方式(即Bean的加载方式中的2和3):
①xml文件+Component注解:
<context:component-scan base-package="com.azy"/>
使用xml方式配置组件进行扫描时,由于component-scan
是一个context
命名空间下的自定义标签,所以要找到对应的命名空间处理器NamespaceHandler
和解析器,查看spring-context
包下的spring.handlers
文件:
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
其中调用了ContextNamespaceHandler
类,查看这个类:
public void init() {
this.registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
}
在这个类中将ComponentScanBeanDefinitionParser
进行了注册,对其源码进行跟踪,最终是执行了ClassPathBeanDefinitionScanner
的doScan
,该方法将@Component标注的类,生成了对应的BeanDefiition
,从而注册进Spring容器中。
②配置类上添加ComponentScan
注解
使用配置类配置组件扫描时,使用AnnotationConfigApplicationContext
进行创建容器时,内部调用了如下代码, 该工具注册了几个Bean后处理器:
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
其中,ConfigurationClassPostProcessor
实现了 BeanDefinitionRegistryPostProcessor
,经过一系列源码调用,最终也指到了 ClassPathBeanDefinitionScanner
的 doScan
方法,与xml方式最终终点一致。
Spring整合Mybatis原理
①xml方式整合:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value=""/>
<property name="username" value=""/>
<property name="password" value=""/>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.azy.ioc.mapper"/>
</bean>
</beans>
主要是依靠SqlSessionFactoryBean
和MapperScannerConfigurer
来进行整合的:
-
SqlSessionFactoryBean
的作用是向容器中提供SqlSessionFactory
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { //这里实现了FactoryBean接口,getObject方法获取Bean public void afterPropertiesSet() throws Exception { //创建SqlSessionFactory对象 this.sqlSessionFactory = this.buildSqlSessionFactory(); } public SqlSessionFactory getObject() throws Exception { return this.sqlSessionFactory; } }
-
MapperScannerConfigurer
的作用是扫描Mapper,向容器中注册Mapper对应的MapperFactoryBean
://MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor,在postProcessBeanDefinitionRegistry方法中利用ClassPathMapperScanner向容器中注册MapperFactoryBean class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean{ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n")); } } //ClassPathMapperScanner继承了ClassPathBeanDefinitionScanner class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { public Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { } else { this.processBeanDefinitions(beanDefinitions); } } private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { //设置Mapper的beanClass是org.mybatis.spring.mapper.MapperFactoryBean definition.setBeanClass(this.mapperFactoryBeanClass); definition.setAutowireMode(2); //设置MapperBeanFactory 进行自动注入 } }
MapperFactoryBean
的作用就是产生对应的Mapper,其底层实际上还是调用的Mybatis的方法:public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { this.sqlSessionTemplate = this.createSqlSessionTemplate(sqlSessionFactory); } public T getObject() throws Exception { return this.getSqlSession().getMapper(this.mapperInterface); }//这里getSqlSession利用自动注入,然后再利用sqlSession调用getMapper }
②注解方式整合:
@Component
@MapperScan("com.azy.annotation.mapper")
public class MybatisConfig {
@Bean //形参可以自动装配
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setTypeAliasesPackage("com.azy.annotation.pojo");
sqlSessionFactory.setDataSource(dataSource);
return sqlSessionFactory;
}
}
这里主要看MapperScan
注解,它导入了MapperScannerRegistrar
:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
}
MapperScannerRegistrar
实现了ImportBeanDefinitionRegistrar
接口:
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
}
其中的registerBeanDefinitions
方法向容器中注册了Mapper对应的MapperFactoryBean
,与前面相同。