如果你看到这篇文章,那就说明你极有可能对@Autowired和Spring的自动注入之间的关系有怀疑了
这里直接给出文章标题结论 --- 两者没有关系。接下来的内容,我将说明为什么两者没有关系。
进入正文之前先提出两个问题:
问题1,在Spring中,依赖注入方式有几种?
我相信这个问题应该难不倒各位。这里给出Spring官网的截图:
浏览器翻译过来是这样的(浏览器翻译仅供参考):
通过Spring官网的说明,我们可以知道依赖注入的方法就是两种:
- 基于构造方法的依赖注入
- 基于setter的依赖注入
其余都是这两种方式的变体!!!(请记住这句话)
问题2,在Spring中,自动注入模式有几种?(我发现很多文章都翻译为自动注入模型,但是英文叫Autowiring mode,我还是觉得翻译为自动注入模式要合理一些,下文都称其为自动注入模式)
这个问题我觉得有一些人就回答不上了,这里还是给出Spring官网的截图:
浏览器翻译过来是这样的(浏览器翻译仅供参考):
通过Spring官网的说明,自动注入模式有四种:
- no
- byName
- byType
- constructor
并且我可以很明确的告诉读者,这四种自动注入模式在Spring源码中是有体现的。
补充知识,在Spring中,其提供了一种描述bean信息的东西,叫做beanDefinition,里面就有一系列描述bean的属性定义,包括:作用域、懒加载、自动注入模式、dependsOn等等(读者可以去了解一下,Spring就是根据beanDefinition的信息去创建一个bean的)
在Spring的AbstractBeanDefinition类中:
/**
* 不自动注入
* Constant that indicates no external autowiring at all.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;
/**
* byName
* Constant that indicates autowiring bean properties by name.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
/**
* byType
* Constant that indicates autowiring bean properties by type.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
/**
* constructor
* Constant that indicates autowiring a constructor.
* @see #setAutowireMode
*/
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;
//默认自动注入模式---no
private int autowireMode = AUTOWIRE_NO; // 这就是beanDefinition描述这个bean的自动注入模式的属性
由上面的Spring源码我们可以知道,四种自动注入模式在源码中的体现就是四个int型的常量(其实还有第五种AUTOWIRE_AUTODETECT,但是目前Spring已经弃用,这里就不再介绍了),对应数值如下:
/**
* Constant that indicates no externally defined autowiring. Note that
* BeanFactoryAware etc and annotation-driven injection will still be applied.
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
int AUTOWIRE_NO = 0;
/**
* Constant that indicates autowiring bean properties by name
* (applying to all bean property setters).
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
int AUTOWIRE_BY_NAME = 1;
/**
* Constant that indicates autowiring bean properties by type
* (applying to all bean property setters).
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
int AUTOWIRE_BY_TYPE = 2;
/**
* Constant that indicates autowiring the greediest constructor that
* can be satisfied (involves resolving the appropriate constructor).
* @see #createBean
* @see #autowire
*/
int AUTOWIRE_CONSTRUCTOR = 3;
这下我们可以更加明确四种自动注入模式了:
- no --------------- 对应数字 0
- byName ------- 对应数字 1
- byType --------- 对应数字 2
- constructor ---- 对应数字 3
有了上面两个问题的铺垫,下面进入文章正题
1、先说明手动注入与自动注入的区别
由xml的形式来说明,至于为什么使用xml来说明,看到后面读者应该就能够明白
手动注入
有如下例子,A类中有B属性,给出set方法,xml中指定A类中属性的依赖,还有一beanFactory后置处理器用于获取类的自动注入模式信息。
A.java
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
public B getB() {
return b;
}
}
B.java
public class B {
}
MyBeanFactoryPostProcessor.java
关于这方面的知识,这里不做展开,不清楚的读者可以去了解一下,其在这里的作用就是用于获取AutowireMode
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 得到 A类的beanDefinition
GenericBeanDefinition a =
(GenericBeanDefinition)beanFactory.getBeanDefinition("a");
System.out.println("A注入模式:" + a.getAutowireMode());
// 得到 B类的beanDefinition
GenericBeanDefinition b =
(GenericBeanDefinition)beanFactory.getBeanDefinition("b");
System.out.println("B注入模式:" + b.getAutowireMode());
}
}
spring.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="a" class="com.cxcxcx.autowired.A">
<property name="b" ref="b"/>
</bean>
<bean id="b" class="com.cxcxcx.autowired.B">
</bean>
<bean id="myBeanFactoryPostProcessor" class="com.cxcxcx.autowired.MyBeanFactoryPostProcessor">
</bean>
</beans>
Test.java
测试类中打印A和A中的b属性
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac =
new ClassPathXmlApplicationContext("classpath:spring.xml");
A a = ac.getBean(A.class);
System.out.println(a);
System.out.println(a.getB()); // 这里打印A中的b属性
}
}
运行结果如下:
我们可以看到A,B正常打印,并且他们的自动注入模式都为0,也就是no(即手动注入)。这就是手动注入
手动注入即需要人为的指定所依赖属性的信息,即xml中的这个配置。
我们手动的指定了A类中的b属性需要依赖的类是名为b的类。
<property name="b" ref="b"/>
这样的缺点也很明显,若是该类有100个属性需要注入,那这个bean就需要写100个<property / >,所以Spring提供了自动注入。
自动注入
还是上面的A,B两类,但是需要修改xml如下:
注意有两个改动:1、注释掉<property />, 2、A的bean添加属性 autowire="byName"
<?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="a" class="com.cxcxcx.autowired.A" autowire="byName">
<!--<property name="b" ref="b"/>-->
</bean>
<bean id="b" class="com.cxcxcx.autowired.B">
</bean>
<bean id="myBeanFactoryPostProcessor" class="com.cxcxcx.autowired.MyBeanFactoryPostProcessor">
</bean>
</beans>
测试类不变,运行结果:
我们看到A,B仍然可以正常打印,即A中的B已经正常注入了,并且A的自动注入模式数值为1,即 byName。
这就是自动注入,我将xml中的 <property name="b" ref="b"/>
注释掉,即我没有手动指定A类中的b属性该由哪个类去注入,并且在A的bean定义中加上参数 autowire="byName"
这样就将A这个bean的自动注入模式修改为了byName。由运行结果也可以看出。
在自动注入的时候,我们只需将类交由Spring管理,然后在类中写明属性,提供set方法,便无需进行其他操作,Spring便自动地将该类中的属性进行注入。
注意:
上面无论是手动注入还是自动注入,我都提供了set方法,还记得文章一开始的第一个问题吗?Spring的依赖注入方法只有两种:基于构造方法的注入和基于setter注入,其余都是这两种的变体
。若是不提供set方法,那么运行抛异常(基于构造方法的手动和自动注入读者可以自行思考下怎么编写xml,其实不难,这里不再说明)
其实文章到这里,我觉得有些读者经过自己的思考可能已经发现了,@Autowired和自动注入其实并没有关系。若是还是有点不清楚,也没关系,请继续看下面的内容
2、@Autowired
我们现在再来看@Autowired这个注解
还是给出一段代码演示
Appconfig.java
@Configuration
@ComponentScan("com.cxcxcx.autowired")
public class Appconfig {
}
A.java
@Component
public class A {
@Autowired
private B b;
public B getB() {
return b;
}
}
B.java
@Component
public class B {
}
MyBeanFactoryPostProcessor.java
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 得到 A类的beanDefinition
GenericBeanDefinition a =
(GenericBeanDefinition)beanFactory.getBeanDefinition("a");
System.out.println("A注入模式:" + a.getAutowireMode());
// 得到 B类的beanDefinition
GenericBeanDefinition b =
(GenericBeanDefinition)beanFactory.getBeanDefinition("b");
System.out.println("B注入模式:" + b.getAutowireMode());
}
}
Test.java
public class Test {
public static void main(String[] args) {
// ClassPathXmlApplicationContext ac =
// new ClassPathXmlApplicationContext("classpath:spring.xml");
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(Appconfig.class);
A a = ac.getBean(A.class);
System.out.println(a);
System.out.println(a.getB());
}
}
运行结果如下:
我们可以看到,即使使用了@Autowired注解,A,B类的自动注入模式并没有改变,都是为no,即手动注入(有些读者可能发现了这句话其实不准确)。其实@Autowired在Spring源码层面是使用的Filed.set 反射的方式完成注入,前面提到过很多次Spring的注入方式只有两种:setter和构造方法,其余都是这两种的变体。@Autowired注解应该算是setter方式的一种变体。
到这里读者应该知道了为什么前面需要使用xml进行演示了吧,我们在使用xml演示自动注入的时候,添加了一行属性autowire="byName"
开启了模式为byName的自动注入,由于作者了解Spring还不够全面深刻,不知道怎么使用注解开启自动注入,若是有了解的读者请在文章下面留言,告诉作者一下。
3、总结
在xml模式下我们开启了A类的自动注入,模式为byName,就是表示A类下的属性都是通过byName去注入,无需手动去写<property / >明确的表明注入关系。所以自动注入就是表示该类下属性的都由某种特定的模式去自动注入属性,无需人为的指定。而这种模式有四种:no、byName、byType和constructor。在使用注解的情况下,我们也还需要手动的添加@Autowired这个注解标识该属性需要注入。
而@Autowired注解,他就只是一个注解,表示该类中的这个属性需要注入,其首先会按类型注入,再按照名称注入。其注入方式是setter方式的一种变体。有没有发现,我们使用了@Autowired注解后就没有再提供set方法了,而之前xml无论手动注入还是自动注入都必须提供set方法才行。
所以@Autowired注解不是Spring的自动注入,并且两者没有任何关系!!!
最后,留下一个问题给读者思考
通过上面的文章,我们知道Spring的自动注入模式在beanDefinition中有描述,所以一个bean是否自动注入在容器初始化的时候便确定了(除非你定义后置处理器去修改它),该bean中的属性就用那种自动注入模式去注入,那么使用@Autowired这个注解去标识一个属性怎么可能会影响到改变一个beanDefinition的属性值设定?
==========================================以下为后来的补充==========================================
我突然想到目前市面上一直说@Autowired注解是自动注入的可能原因了:
原因一:
一说到一个类是自动注入的,这句话其实有两种含义:
- 这个类是需要自动注入到其他类中的
- 这个类中的属性是需要自动注入的
而对于Spring的autowire来说,是哪一种含义呢?明显第二种,这个从xml的例子上就可以看出。
原因二:
后面,我又在Spring官网中注意到了这句话:
浏览器翻译过来是这样的(浏览器翻译仅供参考):
注意,Spring官网中说明的是,@Autowired注解与autowired所最终实现的功能是一样的!!!!我觉得这个是造成让大家觉得@Autowired注解与autowired是一个东西的很大的一个原因。因为最终实现的功能一样,就最终导致了大家产生了这两个是一个东西的误解。