Spring5.0.x源码分析(三)

在第二节中讲到ConfigurationClassParser#doProcessConfigurationClass()方法中在处理一些注解,这节就来分析一下@Import注解

一、 @Import 注解的基本用法

1、用来注册一个Bean

public class MyBean {
    public void info(){
        System.out.println("info");
    }
}

@Configuration
@Import(MyBean.class)
public class AppConfig {
    
}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MyBean myBean = (MyBean) context.getBean("com.liaoxiang.bean.MyBean");
        myBean.info();
    }
}
执行方法输出:info

2、导入一个配置类

@Configuration
public class BeanConfig {
    @Bean
    public MyBean getMyBean(){
        return new MyBean();
    }
}

@Configuration
@Import(BeanConfig.class)
public class AppConfig {

}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MyBean myBean = context.getBean(MyBean.class);
        myBean.info();
    }
}
同样输出:info
如果是用bean的名称来获取,这里要用@Bean注解的方法名

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MyBean myBean = (MyBean) context.getBean("getMyBean");
        myBean.info();
    }
}
同样输出:info

在这里插入图片描述
3、导入ImportSelector的实现类

public class MyBean {
    public void info(){
        System.out.println("info");
    }
}

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //return new String[]{MyBean.class.getName()};
        return new String[]{"com.liaoxiang.bean.MyBean"};
    }
}

@Configuration
@Import(MyImportSelector.class)
public class AppConfig {

}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MyBean bean = context.getBean(MyBean.class);
        bean.info();
    }
}
输出:info

在这里插入图片描述
4、导入ImportBeanDefinitionRegistrar的实现类

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //得到bd
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MyBean.class);
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
        beanDefinitionRegistry.registerBeanDefinition("myBean", beanDefinition);
    }
}

@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfig {

}

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        //MyBean bean = context.getBean(MyBean.class);
        MyBean bean = (MyBean)context.getBean("myBean");
        bean.info();
    }
}
输出:info
此时bean的名称是我们指定的myBean

二、@Import在MyBatis@MapperScan注解的使用

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.3</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.21</version>
        </dependency>
    </dependencies>
public class Account{
    private Integer id;
    private String name;
    private Double money;
}
//===========================================================================
public interface AccountMapper {
    @Select("select * from account")
    List<Account> findAll();
}
//===========================================================================
@Service
public class AccountService {
    @Autowired
    private AccountMapper accountMapper;

    public List<Account> find(){
        return accountMapper.findAll();
    }
}
//===========================================================================
@Configuration
@ComponentScan("com.liaoxiang")
@MapperScan("com.liaoxiang.mapper")
public class AppConfig {

    @Bean
    @Autowired
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setPassword("123456");
        driverManagerDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring");
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return driverManagerDataSource;
    }
}
//===========================================================================
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        AccountService service = context.getBean(AccountService.class);
        System.out.println(service.find());
    }
}
//===========================================================================
[Account{id=1, name='aaa', money=1000.0}, Account{id=2, name='bbb', money=1000.0}, Account{id=9, name='ddd', money=1000.0}, Account{id=10, name='ccc', money=1500.0}]

现在我们注释掉@MapperScan("com.liaoxiang.mapper"),由于在AccountService中我们注入了AccountMapper 类型的Bean,要实现这个功能,首先我们要有一个实现了AccountMapper的类,让后还要放入容器中,这时就能够注入成功

public class MyFactoryBean implements FactoryBean, InvocationHandler {

    private Class clazz;

    /**
     * spring容器在实例化这个对象的时候,会根据clazz这个名字去找对应的类型
     * @param clazz
     */
    public MyFactoryBean(Class clazz) {
        this.clazz = clazz;
    }
    /**
     * 根据构造方法传递进来的类型,创建一个代理对象返回
     * @return
     * @throws Exception
     */
    @Override
    public Object getObject() throws Exception {
        Class[] classes = {this.clazz};
        Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), classes, this);
        return proxy;
    }

    @Override
    public Class<?> getObjectType() {
        return this.clazz;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy");
        return null;
    }
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(AccountMapper.class);
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
        beanDefinition.setBeanClass(MyFactoryBean.class);
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("com.google.mapper.AccountMapper");
        beanDefinitionRegistry.registerBeanDefinition("accountMapper", beanDefinition);
    }
}
@Configuration
@ComponentScan("com.google")
//@MapperScan("com.google.mapper")
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfig {

    @Bean
    @Autowired
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setPassword("123456");
        driverManagerDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring");
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return driverManagerDataSource;
    }
}
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        AccountService service = context.getBean(AccountService.class);
        System.out.println(service.find());
    }
}
运行结果:
proxy
null

自定义一个注解@MyScan,模拟@MapperScan

@Import(MyImportBeanDefinitionRegistrar.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyScan {
}
//====================================================================================
@Configuration
@ComponentScan("com.google")
//@MapperScan("com.google.mapper")
//@Import(MyImportBeanDefinitionRegistrar.class)
@MyScan
public class AppConfig {

    @Bean
    @Autowired
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public DataSource dataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setPassword("123456");
        driverManagerDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring");
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return driverManagerDataSource;
    }
}
运行结果同上

获取sql语句

public class MyFactoryBean implements FactoryBean, InvocationHandler {

    private Class clazz;

    /**
     * spring容器在实例化这个对象的时候,会根据clazz这个名字去找对应的类型
     * @param clazz
     */
    public MyFactoryBean(Class clazz) {
        this.clazz = clazz;
    }
    /**
     * 根据构造方法传递进来的类型,创建一个代理对象返回
     * @return
     * @throws Exception
     */
    @Override
    public Object getObject() throws Exception {
        Class[] classes = {this.clazz};
        Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), classes, this);
        return proxy;
    }

    @Override
    public Class<?> getObjectType() {
        return this.clazz;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取到实现的接口,拿到接口里的方法
        Method interfaceMethod = proxy.getClass().getInterfaces()[0].getMethod(method.getName());
        System.out.println("执行的方法:"+interfaceMethod.toString());
        // 获取到方法上的注解
        Select select = interfaceMethod.getDeclaredAnnotation(Select.class);
        // 获取到注解里的sql语句
        String sql = select.value()[0];
        System.out.println(sql);
        // 执行sql语句,返回数据。。。
        return null;
    }
}
执行的方法:public abstract java.util.List com.google.mapper.AccountMapper.findAll()
select * from account
null

三、Spring解析@Import注解

1、@Import(Xxx.class)
public class MyBean {}
//==============================================================================
@Configuration
@ComponentScan("com.google")
@Import(MyBean.class)
public class AppConfig {

}
//==============================================================================
public class Test {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
		ac.register(AppConfig.class);
		ac.refresh();
		MyBean myBean = ac.getBean(MyBean.class);
		System.out.println(myBean);
	}
}

我们从ConfigurationClassPostProcessor#processConfigBeanDefinitions开始看起,这方法在上一节中已经给出说明
在这里插入图片描述
经过几个中间调用后,进入ConfigurationClassParser#processConfigurationClass(),这个在上一节中已提出,首先解析AppConfig这个类
在这里插入图片描述
进入ConfigurationClassParser#doProcessConfigurationClass(),通过上一节我们知道就是在这个方法里处理AppConfig这个类的所有注解,上一节处理了@ComponentScan,这里直接跳到此处开始处理AppConfig类的@Import注解
在这里插入图片描述
进入ConfigurationClassParser#processImports
在这里插入图片描述
判断MyBean的类型
在这里插入图片描述
在这里插入图片描述
不是ImportSelector和ImportBeanDefinitionRegistrar类型
在这里插入图片描述
在上面开始递归调用ConfigurationClassParser#processConfigurationClass()
在这里插入图片描述
进入ConfigurationClassParser#doProcessConfigurationClass()
在这里插入图片描述
在这里插入图片描述
返回ConfigurationClassParser#doProcessConfigurationClass()然后又在此方法中返回到
ConfigurationClassParser#processConfigurationClass
在这里插入图片描述
将MyBean的相关信息放入到一个名字为configurationClasses的Map中
在这里插入图片描述
执行上面代码之后,跳回到这里,因为实在这里开始的递归,向下执行之后返回
在这里插入图片描述
返回到最开始解析AppConfig类注解的地方,到此AppConfig类的@Import注解就解析完成
在这里插入图片描述
最后,将AppConfig也放入到上面的Map中
在这里插入图片描述
继续执行后就回到ConfigurationClassPostProcessor#processConfigBeanDefinitions,往下可以看到这两个类的信息
在这里插入图片描述
向下执行,进入下面的方法
在这里插入图片描述
ConfigurationClassBeanDefinitionReader#loadBeanDefinitions(),循环传递进来的集合,调用loadBeanDefinitionsForConfigurationClass()方法
在这里插入图片描述
ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
在这里插入图片描述
ConfigurationClassBeanDefinitionReader#registerBeanDefinitionForImportedConfigurationClass()

/**
 * Register the {@link Configuration} class itself as a bean definition.
 */
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
	AnnotationMetadata metadata = configClass.getMetadata();
	AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

	// 获取并设置作用域
	ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
	configBeanDef.setScope(scopeMetadata.getScopeName());
	// 生成Bean的名字
	String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
	AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	// 注册到工厂
	this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
	configClass.setBeanName(configBeanName);

	if (logger.isDebugEnabled()) {
		logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
	}
}

到这里@Import的MyBean这个类的BD就被放到了工厂中

2、@Import(ImportSelector.class)
public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //return new String[]{MyBean.class.getName()};
        return new String[]{"com.google.bean.MyBean"};
    }
}
//===============================================================================
@Configuration
@ComponentScan("com.google")
@Import(MyImportSelector.class)
public class AppConfig {

}

到这里跟上面第一种情况类似,在ConfigurationClassParser#processImports()
在这里插入图片描述
在这里递归调用ConfigurationClassParser#processImports()
在这里插入图片描述
再次进入processImports(),判断MyBean@Import中的哪种情况
在这里插入图片描述
在这里插入图片描述
到这里之后,就跟第一种情况一样了,递归processConfigurationClass()之后又回到这里
在这里插入图片描述
跳出processImports()的递归
在这里插入图片描述
最后跟第一种情况一样将两个bean信息放入到Map中,MyBean的注册方式和第一种一样

3、@Import(ImportBeanDefinitionRegistrar.class)
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //得到bd
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MyBean.class);
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
        beanDefinitionRegistry.registerBeanDefinition("myBean", beanDefinition);
    }
}
//==================================================================================
@Configuration
@ComponentScan("com.google")
@Import(MyImportBeanDefinitionRegistrar.class)
public class AppConfig {

}

跟上面情况类似
在这里插入图片描述
在这里插入图片描述
进入ConfigurationClass#addImportBeanDefinitionRegistrar,放入到本类的名叫importBeanDefinitionRegistrars的Map中
在这里插入图片描述
执行完之后回到ConfigurationClassParser#processConfigurationClass,将AppConfig放入configurationClasses
在这里插入图片描述
来到ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass,这个方法在第一种情况提到
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值