spring框架
一、技术说明(技术介绍,技术优势以及发展史等)
1.1、什么是spring
- Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
目的:解决企业应用开发的复杂性
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
范围:任何Java应用
-
Spring是一个轻量级控制反转(IOC)和面向切面(AOP)的容器框架。
-
Spring是分层的JavaSE、EE、full-stack 轻量级开源框架,轻量级:使用时占用资源少,依赖程序少。
分层:三层体系结构,为每一个层都提供解决方案
-----web层:struts2、spring-mvc
-----service层:spring
-----dao层:hibernate、mybatis、jdbcTemplate(spring)……
1.2、spring优点
-
方便解耦,简化开发 (易扩展,易维护)
Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理
扫描二维码关注公众号,回复: 9854356 查看本文章 -
AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
-
声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程
-
方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序
-
方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持
-
降低JavaEE API的使用难度
Spring 对开发中非常难用的一些(、、远程调用等),都提供了封装,使这些应用难度大大降低
1.3、 spring体系结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdjmDXVx-1574504390518)(spring框架.assets/1.png)]
spring核心功能:beans、core、context、expression
二、环境搭建(技术开发环境)
1、官网下载
-
从官网下载spring 最新的相关jar包,官网download地址 http://www.springsource.org/springcommunity-download下载完成后会发现三个目录,命名很明确。
-
Docs 目录相关文档。包括一份和一份各种的使用说明,提供了版本,非常详细。
2、spring包的核心包
搭建第一个用到 spring 依赖注册的程序,直接用 IDEA建立一个 WEB项目,然后添加 spring 的 jar 包引入
spring-core-3.2.0.M1.jar 核心依赖 jar 包
spring-context-3.2.0.M1.jar Spring 容器包
spring-beans-3.2.0.M1.jar Spring beans 的管理包
spring-asm-3.2.0.M1.jar Spring
spring-expression-3.2.0.M1.jar
commons-logging-1.1.1.jar 千万不要忘记日志包,否则会报错
3. 配置 XML
- Spring 的最大的作用就是提供 bean 的管理功能,在 spring 中 bean 的管理是通过 XML 实现的,要用此功能,需要把 bean 配置到 spring 的 xml.
- 新建立一个 xml.名字任意,如 applicationContext.xml,或者 text.xml 都可以
- 添加 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">
</beans>
4、 依赖注入
spring注入的简单案例(入门级)
Demo类:
public class Demo {
public void say() {
System.out.println ("hellow spring");
}
}
application.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="demo" class="com.zhangyong.Demo"></bean>
</beans>
测试类:DemoTest
public class DemoTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
Demo demo = (Demo) context.getBean ("demo");
demo.say ();
}
}
5、注入方式
spring框架为我们提供了三种注入方式,分别是set注入,构造方法注入,接口注入。接口注入不作要求,下面介绍前两种方式。
1、set注入
采用属性的set方法进行初始化,就成为set注入。给普通字符类型赋值。
User类:
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void say() {
System.out.println ("hellow spring" + name);
}
}
application.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="user" class="com.zhangyong.User">
<property name="name" value="admin"></property>
</bean>
</beans>
测试类:
public class DemoTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
User user = (User) context.getBean ("user");
user.say ();
}
}
运行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TGfjC1xC-1574504390523)(spring框架.assets/image-20191114143728121.png)]
给对象赋值
User类
public class User {
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
UserService 接口
public interface UserService {
void say();
}
UserServiceImpl实现类
public class UserServiceImpl implements UserService {
@Override
public void say() {
System.out.println ("注入进来");
}
}
DemoTest 测试:
public class DemoTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
UserService userService = (UserService) context.getBean ("userService");
userService.say ();
}
}
总结:这样配置,框架就会将UserService对象注入到User类中。
给list集合赋值
同样提供set方法
User类
public class User {
private List<String> name;
public List<String> getName() {
return name;
}
public void setName(List<String> name) {
this.name = name;
}
//为了测试
public void say() {
System.out.println (name);
}
}
application.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="user" class="com.zhangyong.User">
<property name="name">
<list>
<value>zhangsan</value>
<value>lisi</value>
<value>wanger</value>
</list>
</property>
</bean>
</beans>
测试
public class DemoTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
User user = (User) context.getBean ("user");
user.say ();
}
}
给属性文件中的字段赋值
User类
public class User {
private Properties props;
public Properties getProps() {
return props;
}
public void setProps(Properties props) {
this.props = props;
}
}
application.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="user" class="com.zhangyong.User">
<property name="props">
<props>
<prop key="url">jdbc:oracle:thin:@localhost:orl</prop>
<prop key="driverName">oracle.jdbc.driver.OracleDriver</prop>
<prop key="username">scott</prop>
<prop key="password">tiger</prop>
</props>
</property>
</bean>
</beans>
注:prop标签中的key值是.properties属性文件中的名称
2、构造方法注入
构造方法一个参数
User类
public class User {
private String usercode;
public User(String usercode) {
this.usercode = usercode;
}
public void say() {
System.out.println (usercode);
}
}
application.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="user" class="com.zhangyong.User">
<constructor-arg value="admin"></constructor-arg>
</bean>
</beans>
DemoTest 测试:
public class DemoTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
User user = (User) context.getBean ("user");
user.say ();
}
}
构造函数有两个参数时
当参数为非字符串类型时,在配置文件中需要制定类型,如果不指定类型一律按照字符串类型赋值。
当参数类型不一致时,框架是按照字符串的类型进行查找的,因此需要在配置文件中制定是参数的位置
<constructor-arg value="admin" type="String" index="0"></constructor-arg>
<constructor-arg value="23" type="int" index="1"></constructor-arg>
5、Spring的 junit 单元测试
单元测试以set注入的第一个实例为测试对象。进行单元测试。
User 类
public class User {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
//添加方法
public String login(){
if ("admin".equals (username)){
System.out.println ("success");
return "success";
}else {
System.out.println ("error");
return "error";
}
}
}
application.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="user" class="com.zhangyong.User">
<property name="username" value="admin"></property>
</bean>
</beans>
测试类:
public class DemoTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext ("/application.xml");
User user = (User) context.getBean ("user");
user.login ();
}
}
6、Spring对注解(Annotation)处理源码分析1——扫描和读取Bean定义
1、Spring IOC容器对于类级别的注解和类内部的注解分以下两种处理策略:
1.类级别的注解:
如@Component、@Repository、@Controller、@Service以及JavaEE6的@ManagedBean和@Named注解,都是添加在类上面的类级别注解,Spring容器根据注解的过滤规则扫描读取注解Bean定义类,并将其注册到Spring IoC容器中。
2.类内部的注解:
如@Autowire、@Value、@Resource以及EJB和WebService相关的注解等,都是添加在类内部的字段或者方法上的类内部注解,SpringIoC容器通过Bean后置注解处理器解析Bean内部的注解。
下面将根据这两种处理策略,分别分析Spring处理注解相关的源码。
2、Annotation Config Application Context对注解Bean初始化:
Spring中,管理注解Bean定义的容器有两个:AnnotationConfigApplicationContext和 AnnotationConfigWebApplicationContext。这两个类是专门处理Spring注解方式配置的容器,直接依赖于注解作为容器配置信息来源的IOC容器。 AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的web版本,两者的用法以及对注解的处理方式几乎没有什么差别,因此本文将以AnnotationConfigApplicationContext为例进行讲解。
AnnotationConfigApplicationContext的源码如下:
public class AnnotationConfigApplicationContext extends GenericApplicationContext {
//创建一个读取注解的Bean定义读取器,并将其设置到容器中
private final AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader (this);
//创建一个扫描指定类路径中注解Bean定义的扫描器,并将其设置到容器中
private final ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner (this);
//默认构造函数,初始化一个空容器,容器不包含任何 Bean 信息,需要在稍后通过调用其register() //方法注册配置类,并调用refresh()方法刷新容器,触发容器对注解Bean的载入、解析和注册过程
public AnnotationConfigApplicationContext() {
}
//最常用的构造函数,通过将涉及到的配置类传递给该构造函数,以实现将相应配置类中的Bean
//自动注册到容器中
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
register (annotatedClasses);
refresh ();
}
//该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的Spring Bean,将其
//注册到容器中
public AnnotationConfigApplicationContext(String... basePackages) {
scan (basePackages);
refresh ();
}
//为容器的注解Bean读取器和注解Bean扫描器设置Bean名称产生器
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
this.reader.setBeanNameGenerator (beanNameGenerator);
this.scanner.setBeanNameGenerator (beanNameGenerator);
}
//为容器的注解Bean读取器和注解Bean扫描器设置作用范围元信息解析器
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
this.reader.setScopeMetadataResolver (scopeMetadataResolver);
this.scanner.setScopeMetadataResolver (scopeMetadataResolver);
}
//为容器注册一个要被处理的注解Bean,新注册的Bean,必须手动调用容器的
//refresh()方法刷新容器,触发容器对新注册的Bean的处理
public void register(Class<?>... annotatedClasses) {
this.reader.register (annotatedClasses);
}
//扫描指定包路径及其子包下的注解类,为了使新添加的类被处理,必须手动调用
//refresh()方法刷新容器
public void scan(String... basePackages) {
this.scanner.scan (basePackages);
}
}
通过对 AnnotationConfigApplicationContext 的源码分析,我们了解到Spring对注解的处理分为两种方式:
a、直接将注解Bean注册到容器中:
可以在初始化容器时注册;也可以在容器创建之后手动调用注册方法向容器注册,然后通过手动刷新容器,使得容器对注册的注解Bean进行处理。
b、通过扫描指定的包及其子包下的所有类:
在初始化注解容器时指定要自动扫描的路径,如果容器创建以后向给定路径动态添加了注解Bean,则需要手动调用容器扫描的方法,然后手动刷新容器,使得容器对所注册的Bean进行处理。
3、AnnotationConfigApplicationContext 注册注解Bean:
当创建注解处理容器时,如果传入的初始参数是具体的注解Bean定义类时,注解容器读取并注册。
(1)、AnnotationConfigApplicationContext 通过调用注解Bean定义读取器 AnnotatedBeanDefinitionReader 的register方法向容器注册指定的注解Bean
注解Bean定义读取器向容器注册注解Bean的源码如下:
//注册多个注解Bean定义类
public void register(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
registerBean (annotatedClass);
}
}
//注册一个注解Bean定义类
public void registerBean(Class<?> annotatedClass) {
registerBean (annotatedClass, null, (Class<? extends Annotation>[]) null);
}
//Bean定义读取器注册注解Bean定义的入口方法
public void registerBean(Class<?> annotatedClass, Class<? extends Annotation>... qualifiers) {
registerBean (annotatedClass, null, qualifiers);
}
//Bean定义读取器向容器注册注解Bean定义类
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
//根据指定的注解Bean定义类,创建Spring容器中对注解Bean的封装的数据结构
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition (annotatedClass);
//解析注解Bean定义的作用域,若@Scope("prototype"),则Bean为原型类型;
//若@Scope("singleton"),则Bean为单态类型
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata (abd);
//为注解Bean定义设置作用域
abd.setScope (scopeMetadata.getScopeName ());
//为注解Bean定义生成Bean名称
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName (abd, this.registry));
//处理注解Bean定义中的通用注解
AnnotationConfigUtils.processCommonDefinitionAnnotations (abd);
//如果在向容器注册注解Bean定义时,使用了额外的限定符注解,则解析限定符注解。
//主要是配置的关于autowiring自动依赖注入装配的限定条件,即@Qualifier
//注解,Spring自动依赖注入装配默认是按类型装配,如果使用@Qualifier则按名称
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
//如果配置了@Primary注解,设置该Bean为autowiring自动依赖注入装//配时的首选
if (Primary.class.equals (qualifier)) {
abd.setPrimary (true);
}
//如果配置了@Lazy注解,则设置该Bean为非延迟初始化,如果没有配置,
//则该Bean为预实例化
else if (Lazy.class.equals (qualifier)) {
abd.setLazyInit (true);
}
//如果使用了除@Primary和@Lazy以外的其他注解,则为该Bean添加一
//个autowiring自动依赖注入装配限定符,该Bean在进autowiring
//自动依赖注入装配时,根据名称装配限定符指定的Bean
else {
abd.addQualifier (new AutowireCandidateQualifier (qualifier));
}
}
}
//创建一个指定Bean名称的Bean定义对象,封装注解Bean定义类数据
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder (abd, beanName);
//根据注解Bean定义类中配置的作用域,创建相应的代理对象
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode (scopeMetadata, definitionHolder, this.registry);
//向IoC容器注册注解Bean类定义对象 BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
(2)从上面的源码我们可以看出,注册注解Bean定义类的基本步骤:
a、需要使用注解元数据解析器解析注解Bean中关于作用域的配置。
b、使用 AnnotationConfigUtils的processCommonDefinitionAnnotations方法处理注解Bean定义类中通用的注解。
c、使用 AnnotationConfigUtils的applyScopedProxyMode 方法创建对于作用域的代理对象。
d、通过 BeanDefinitionReaderUtils 向容器注册Bean。
下面我们继续分析这3步的具体实现过程
(3)AnnotationScopeMetadataResolver解析作用域元数据:
AnnotationScopeMetadataResolver通过processCommonDefinitionAnnotations方法解析注解Bean定义类的作用域元信息,即判断注册的Bean是原生类型(prototype)还是单态(singleton)类型
其源码如下:
//解析注解Bean定义类中的作用域元信息
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata ();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
//从注解Bean定义类的属性中查找属性为”Scope”的值,即@Scope注解的值
// annDef.getMetadata().getAnnotationAttributes方法将Bean
//中所有的注解和注解的值存放在一个map集合中
Map<String, Object> attributes =
annDef.getMetadata ().getAnnotationAttributes (this.scopeAnnotationType.getName ());
//将获取到的@Scope注解的值设置到要返回的对象中
if (attributes != null) {
metadata.setScopeName ((String) attributes.get ("value"));
//获取@Scope注解中的proxyMode属性值,在创建代理对象时会用到
ScopedProxyMode proxyMode = (ScopedProxyMode) attributes.get ("proxyMode");
//如果@Scope的proxyMode属性值为null、DEFAULT或者NO
if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
//设置proxyMode为NO
proxyMode = this.defaultProxyMode;
}
//为返回的元数据设置proxyMode
metadata.setScopedProxyMode (proxyMode);
}
}
//返回解析的作用域元信息对象
return metadata;
}
上述代码中的 annDef.getMetadata().getAnnotationAttributes方法就是获取对象中指定类型的注解的值。
(4)AnnotationConfigUtils处理注解Bean定义类中的通用注解:
AnnotationConfigUtils类的processCommonDefinitionAnnotations在向容器注册Bean之前,首先对注解Bean定义类中的通用Spring注解进行处理
源码如下:
//处理Bean定义中通用注解
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
//如果Bean定义中有@Primary注解,则为该Bean设置为autowiring自动依赖注入//装配的首选对象
if (abd.getMetadata ().isAnnotated (Primary.class.getName ())) {
abd.setPrimary (true);
}
//如果Bean定义中有@Lazy注解,则将该Bean预实例化属性设置为@lazy注解的值
if (abd.getMetadata ().isAnnotated (Lazy.class.getName ())) {
Boolean value = (Boolean) abd.getMetadata ().getAnnotationAttributes (Lazy.class.getName ()).get ("value");
abd.setLazyInit (value);
}
//如果Bean定义中有@ DependsOn注解,则为该Bean设置所依赖的Bean名称,
//容器将确保在实例化该Bean之前首先实例化所依赖的Bean
if (abd.getMetadata ().isAnnotated (DependsOn.class.getName ())) {
String[] value = (String[]) abd.getMetadata ().getAnnotationAttributes (DependsOn.class.getName ()).get ("value");
abd.setDependsOn (value);
}
}
(5)AnnotationConfigUtils根据注解Bean定义类中配置的作用域为其应用相应的代理策略:
AnnotationConfigUtils类的applyScopedProxyMode方法根据注解Bean定义类中配置的作用域@Scope注解的值,为Bean定义应用相应的代理模式,主要是在Spring面向切面编程(AOP)中使用
源码如下:
//根据作用域为Bean应用引用的代码模式
static BeanDefinitionHolder applyScopedProxyMode(
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
//获取注解Bean定义类中@Scope注解的proxyMode属性值
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode ();
//如果配置的@Scope注解的proxyMode属性值为NO,则不应用代理模式
if (scopedProxyMode.equals (ScopedProxyMode.NO)) {
return definition;
}
//获取配置的@Scope注解的proxyMode属性值,如果为TARGET_CLASS,则返
//回true,如果为INTERFACES,则返回false
boolean proxyTargetClass = scopedProxyMode.equals (ScopedProxyMode.TARGET_CLASS);
//为注册的Bean创建相应模式的代理对象
return ScopedProxyCreator.createScopedProxy (definition, registry, proxyTargetClass);
}
这段为Bean引用创建相应模式的代理,如果在Spring面向切面编程(AOP)中涉及到再详细分析,这里不做深入的分析。
(6)BeanDefinitionReaderUtils向容器注册Bean:
4、AnnotationConfigApplicationContext扫描指定包及其子包下的注解Bean:
当创建注解处理容器时,如果传入的初始参数是注解Bean定义类所在的包时,注解容器将扫描给定的包及其子包,将扫描到的注解Bean定义载入并注册。
(1)Spring中常用的注解:
a、Component注解:(把普通pojo实例化到spring容器中,相当于配置文件中的)
泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
String value() default "";
}
b、Service注解:@service 服务(注入dao)
用于标注服务层,主要用来进行业务的逻辑处理
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
String value() default "";
}
c、Controller注解: @controller 控制器(注入服务)
用于标注控制层,相当于struts中的action层
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
String value() default "";
}
d、Repository注解: 用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
String value() default "";
}
通过分析Spring这4个常用的注解源码,我们看到:@Service、@Controller和@Repository注解都添加了一个@Component注解,因此他们都属于@Component注解。
(2)ClassPathBeanDefinitionScanner扫描给定的包及其子包:
AnnotationConfigApplicationContext通过调用类路径Bean定义扫描器ClassPathBeanDefinitionScanner扫描给定包及其子包下的所有类,主要源码如下:
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
//创建一个类路径Bean定义扫描器
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
this (registry, true);
}
//为容器创建一个类路径Bean定义扫描器,并指定是否使用默认的扫描过滤规则。
//即Spring默认扫描配置:@Component、@Repository、@Service、@Controller
//注解的Bean,同时也支持JavaEE6的@ManagedBean和JSR-330的@Named注解
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
//调用父类ClassPathScanningCandidateComponentProvider构造方法设置过滤规则
super (useDefaultFilters);
Assert.notNull (registry, "BeanDefinitionRegistry must not be null");
//为容器设置加载Bean定义的注册器
this.registry = registry;
//如果注册器是资源加载器,则为容器设置资源加载器
if (this.registry instanceof ResourceLoader) {
setResourceLoader ((ResourceLoader) this.registry);
}
}
//调用类路径Bean定义扫描器入口方法
public int scan(String... basePackages) {
//获取容器中已经注册的Bean个数
int beanCountAtScanStart = this.registry.getBeanDefinitionCount ();
//启动扫描器扫描给定包
doScan (basePackages);
//注册注解配置(Annotation config)处理器
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors (this.registry);
}
//返回注册的Bean个数
return this.registry.getBeanDefinitionCount () - beanCountAtScanStart;
}
//类路径Bean定义扫描器扫描给定包及其子包
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
//创建一个集合,存放扫描到Bean定义的封装类
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder> ();
//遍历扫描所有给定的包
for (String basePackage : basePackages) {
//调用父类ClassPathScanningCandidateComponentProvider的方法
//扫描给定类路径,获取符合条件的Bean定义
Set<BeanDefinition> candidates = findCandidateComponents (basePackage);
//遍历扫描到的Bean
for (BeanDefinition candidate : candidates) {
//获取Bean定义类中@Scope注解的值,即获取Bean的作用域
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata (candidate);
//为Bean设置注解配置的作用域
candidate.setScope (scopeMetadata.getScopeName ());
//为Bean生成名称
String beanName = this.beanNameGenerator.generateBeanName (candidate, this.registry);
//如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值,
//设置Bean的自动依赖注入装配属性等
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition ((AbstractBeanDefinition) candidate, beanName);
}
//如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解
if (candidate instanceof AnnotatedBeanDefinition) {
//处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突
if (checkCandidate (beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder (candidate, beanName);
//根据注解中配置的作用域,为Bean应用相应的代理模式
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode (scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add (definitionHolder);
//向容器注册扫描到的Bean
registerBeanDefinition (definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
……
}
义类读取器时已经分析过 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突
if (checkCandidate (beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder (candidate, beanName);
//根据注解中配置的作用域,为Bean应用相应的代理模式
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode (scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add (definitionHolder);
//向容器注册扫描到的Bean
registerBeanDefinition (definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
……
}
类路径Bean定义扫描器ClassPathBeanDefinitionScanner主要通过findCandidateComponents方法调用其父类ClassPathScanningCandidateComponentProvider类来扫描获取给定包及其子包下的类。