spring的自动装配永远都是开启的,只是默认模式是no罢了
spring的自动装配从来都不是在需要注入的地方加上注解,这一点可以从基于xml的方式注入来看出,只需要设置default-autowire
属性就够了,默认是no模式
下面是4种模式的官方解释
如果是使用非xml方式来配置spring那么就无法设置这个自动装配的模式了,这样就默认是no模式,于是spring提供了注解的方式协助自动装配,在no模式下只要加上例如@Autowired、@Resource注解也可以完成自动装配。
可是我们要记住这种只是no模式下的自动装配,从来不是byType、byName或者constructor模式下的自动装配。
但是我们同样也可以通过javaConfig开启某一个bean的自动装配,示例如下
加入有如下两个bean对象
@Component
public class AutoTest1 {
private AutoTest2 autoTest2;
public AutoTest2 getAutoTest2() {
return autoTest2;
}
public void setAutoTest2(AutoTest2 autoTest2) {
this.autoTest2 = autoTest2;
}
}
@Component
public class AutoTest2 {
private AutoTest1 autoTest1;
public AutoTest1 getAutoTest1() {
return autoTest1;
}
public void setAutoTest1(AutoTest1 autoTest1) {
this.autoTest1 = autoTest1;
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
AutoTest1 autoTest1 = annotationConfigApplicationContext.getBean(AutoTest1.class);
AutoTest2 autoTest2 = annotationConfigApplicationContext.getBean(AutoTest2.class);
/**
* 如果什么都没设置那么spring的自动装配的模式是no
* 自然就得不到对象中的属性
*/
System.out.println("autoTest1&&AutoTest2===========>"+autoTest1.getAutoTest2());
System.out.println("autoTest2&&AutoTest1===========>"+autoTest2.getAutoTest1());
}
autoTest1&&AutoTest2===========>null
autoTest2&&AutoTest1===========>null
这样下来自然得到的是null
但是如果我们通过ImportBeanDefinitionRegistrar接口的实现类改变bd自动装配的模式
public class AutoImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//拿到gbd
GenericBeanDefinition autoTest1 = (GenericBeanDefinition) registry.getBeanDefinition("autoTest1");
//设置gbd的自动装配模式为byType
autoTest1.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
//重新注册bd
registry.registerBeanDefinition("autoTest1",autoTest1);
}
}
再次运行测试代码我们会发现autoTest1对象中已经完成了自动装配
autoTest1&&AutoTest2===========>com.zdd.spring.AutoDistribution.AutoTest2@105fece7
autoTest2&&AutoTest1===========>null
不过如果我们去掉set方法依然不能自动装配,因为byType模式的自动装配就是通过set方法来实现的(byName也同样需要set方法)
如果是注解就不需要借助set方法
自动装配会忽略的属性
这里讨论的是byType模式的自动装配,下面这个方法就是底层判断自动装配的属性是否被忽略,返回的就是真正需要自动装配的方法,传进来的BeanWrapper包含了类的所有属性(包括父类)
protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
Set<String> result = new TreeSet<>();
PropertyValues pvs = mbd.getPropertyValues();
PropertyDescriptor[] pds = bw.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
result.add(pd.getName());
}
}
return StringUtils.toStringArray(result);
}
spring会把一个对象的所有属性(包括父类的)都拿出来判断是否要自动装配,这里的属性是只要有get方法(Boolean类型的get方法是isXXX方法)或者set方法的所有属性。
首先判断有没有set方法,没有就不会自动装配
其次会将设置了被忽略的属性忽略掉,这里忽略的属性可以通过一个接口
如果在bd中设置了无需自动装配,已经设置了属性值的属性也会被排除
最后简单类型也会被排除
这里包含了六种简单类型
在mybatis中也是借助了spring的自动装配来将之前所说的SqlSessionTemplate注入到每一个mapperbean中的
mybatis在@MapperScan注解中同样也注入了一个MapperScannerRegistrar对象,它也实现了ImportBeanDefinitionRegistrar接口
mybatis也是在这个方法中将所有MapperFactoryBean的bd的自动装配模式设置为byType的
Mybatis总结
spring在实例化mapperbean的时候将方法的信息(包括注解)解析出来,并且把这个元数据放到一个map中,key是类全名加方法名。这个map存在sqlsession中,叫做MappedStatments。这样在mapperbean真正执行方法时就可以从其中的sqlSessionTemplate中拿到map,通过类全名加方法名得到元数据,拿到真正的sql语句,从数据库中拿到数据或者修改数据,底层依然是JDBC。
这里需要注意的就是sqlSession的实现类DefaultSqlSession被sqlSessionTemplate封装,并且sqlSessionTemplate被代理,在代理方法中最后非关闭session。
还有就是注入的mapper是MapperFactoryBean,只是返回的是mapper的代理对象。
还有就是在MapperFactroyBean中注入sqlSessionTemplate是使用了spring的自动装配,借助了ImportBeanDefinitionRegistrar接口开启了byType模式的自动装配。无需加上spring的注解注入,这样mybatis的源码就不会被spring的代码侵蚀,减少了耦合度。