References
有关 Annotation-based 相关的注解说明参考官方文档【7.10.1】@Component and further stereotype annotations;
将相关重要的部分摘录如下,
@Repository
,@Service
, and@Controller
是@Component
的特殊例子,分别用在 persistence, service, and presentation layers;现在,你也可以使用@Component
来统一注解,
Spring 目前没有特别区分上述的几种注解方式,不过按照约定,最好在持久化层使用@Repository
,在服务层使用@Service
,在 web 控制层使用@Controller
,你也可以统一使用@Component
;
前言
虽然标题为讲解@Service
,实际上会牵涉到@Component
、@Repository
, @Controller
等概念;
本文为作者的原创作品,转载需注明出处;
源码分析环境搭建
参考 Spring Core Container 源码分析一:环境准备
源码分析
meta annotation
Spring 目前没有特别区分 @Repository
, @Service
和 @Controller
这几种注解方式,你也可以统一使用@Component
注解;那为什么呢?其实答案就是@Repository
, @Service
和@Controller
这三类注解都是通过@Component
注解生成的,类似于 Annotation 中的继承机制;看看 Service 注解的源码就清楚了,1
2
3
4
5
6
7 (ElementType.TYPE)
(RetentionPolicy.RUNTIME)
// Spring will see this and treat @Service in the same way as @Component
public Service {
// ....
}
可见,@Service
本身也就是一个@Component
;有关 meta annotation 的详细描述参看【7.10.2】Meta-annotations;
测试用例
这里继续沿用 Spring Beans 初始化流程分析中所使用到的测试用例;这里呢,我们为 Person 对象添加一个服务对象 DogService,该服务对象提供遛狗服务;
DogService.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package org.shangyang.spring.container;
import org.springframework.stereotype.Service;
/**
*
* 专门为狗狗设计的服务,
*
* @author shangyang
*
*/
public class DogService {
/**
* 提供遛狗服务
* @param dog
*/
public void walkDog(Dog dog){
System.out.println("walks the dog "+dog.getName());
}
}
将其声明为 @Service,表示 DogService 是一个容器服务对象实例;
Person.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51package org.shangyang.spring.container;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* @author shangyang
*
*/
public class Person {
String name;
Person spouse;
Dog dog;
DogService dogService;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person getSpouse() {
return spouse;
}
public void setSpouse(Person spouse) {
this.spouse = spouse;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
// walks my dog
public void walksDog(){
dogService.walkDog(dog);
}
}
将容器中的 DogService 实例通过 @Autowired 注入当前对象 Person;
beans.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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">
<context:component-scan base-package="org.shangyang" />
<bean name="john" class="org.shangyang.spring.container.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="jane" class="org.shangyang.spring.container.Person">
<property name="name" value="Jane Doe"/>
</bean>
<bean name="niba" class="org.shangyang.spring.container.Dog">
<property name="name" value="Niba" />
</bean>
</beans>
这里使用 <context:component-scan> 扫描并注入由 @Service 注解的 bean 到容器中;
_测试_1
2
3
4
5
6
7
8
9
10
11
public void testApplicationContext(){
"resource") (
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person p = context.getBean("john", Person.class);
p.walksDog();
}
输出结果,1
walks the dog Niba
Ok,john 调用遛狗服务成功;下面,我们来看看 Spring 底层容器是如何实现的?
流程分析
当分析完章节 Spring Core Container 源码分析七:注册 Bean Definitions,这部分其实就简单许多了;
通过解析 <context:component-scan base-package=”org.shangyang” /> 元素,触发 ContextNamespaceHandler 的 do scan 流程的操作,该过程就是去遍历由 base-package 所指定路径下的所有 .class 文件,找到并解析与 @Component、@Service、@Repository 等相关的 .class,并生成相应的 annotation-bean-definitions,将其注册到当前的 bean factory 中;具体过程参考 do scan 流程,不再赘述;
通过 @Autowired 注解注入由 @Component、@Service 等注解所生成的 bean 实例对象,该步骤参考 AutowiredAnnotationBeanPostProcessor部分,这里不再赘述;
通过上述两个步骤,完成了通过 @Autowired 注入 @Component 实例到当前 bean 的过程;