Spring之IOC

image-20230214202811116

Spring的后处理器

  • BeanFactoryPostProcessor
  • BeanPostProcessor

Bean的生命周期

image-20230209203042408

具体可见图解:点击这里

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衍生注解

image-20230214170532453

这些衍生注解都是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进行了注册,对其源码进行跟踪,最终是执行了ClassPathBeanDefinitionScannerdoScan,该方法将@Component标注的类,生成了对应的BeanDefiition,从而注册进Spring容器中。

②配置类上添加ComponentScan注解

使用配置类配置组件扫描时,使用AnnotationConfigApplicationContext进行创建容器时,内部调用了如下代码, 该工具注册了几个Bean后处理器:

AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);

image-20230216132739607

​ 其中,ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor ,经过一系列源码调用,最终也指到了 ClassPathBeanDefinitionScanner doScan 方法,与xml方式最终终点一致。

image-20230216131529204

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>

主要是依靠SqlSessionFactoryBeanMapperScannerConfigurer来进行整合的:

  • 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,与前面相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

无知猫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值