1、概览 {#1概览}
本文将带你了解最常见的 Spring Bean 注解,用于定义不同类型的 Bean。
在 Spring 容器中配置 Bean 有几种方法。可以使用 XML 配置声明,也可以在配置类中使用 @Bean
注解声明 Bean。
最后,还可以使用 org.springframework.stereotype
包中的注解来标记类,然后由组件扫描来处理。
2、组件扫描 {#2组件扫描}
如果启用了组件扫描,Spring 就可以自动扫描包中的 Bean。
通过 @ComponentScan
注解配置要扫描的包,以查找带有注解配置的类。可以使用 basePackages
或 value
参数(value
是 basePackages
的别名)指定 base package:
@Configuration
@ComponentScan(basePackages = "com.baeldung.annotations")
class VehicleFactoryConfig {}
还可以使用 basePackageClasses
参数指定 base package 中的类:
@Configuration
@ComponentScan(basePackageClasses = VehicleFactoryConfig.class)
class VehicleFactoryConfig {}
这两个参数都是数组,因此可以为每个参数指定多个 package。
如果没有指定参数,将从 @ComponentScan
注解类所在的同一个包中开始扫描。
@ComponentScan
利用了 Java 8 的重复注解功能,这意味着可以多次使用它来标记一个类:
@Configuration
@ComponentScan(basePackages = "com.baeldung.annotations")
@ComponentScan(basePackageClasses = VehicleFactoryConfig.class)
class VehicleFactoryConfig {}
或者,也可以使用 @ComponentScans
来指定多个 @ComponentScan
配置:
@Configuration
@ComponentScans({
@ComponentScan(basePackages = "com.baeldung.annotations"),
@ComponentScan(basePackageClasses = VehicleFactoryConfig.class)
})
class VehicleFactoryConfig {}
使用 XML 配置时,配置组件扫描也同样简单:
<context:component-scan base-package="com.baeldung" />
3、@Component {#3component}
@Component
是一个类级注解。在组件扫描过程中,Spring 会自动检测注解为 @Component
的类:
@Component
class CarUtility {
// ...
}
默认情况下,Bean 实例名称与类名相同,首字母小写。也可以使用此注解的可选 value
参数指定不同的名称。
由于 @Repository
、@Service
、@Configuration
和 @Controller
都是 @Component
的元注解,因此它们共享相同的 Bean 命名行为。在组件扫描过程中,Spring 也会自动注册它们。
4、@Repository {#4repository}
DAO 或 Repository 类通常代表应用中的数据库访问层,应使用 @Repository
进行注解:
@Repository
class VehicleRepository {
// ...
}
使用该注解的一个好处是,它可以启用持久化异常自动翻译功能。当使用持久化框架(如 Hibernate
)时,在注解了 @Repository
的类中抛出的本地异常将自动翻译为 Spring 的 DataAccessExeption
子类。
要启用异常翻译,需要声明自己的 PersistenceExceptionTranslationPostProcessor
Bean:
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
注意,在大多数情况下,Spring 会自动执行上述步骤。
或通过 XML 配置:
<bean class=
"org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
5、@Service {#5service}
应用的业务逻辑通常位于 Service 层中,因此可以使用 @Service
注解来表示某个类属于 Service 层:
@Service
public class VehicleService {
// ...
}
6、@Controller {#6controller}
@Controller
是一个类级注解,它告诉 Spring 该类在 Spring MVC 中充当 Controller:
@Controller
public class VehicleController {
// ...
}
7、@Configuration {#7configuration}
配置类可以包含用 @Bean 注解的 Bean 定义方法:
@Configuration
class VehicleFactoryConfig {
@Bean
Engine engine() {
return new Engine();
}
}
8、Stereotype 注解和 AOP {#8stereotype-注解和-aop}
当使用 Spring 的 stereotype 注解时,很容易创建一个切点(Pointcut),以针对所有具有特定 stereotype 类。
例如,假设我们想计算 DAO 层方法的执行时间。可以利用 @Repository
stereotype 创建以下切面(使用 @AspectJ
注解):
@Aspect
@Component
public class PerformanceAspect {
@Pointcut("within(@org.springframework.stereotype.Repository *)")
public void repositoryClassMethods() {};
@Around("repositoryClassMethods()")
public Object measureMethodExecutionTime(ProceedingJoinPoint joinPoint)
throws Throwable {
long start = System.nanoTime();
Object returnValue = joinPoint.proceed();
long end = System.nanoTime();
String methodName = joinPoint.getSignature().getName();
System.out.println(
"Execution of " + methodName + " took " +
TimeUnit.NANOSECONDS.toMillis(end - start) + " ms");
return returnValue;
}
}
在本例中,我们创建了一个切点(Pointcut),匹配所有被 @Repository
注解标记的类中的方法。然后,使用 @Around
通知来针对该切点,并计算拦截的方法调用的执行时间。
通过这种方法,还可以为每个应用层添加日志记录、性能监控、审计和其他功能。
9、总结 {#9总结}
本文介绍了 Spring stereotype 注解,以及它们各自代表的语义类型。还介绍了如何通过配置组件扫描来自动注册 Bean。最后还介绍了这些注解如何带来简洁、分层的设计,以及如何将应用的关注点分离开来。
Ref:https://www.baeldung.com/spring-bean-annotations