1、@Controller、@Service在org.springframework的spring-context依赖下,@Autowired在org.springframework的spring-beans依赖下。

2、@Service用在类上,注册为一个bean,bean名称默认为类名称(首字母小写),也可以手动指定@Service(“abc”)或@Service(value = “abc”)

3、@Autowired优先根据属性类型匹配,根据属性类型只匹配到一个时,则直接使用,不再比较属性值;当匹配到多个时再根据属性名称匹配,@Autowired可以用在已注册为bean的类的属性上,来引用其他外部bean,属性的类型为外部bean的类名或外部bean实现的接口名,

----外部bean没有实现接口

  ----属性类型应为外部bean的类名,属性值任意写或与@Service的value值相同

----外部bean实现了接口,且是接口的唯一实现类时

  ----属性类型为外部bean类名时,属性值任意写或与@Service的value值相同

  ----属性类型为外部bean实现的接口名时,属性值任意写或与@Service的value值相同

----外部bean实现了接口,且不是接口的唯一实现类时

  ----属性类型为外部bean类名时,属性值任意写或与@Service的value值相同

  ----属性类型为外部bean实现的接口名时,属性值应与需要的某一个实现类的类名相同(首字母小写)或与@Service指定的value值相同

Spring是怎么发现@Bean、@Controller、@Service这些注解修饰的类的?

我们通过AnnotationConfigApplicationContext类传入一个包路径启动Spring之后,会首先初始化包扫描的过滤规则。那我们今天就来看下包扫描的具体过程。

还是先看下面的代码: AnnotationConfigApplicationContext类

  1. //该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的Spring Bean,将其注册到容器中
  2. public AnnotationConfigApplicationContext(String... basePackages) {
  3. //初始化
  4. this();
  5. //扫描包、注册bean
  6. scan(basePackages);
  7. refresh();
  8. }

上文我们分析了this()方法,会去初始化AnnotatedBeanDefinitionReader读取器和ClassPathBeanDefinitionScanner扫描器,并初始化扫描过滤规则。 接下来我们看一下scan(basePackages)方法: 一直跟踪下去,发现调用了ClassPathBeanDefinitionScanner类中的scan()方法

  1. //调用类路径Bean定义扫描器入口方法
  2. public int scan(String... basePackages) {
  3. //获取容器中已经注册的Bean个数
  4. int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
  5.  
  6. //启动扫描器扫描给定包
  7. doScan(basePackages);
  8.  
  9. // Register annotation config processors, if necessary.
  10. //注册注解配置(Annotation config)处理器
  11. if (this.includeAnnotationConfig) {
  12. AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
  13. }
  14.  
  15. //返回注册的Bean个数
  16. return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
  17. }

可以看到主要是doScan(basePackages)方法实现了扫描的逻辑,我们继续跟踪进去看下

  1. //类路径Bean定义扫描器扫描给定包及其子包
  2. protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
  3. Assert.notEmpty(basePackages, "At least one base package must be specified");
  4. //创建一个集合,存放扫描到Bean定义的封装类
  5. Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
  6. //遍历扫描所有给定的包
  7. for (String basePackage : basePackages) {
  8. //调用父类ClassPathScanningCandidateComponentProvider的方法
  9. //扫描给定类路径,获取符合条件的Bean定义
  10. Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
  11. //遍历扫描到的Bean
  12. for (BeanDefinition candidate : candidates) {
  13. //获取@Scope注解的值,即获取Bean的作用域
  14. ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
  15. //为Bean设置作用域
  16. candidate.setScope(scopeMetadata.getScopeName());
  17. //为Bean生成名称
  18. String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
  19. //如果扫描到的Bean不是Spring的注解Bean,则为Bean设置默认值,
  20. //设置Bean的自动依赖注入装配属性等
  21. if (candidate instanceof AbstractBeanDefinition) {
  22. postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
  23. }
  24. //如果扫描到的Bean是Spring的注解Bean,则处理其通用的Spring注解
  25. if (candidate instanceof AnnotatedBeanDefinition) {
  26. //处理注解Bean中通用的注解,在分析注解Bean定义类读取器时已经分析过
  27. AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
  28. }
  29. //根据Bean名称检查指定的Bean是否需要在容器中注册,或者在容器中冲突
  30. if (checkCandidate(beanName, candidate)) {
  31. BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
  32. //根据注解中配置的作用域,为Bean应用相应的代理模式
  33. definitionHolder =
  34. AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
  35. beanDefinitions.add(definitionHolder);
  36. //向容器注册扫描到的Bean
  37. registerBeanDefinition(definitionHolder, this.registry);
  38. }
  39. }
  40. }
  41. return beanDefinitions;
  42. }

这一大段代码基本上就是spring扫描识别注解,并注册Bean到IOC容器中的代码。 在第10行有一个findCandidateComponents(basePackage)方法,这个方法里就是具体的扫描逻辑。 继续跟踪: ClassPathScanningCandidateComponentProvider类

  1. //扫描给定类路径的包
  2. public Set<BeanDefinition> findCandidateComponents(String basePackage) {
  3. //spring5.0开始 索引 开启的话生成文件META-INF/spring.components 后面加载直接从本地文件读取(一般不建议开启 spring.index.ignore=true)
  4. if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
  5. return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
  6. }
  7. else {
  8. return scanCandidateComponents(basePackage);
  9. }
  10. }

这里有一个if判断,我们默认走的是else里的分支,即scanCandidateComponents(basePackage)方法。

  1. private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
  2. Set<BeanDefinition> candidates = new LinkedHashSet<>();
  3. try {
  4. //补全扫描路径,扫描所有.class文件 classpath*:com/mydemo/**/*.class
  5. String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
  6. resolveBasePackage(basePackage) + '/' + this.resourcePattern;
  7. //定位资源
  8. Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
  9. boolean traceEnabled = logger.isTraceEnabled();
  10. boolean debugEnabled = logger.isDebugEnabled();
  11. for (Resource resource : resources) {
  12. if (traceEnabled) {
  13. logger.trace("Scanning " + resource);
  14. }
  15. if (resource.isReadable()) {
  16. try {
  17. //通过ASM获取class元数据,并封装在MetadataReader元数据读取器中
  18. MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
  19. //判断该类是否符合@CompoentScan的过滤规则
  20. //过滤匹配排除excludeFilters排除过滤器(可以没有),包含includeFilter中的包含过滤器(至少包含一个)。
  21. if (isCandidateComponent(metadataReader)) {
  22. //把元数据转化为 BeanDefinition
  23. ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
  24. sbd.setResource(resource);
  25. sbd.setSource(resource);
  26. //判断是否是合格的bean定义
  27. if (isCandidateComponent(sbd)) {
  28. if (debugEnabled) {
  29. logger.debug("Identified candidate component class: " + resource);
  30. }
  31. //加入到集合中
  32. candidates.add(sbd);
  33. }
  34. else {
  35. //不合格 不是顶级类、具体类
  36. if (debugEnabled) {
  37. logger.debug("Ignored because not a concrete top-level class: " + resource);
  38. }
  39. }
  40. }
  41. else {
  42. //不符@CompoentScan过滤规则
  43. if (traceEnabled) {
  44. logger.trace("Ignored because not matching any filter: " + resource);
  45. }
  46. }
  47. }
  48. catch (Throwable ex) {
  49. throw new BeanDefinitionStoreException(
  50. "Failed to read candidate component class: " + resource, ex);
  51. }
  52. }
  53. else {
  54. if (traceEnabled) {
  55. logger.trace("Ignored because not readable: " + resource);
  56. }
  57. }
  58. }
  59. }
  60. catch (IOException ex) {
  61. throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
  62. }
  63. return candidates;
  64. }

这里就是主要的扫描逻辑,代码中的注释已经说的很清楚了。 主要过程:

  • 根据包路径,扫描所有.class文件
  • 根据包路径,生成.class对应的Resource对象
  • 通过ASM获取class元数据,并封装在MetadataReader元数据读取器中
  • 判断该类是否符合过滤规则
  • 判断该类是否为独立的类、具体的类
  • 加入到集合中

我们来详细看下过滤的方法 isCandidateComponent(metadataReader)

  1. //判断元信息读取器读取的类是否符合容器定义的注解过滤规则
  2. //@CompoentScan的过滤规则支持5种 (注解、类、正则、aop、自定义)
  3. protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
  4. //如果读取的类的注解在排除注解过滤规则中,返回false
  5. for (TypeFilter tf : this.excludeFilters) {
  6. if (tf.match(metadataReader, getMetadataReaderFactory())) {
  7. return false;
  8. }
  9. }
  10. //如果读取的类的注解在包含的注解的过滤规则中,则返回ture
  11. for (TypeFilter tf : this.includeFilters) {
  12. //判断当前类的注解是否match规则
  13. if (tf.match(metadataReader, getMetadataReaderFactory())) {
  14. //是否有@Conditional注解,进行相关处理
  15. return isConditionMatch(metadataReader);
  16. }
  17. }
  18. //如果读取的类的注解既不在排除规则,也不在包含规则中,则返回false
  19. return false;
  20. }

接着跟踪 tf.match()方法 AbstractTypeHierarchyTraversingFilter类

  1. public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
  2. throws IOException {
  3.  
  4. // This method optimizes avoiding unnecessary creation of ClassReaders
  5. // as well as visiting over those readers.
  6. //检查当前类的注解是否符合规律规则
  7. if (matchSelf(metadataReader)) {
  8. return true;
  9. }
  10. //check 类名是否符合规则
  11. ClassMetadata metadata = metadataReader.getClassMetadata();
  12. if (matchClassName(metadata.getClassName())) {
  13. return true;
  14. }
  15.  
  16. //如果有继承父类
  17. if (this.considerInherited) {
  18. String superClassName = metadata.getSuperClassName();
  19. if (superClassName != null) {
  20. // Optimization to avoid creating ClassReader for super class.
  21. Boolean superClassMatch = matchSuperClass(superClassName);
  22. if (superClassMatch != null) {
  23. if (superClassMatch.booleanValue()) {
  24. return true;
  25. }
  26. }
  27. else {
  28. // Need to read super class to determine a match...
  29. try {
  30. if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
  31. return true;
  32. }
  33. }
  34. catch (IOException ex) {
  35. logger.debug("Could not read super class [" + metadata.getSuperClassName() +
  36. "] of type-filtered class [" + metadata.getClassName() + "]");
  37. }
  38. }
  39. }
  40. }
  41.  
  42. //如果有实现接口
  43. if (this.considerInterfaces) {
  44. for (String ifc : metadata.getInterfaceNames()) {
  45. // Optimization to avoid creating ClassReader for super class
  46. Boolean interfaceMatch = matchInterface(ifc);
  47. if (interfaceMatch != null) {
  48. if (interfaceMatch.booleanValue()) {
  49. return true;
  50. }
  51. }
  52. else {
  53. // Need to read interface to determine a match...
  54. try {
  55. if (match(ifc, metadataReaderFactory)) {
  56. return true;
  57. }
  58. }
  59. catch (IOException ex) {
  60. logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
  61. metadata.getClassName() + "]");
  62. }
  63. }
  64. }
  65. }
  66.  
  67. return false;
  68. }

这里面最主要的是 matchSelf(metadataReader) 方法 AnnotationTypeFilter类

  1. protected boolean matchSelf(MetadataReader metadataReader) {
  2. //获取注解元数据
  3. AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
  4. //check 注解及其派生注解中是否包含@Component
  5. //获取当前类的注解 metadata.hasAnnotation @Controller
  6. //获取当前类的注解及其派生注解 metadata.hasAnnotation @Controller包含的@Component\@Documented等等
  7. return metadata.hasAnnotation(this.annotationType.getName()) ||
  8. (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
  9. }

在这段代码代码中,可以解决我们之前的疑惑“Spring是怎么发现@Configuration、@Controller、@Service这些注解修饰的类的?” 原来@Configuration、@Controller、@Service这些注解其实都是@Component的派生注解,我们看这些注解的代码会发现,都有@Component注解修饰。而spring通过metadata.hasMetaAnnotation()方法获取到这些注解包含@Component,所以都可以扫描到。如下:

然后我们再看回 scanCandidateComponents(basePackage)方法,接下来有一个 isCandidateComponent(sbd)方法,如下:

  1. //是否是独立的类、具体的类
  2. protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
  3. AnnotationMetadata metadata = beanDefinition.getMetadata();
  4. return (metadata.isIndependent() && (metadata.isConcrete() ||
  5. (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
  6. }

这个方法的作用是,判断该类是否为

  • 顶层的类(没有父类或静态内部类)
  • 具体的类(不是抽象类或接口)

至此,ClassPathBeanDefinitionScanner类中的doScan(basePackages)方法中的findCandidateComponents(basePackage)方法已经结束了,即我们的包扫描也结束了,已经把扫描到的类存入到了集合中,结下来就是解析注册Bean的过程了。

总结 通过这篇文章,我们可以回答之前的一些问题了:

  • Spring是怎么发现@Bean、@Controller、@Service这些注解修饰的类的? 通过 matchSelf(metadataReader)方法,判断这些注解中是否包含@Component
  • @CompoentScan注解是怎么起作用的? 通过 isCandidateComponent(metadataReader)方法过滤

这里面有两个关键变量:

  • private final List<TypeFilter> includeFilters = new LinkedList<>();

  • private final List<TypeFilter> excludeFilters = new LinkedList<>();

includeFilters表示要包含的注解,即只有包含includeFilters中的注解,才会被扫描
excludeFilters表示要排除的注解,即包含excludeFilters中的注解不会被扫描

在这个方法中,includeFilters集合中添加了@Component、JavaEE6的@ManagedBean和JSR-330的@Named注解 而excludeFilters集合没做任何变动,即没有要排除的注解

总结:
所以默认规则就是,只要包含了@Component、JavaEE6的@ManagedBean和JSR-330的@Named这3个注解中的任意一个,就会被扫描

@Bean、@Component、 @Service、 @Repository 和 @Controller注解的区别

@Bean:表示一个方法实例化、配置或者初始化一个Spring IoC容器管理的新对象。
@Component: 自动被comonent扫描。 表示被注解的类会自动被component扫描
@Repository: 用于持久层,主要是数据库存储库。
@Service: 表示被注解的类是位于业务层的业务component。
@Controller:表明被注解的类是控制component,主要用于展现层 。

@Bean与@Component区别
@Component是 spring 2.5引入的,为了摆脱通过classpath扫描根据xml方式定义的bean的方式.

@Bean是spring 3.0 引入的,和 @Configuration一起工作,为了摆脱原先的xml和java config方式。

Spring管理Bean方式有两种,一种是注册Bean,一种装配Bean。
可以通过三种方式实现bean管理,一使用自动配置的方式、二使用JavaConfig的方式、三使用XML配置的方式。

@Compent 作用就相当于 XML配置

@Component
@Data
public class User{
    private String name = "tom";
}

@Bean 需要在配置类中使用,即类上需要加上@Component或者@Configuration注解, 通常加上@Configuration。 @Bean的用法在这里

@Configuration
public class AppConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

官方文档在这里。

两者都可以通过@Autowired装配

@Autowired
private TransferService transferService;

@Component与@Service区别
目前基本没有区别。
参看源代码spring-context-4.3.16

/**
 * Indicates that an annotated class is a "Service", originally defined by Domain-Driven
 * Design (Evans, 2003) as "an operation offered as an interface that stands alone in the
 * model, with no encapsulated state."
 *
 * <p>May also indicate that a class is a "Business Service Facade" (in the Core J2EE
 * patterns sense), or something similar. This annotation is a general-purpose stereotype
 * and individual teams may narrow their semantics and use as appropriate.
 *
 * <p>This annotation serves as a specialization of {@link Component @Component},
 * allowing for implementation classes to be autodetected through classpath scanning.
 *
 * @author Juergen Hoeller
 * @since 2.5
 * @see Component
 * @see Repository
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 */
	String value() default "";

}

从源代码可以看出@Service和@Component没啥本质区别,官方文档也说了This annotation serves as a specialization of {@link Component @Component},

  • allowing for implementation classes to be autodetected through classpath scanning. 也就是@Service是一种具体的@Component,使得被注解类能通过classpath扫描被自动检测。