Spring Core Container 源码分析六:@Service

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
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // Spring will see this and treat @Service in the same way as @Component
public @interface Service {
// ....
}

可见,@Service本身也就是一个@Component;有关 meta annotation 的详细描述参看【7.10.2】Meta-annotations;

测试用例

这里继续沿用 Spring Beans 初始化流程分析中所使用到的测试用例;这里呢,我们为 Person 对象添加一个服务对象 DogService,该服务对象提供遛狗服务;

DogService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package org.shangyang.spring.container;

import org.springframework.stereotype.Service;

/**
*
* 专门为狗狗设计的服务,
*
* @author shangyang
*
*/
@Service
public class DogService {

/**
* 提供遛狗服务
* @param dog
*/
public void walkDog(Dog dog){

System.out.println("walks the dog "+dog.getName());

}

}

将其声明为 @Service,表示 DogService 是一个容器服务对象实例;

Person.java

1
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
51
package org.shangyang.spring.container;

import org.springframework.beans.factory.annotation.Autowired;

/**
*
* @author shangyang
*
*/
public class Person {

String name;

Person spouse;

@Autowired
Dog dog;

@Autowired
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.xml

1
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
@Test
public void testApplicationContext(){

@SuppressWarnings("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,这部分其实就简单许多了;

  1. 通过解析 <context:component-scan base-package=”org.shangyang” /> 元素,触发 ContextNamespaceHandlerdo scan 流程的操作,该过程就是去遍历由 base-package 所指定路径下的所有 .class 文件,找到并解析与 @Component、@Service、@Repository 等相关的 .class,并生成相应的 annotation-bean-definitions,将其注册到当前的 bean factory 中;具体过程参考 do scan 流程,不再赘述;

  2. 通过 @Autowired 注解注入由 @Component、@Service 等注解所生成的 bean 实例对象,该步骤参考 AutowiredAnnotationBeanPostProcessor部分,这里不再赘述;

通过上述两个步骤,完成了通过 @Autowired 注入 @Component 实例到当前 bean 的过程;