在第二节中讲到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
,这个方法在第一种情况提到