文章目录
相关文章:
Spring中有几种配置方式(xml、注解<jsr250>、JavaConfig)讲述了spring的三种配置形式
【spring注解】context:annotation-config和context:component-scan区别 解释了开启注解形式的配置语法,本质就是BeanFactoryPostProcessor和BeanPostProcessor的具体应用
【spring】Spring的BeanFactoryPostProcessor和BeanPostProcessor BeanFactoryPostProcessor和BeanPostProcessor理论知识
背景知识
结合bean实例化的周期图来看下context:annotation-config和context:component-scan的作用:
-
在实例化bean之前,也会有个BeanFactoryPostProcessor接口实例的链式集合,用于处理bean定义;
-
bean的定义固化下来后,就进行new 实例化的操作
-
在new 的过程中,就会有很多步骤,例如实现了Aware接口实例的一组集合,类似链式,逐一处理一遍,接着是实现了BeanPostProcessor接口实例的一组集合,类似链式,逐一处理一遍
重点来了,这个预置的处理链时是怎么生成的?答案就是 在一个项目启动时,会预先加载一些bean定义,这些bean根据类型,会加入不同的处理链,而我们本篇文章的核心语法 context:annotation-config和context:component-scan就是在其定义的范围内的,如果实现了BeanFactoryPostProcessor和BeanPostProcessor,这些bean 就被当做处理器,加入集合。
前言
Spring 框架对 Bean 进行装配提供了很灵活的方式,下面归纳一下主要的方式:
- 在 XML 中进行显示配置
- 在 Java 中进行显示配置
- 隐式的 bean 发现机制和自动装配
注解装配在 Spring 中是默认关闭的。所以需要在 Spring 文件中配置一下才能使用基于注解的装配 模式。
而自动装配就需要注解扫描,这里有两种开启注解扫描的方式,即
<context:annotation-config/>
和
<context:component-scan>
下面归纳一下这两种注解方式的异同点:
-
<context:annotation-config/>
注解扫描是针对已经在 Spring 容器里注册过的 Bean,即特定的一些后置bean,能完成部分@标签的解析功能,但是还有部分功能不能用,作用有限
。 -
<context:component-scan/>
不仅具备<context:annotation-config/>
的所有功能,还可以在指定的 package 下面扫描对应的 bean
Spring 中在使用注解(Annotation)会涉及到< context:annotation-config>
和 < context:component-scan>
配置,下面就对这两个配置进行诠释。
1. @Autowired注解原理
我们通过例子,来演示< context:annotation-config>
和 < context:component-scan>
的作用,没有这些配置,注解符不会生效。
1.1 @Autowired不生效演示
我们通过在xml中定义一个Driver 类型的bean ,然后在bean中通过@Autowired注解,希望自动注入一个car类型的bean,当然此时不会生效的,因为识别不了@Autowired标签。
package com.test.springaop;
public class Car {
public Car() {
}
private String name ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Driver 有个属性,配置 @Autowired注解:
package com.test.springaop;
import org.springframework.beans.factory.annotation.Autowired;
public class Driver {
private String name;
@Autowired
private Car car;
public void setCar(Car car) {
this.car = car;
}
public void setName(String driver) {
this.name = driver;
}
public Car getCar() {
return this.car;
}
}
test-scan.xml,配置了driver和car的2个bean:
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="car" class="com.test.springaop.Car"/>
<bean id="driver" class="com.test.springaop.Driver"/>
</beans>
main方法执行类:
package com.test.springaop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("test-scan.xml");
//getBean的入参是代理bean,但是获得结果确实一个ISubject类型
Driver driver = ((Driver) context.getBean("driver"));
System.out.println(driver.getCar());
}
}
执行结果:
null
虽然我们期望driver的属性自动注入car,但是失败了,原因是spring目前识别不了 @Autowired注解
1.2 @Autowired生效演示
如何让注解生效?那么就必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean,这是一个BeanPostProcessor后置处理器,专门用来解析带@Autowired注解的:
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/>
<bean id="car" class="com.test.springaop.Car"/>
<bean id="driver" class="com.test.springaop.Driver"/>
增加声明后,我们再来试下:
com.test.springaop.Car@1e397ed7
果然生效了,注入的car不为null了。
1.3 总结
要运用注解,需要注册相应的BeanPostProcessor后置处理器,因此汇总下:
-
@Autowired注解,那么就必须事先在 Spring 容器中声明 AutowiredAnnotationBeanPostProcessor Bean。传统声明方式如下
<bean class="org.springframework.beans.factory.annotation. AutowiredAnnotationBeanPostProcessor "/>
-
@ Resource 、@ PostConstruct、@ PreDestroy等注解就必须声明CommonAnnotationBeanPostProcessor。传统声明方式如下
<bean class="org.springframework.beans.factory.annotation. CommonAnnotationBeanPostProcessor"/>
-
@PersistenceContext注解,就必须声明PersistenceAnnotationBeanPostProcessor的Bean。
<bean class="org.springframework.beans.factory.annotation.PersistenceAnnotationBeanPostProcessor"/>
-
@Required的注解,就必须声明RequiredAnnotationBeanPostProcessor的Bean。
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
也就是说,每个注解语法,都有一个相应的解析器类,需要预配置在配置文件中!但是缺点是配置项太多了,一个个配会很麻烦
2. context:annotation-config
一般来说,像@ Resource 、@ PostConstruct、@Antowired这些注解在自动注入还是比较常用,所以如果总是需要按照传统的方式一条一条配置显得有些繁琐和没有必要,于是spring给我们提供< context:annotation-config/>
的简化配置方式,自动帮你完成声明。
< context:annotation-config>
是用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册如下四个后置处理器:
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor
现在读者可以自行修改test-scan.xml:
<context:annotation-config/>
<bean id="car" class="com.test.springaop.Car"/>
<bean id="driver" class="com.test.springaop.Driver"/>
执行结果也是ok的。
2.1 存在的不足
< context:annotation-config/>
可以帮助我们识别@Antowired注解,但是不能帮我我们识别@Component、@Controller、@Service等这些注解。
我们来验证下,修改test-scan.xml:
去掉注册的bean:
<context:annotation-config/>
<!--<bean id="car" class="com.test.springaop.Car"/>-->
<!--<bean id="driver" class="com.test.springaop.Driver"/>-->
我们修改Driver和Car,增加@Component:
import org.springframework.stereotype.Component;
@Component
public class Driver {
import org.springframework.stereotype.Component;
@Component
public class Car {
执行结果,报找不到driver这个bean,也就是说不识别@Component:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'driver' available
此时,就需要用到context:component-scan
了
3. context:component-scan
Spring 给我们提供了context:component-scan配置,如下:
<context:component-scan base-package="XX.XX"/>
通过对base-package配置,就可以把指定路径下 @Component等注解全部扫描到context中!
该配置项其实也包含了自动注入上述processor的功能,因此当使用 < context:component-scan/> 后,就可以将 < context:annotation-config/> 移除了。
现在来试验下,修改test-scan.xml,增加scan标签:
<context:component-scan base-package="com.test.springaop"/>
<!--<context:annotation-config/>-->
<!--<bean id="car" class="com.test.springaop.Car"/>-->
<!--<bean id="driver" class="com.test.springaop.Driver"/>-->
执行是ok的:
com.test.springaop.Car@4cf4d528
4.总结
(1):<context:annotation-config />
仅能够在已经在已经注册过的bean上面起作用。对于没有在spring容器中注册的bean,它并不能执行任何操作。
(2)<context:component-scan>
除了具有<context:annotation-config />
的功能之外,还具有自动将带有@component,@service,@Repository等注解的对象注册到spring容器中的功能。
4.1 如果同时使用这两个配置会不会出现重复注入的情况呢?
答案:不会重复注入,因为< context:annotation-config />和 < context:component-scan >同时存在的时候,前者会被忽略。如@autowire,@resource等注入注解只会被注入一次!
参考:
《Spring配置中context:annotation-config VS context:component-scan》