@Component
和类似的注解:@Repository用于Dao层,@Component用于Spring管理的通用的组件。@Repository
,@Service
,和@Controller
都是特别版的@Component,用于特定用途。
比如:@Service注解是由@Component注解和元注解定义出来的:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
// ...
}
@RestController是由@Controller和@ResponseBody组合成的。
自动探测类和注册bean:
给@Configuration修饰的类添加@ComponentScan注解。
有一个basePackages属性,可以设置扫描的包范围,一般是类的父包。
也可以使用逗号或者分号指定扫描多个包。
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
// ...
}
在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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.example"/>
</beans>
注意:<context:component-scan>隐式开启<context:annotation-config>,
所以不需要添加<context:annotation-config>。
AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor也被隐式包含。
使用过滤器定制扫描行为
(MyBatis的拦截器也会用到类似的)
使用@ComponentScan注解的includeFilters或excludeFilters属性添加过滤器,
过滤器有两个属性,type和pattern。
过滤器类型:
Filter Type | Example | Description |
FilterType.ANNOTATION | org.example.SomeAnnotation | 匹配被注解的组件 |
FilterType.ASSIGNABLE_TYPE | org.example.SomeClass | 类和它extends和implements的类 |
FilterType.ASPECTJ | org.example..*Service+ | 匹配这个切面 |
FilterType.REGEX | org\.example\.Default.* | 匹配正则表达式 |
FilterType.CUSTOM | org.example.MyTypeFilter | 实现org.springframework.core.type.TypeFilter的类 |
使用例子:
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}
等价的xml配置:
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
可以关闭默认的过滤器以禁止自动探测@Component
,@Repository
,@Service
,@Controller
,@RestController
或@Configuration
修饰的类:
@ComponentScan(useDefaultFilters=false)或:
<component-scan use-default-filters=”false”/>
在组件里定义bean
定义一个类,用@Component
,@Repository
,@Service
,@Controller
,@RestController
或@Configurati
on
修饰它,然后在它里面用@Bean修饰返回对象的方法,即可自动将对象注册成bean:
@Component
public class FactoryMethodComponent {
@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
public void doWork() {
// 组件内的方法会被忽略
}
}
还可以将@Bean和@Qualifier、@Scope
或@Lazy
等修饰bean的注解组合使用。
这种生成bean的方法也称作工厂方法。
从Spring4.3版本开始,可以将InjectionPoint或者它的更详细的子类DependencyDescriptor类型作为工厂方法的参数。用于访问创建当前bean的注入点,而不是注入现有的bean。用于生成prototype的bean。
对于别的作用域的bean,只在创建当前bean的时候可以看见注入点。
用法:
@Component
public class FactoryMethodComponent {
@Bean @Scope("prototype")
public TestBean prototypeInstance(InjectionPoint injectionPoint) {
return new TestBean("prototypeInstance for " + injectionPoint.getMember());
}
}
在@Component
内的@bean方法和在@Configurati
on里的不太一样。@Component
类没有用CGLIB代理拦截方法调用或者域(变量)。
CGLIB是调用@Configuration 类中@Bean方法内的方法或变量的一种方式,
是创建bean元数据引用给其协作对象的途径。
这些方法不是通过普通的Java语法调用,而是通过容器这一途径,用以提供生命周期管理和Bean代理,
就算主动调用别的@Bean方法也是一样。
作为对比,@Component
类中的@Bean方法或变量调用就是标准的Java语法。
声明静态@Bean方法可以在不创建它们的容器类的情况下被调用,比如定义各种BeanFactoryPostProcessor
或BeanPostProcessor
。
调用静态@Bean方法不会被容器拦截,这是因为技术限制:CGLIB子类仅可以覆盖非静态方法。
作为结果,在这种静态@Bean方法内调用别的@Bean方法是标准的Java语法,
将导致直接返回独立的bean实例。
Java语言的可见性并不会在Spring容器内与bean定义结果相冲突。
可以在非@Configuration类内随便定义@Bean工厂方法。
但是只能在@Configuration类内定义可被覆盖的@Bean工厂方法,不可以是private或者final。
Spring也会在@Component
或@Configurati
on类的基类或实现的接口的默认方法里检测@Bean方法。
单一的一个类可能含有多个bean工厂方法返回同一类型的bean,方法之间的区别在于需要的参数不一样。
最多参数被匹配的方法胜出。
给被检测到的组件类命名
@Component
,@Repository
,@Service
,@Controller
,@RestController
或@Configurati
on
注解有一个value属性,将这个类视为一个bean,就是这个bean的名称。
如果不提供value属性,默认返回小写的非全限定类名:
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
SimpleMovieLister类被发现时的名称为myMovieLister。
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
MovieFinderImpl类被发现时的名称为movieFinderImpl。
如果要定制一个bean命名策略,可以实现BeanNameGenerator
接口,必需要有无参数构造器。
然后在@ComponentScan里提供实现类的全限定类名:
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
// ...
}
或者xml里面:
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator" />
</beans>
最好不要自定义名称生成,用注解给定就够了。
组件类的作用域
默认作用域是singleton。可以通过@Scope注解声明:
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
作用域只用于组件类bean,不会被类里面的bean工厂方法继承。
如果要定制一个作用域解析策略,可以实现
接口,必需要有无参数构造器。ScopeMetadataResolver
然后在@ComponentScan里提供实现类的全限定类名:
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
// ...
}
在xml配置中:
<beans>
<context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>
当使用非singleton作用域时,可能要为被作用域修饰的对象生成代理。
通过@ComponentScan注解的scopedProxy属性设置:
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
// ...
}
xml配置:
<beans>
<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>
interfaces
指使用标准JDK动态代理。
三个可选的值为:no
,interfaces
,和targetClass
用于容器类的@Qualifier
和用于自动bean的@Qualifier一样,可以将这个注解用于类:
@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
只绑定类定义,不会继承给内部定义的bean。
生成候选组件索引
在编译时生成一个静态的候选列表对于大型程序可以提升启动速度。
现存的@ComponentScan
或<context:component-scan/>
不可以改动。
容器检测到有索引存在就会直接使用,不会再扫描classpath。
生成索引需要添加依赖,比如在Maven中添加:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>5.3.7</version>
<optional>true</optional>
</dependency>
</dependencies>
在项目打包成jar时会在META-INF/目录下生成spring.components文件。
spring-context-indexer必需被注册成注解处理器才能确保当组件更新时索引也同步更新。
可以通过在SpringProperties
里设置spring.index.ignore为true以切换回扫描classpath模式。
JSR 330标准注解
都用Spring了,Spring是它的超集,用Spring就好了。