Spring 全注解开发详解
一、什么是全注解开发
全注解开发是指完全摆脱 XML 配置文件,用 Java 类 + 注解来构建 Spring 应用。配置类替代了applicationContext.xml,注解替代了<bean>、<property>、<constructor-arg>等标签。
传统 XML 方式需要维护多个配置文件,配置和代码分离,修改配置要改 XML。全注解方式把配置和代码放在一起,类上标注解就能被 Spring 管理,方法参数自动注入依赖,配置类替代 XML 文件。
Spring Boot 就是全注解开发的极致体现,但理解全注解开发,需要先掌握 Spring Framework 层面的注解体系。
二、从 XML 到注解的演进
XML 时代:
<beanid="userDao"class="com.example.UserDao"/><beanid="userService"class="com.example.UserService"><propertyname="userDao"ref="userDao"/></bean>注解时代:
@RepositorypublicclassUserDao{}@ServicepublicclassUserService{@AutowiredprivateUserDaouserDao;}配置从 XML 转移到了 Java 类上。类的职责和依赖关系一目了然,不需要在 XML 和 Java 之间来回切换。
三、核心注解体系
1. 声明 Bean
| 注解 | 语义 | 适用层 |
|---|---|---|
@Component | 通用组件 | 任意 |
@Service | 业务层 | Service |
@Repository | 数据访问层 | Dao |
@Controller | 控制层 | Controller |
@RestController | REST 控制器 | Controller |
@Configuration | 配置类 | 配置 |
@ServicepublicclassUserService{}@RepositorypublicclassUserDao{}Bean 名称默认是类名首字母小写,可以显式指定:
@Service("userService")publicclassUserService{}2. 配置类与 @Bean
@Configuration标记一个类为配置类,替代 XML 文件。@Bean标记方法,把返回值注册为 Bean。
@ConfigurationpublicclassAppConfig{@BeanpublicDataSourcedataSource(){HikariDataSourceds=newHikariDataSource();ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");returnds;}@BeanpublicJdbcTemplatejdbcTemplate(DataSourcedataSource){returnnewJdbcTemplate(dataSource);}}@Configuration类会被 CGLIB 代理,@Bean方法之间互相调用返回容器中的单例。@Component不会被代理,方法间调用直接执行方法体。
3. 组件扫描
@Configuration@ComponentScan("com.example")publicclassAppConfig{}@SpringBootApplication已经包含了@ComponentScan,默认扫描启动类所在包及其子包。
4. 依赖注入
@ServicepublicclassUserService{privatefinalUserDaouserDao;privatefinalEmailServiceemailService;publicUserService(UserDaouserDao,EmailServiceemailService){this.userDao=userDao;this.emailService=emailService;}}构造器注入是 Spring 官方推荐的方式。类只有一个构造方法时,@Autowired可以省略。
5. 配置值注入
@ComponentpublicclassAppConfig{@Value("${app.name}")privateStringappName;@Value("${app.timeout:30}")privateinttimeout;}或者用@ConfigurationProperties批量绑定:
@Component@ConfigurationProperties(prefix="app")@Getter@SetterpublicclassAppProperties{privateStringname;privateinttimeout;privateList<String>servers;}四、配置类详解
1. @Configuration 的本质
@Configuration本身被@Component标注,所以它也是一个 Bean。但 Spring 对@Configuration类做了特殊处理——通过 CGLIB 生成子类,拦截@Bean方法的调用。
@ConfigurationpublicclassAppConfig{@BeanpublicUserDaouserDao(){returnnewUserDao();}@BeanpublicUserServiceuserService(){// 由于 CGLIB 代理,userDao() 返回容器中的单例returnnewUserService(userDao());}}如果换成@Component,userDao()每次调用都会执行方法体,创建新实例。
2. @Bean 方法参数注入
@BeanpublicJdbcTemplatejdbcTemplate(DataSourcedataSource){returnnewJdbcTemplate(dataSource);}Spring 会自动从容器中查找DataSource类型的 Bean 注入到方法参数。
3. @Bean 的生命周期回调
@Bean(initMethod="init",destroyMethod="cleanup")publicMyBeanmyBean(){returnnewMyBean();}也可以在 Bean 类中用@PostConstruct和@PreDestroy。
4. 条件化配置
@ConfigurationpublicclassAppConfig{@Bean@ConditionalOnMissingBeanpublicObjectMapperobjectMapper(){returnnewObjectMapper();}@Bean@ConditionalOnProperty(name="app.cache.enabled",havingValue="true")publicCacheManagercacheManager(){returnnewCaffeineCacheManager();}}5. 环境化配置
@Configuration@Profile("dev")publicclassDevConfig{@BeanpublicDataSourcedevDataSource(){...}}@Configuration@Profile("prod")publicclassProdConfig{@BeanpublicDataSourceprodDataSource(){...}}五、AOP 全注解
1. 开启 AOP
@Configuration@EnableAspectJAutoProxypublicclassAopConfig{}Spring Boot 中引入spring-boot-starter-aop后自动生效。
2. 定义切面
@Aspect@ComponentpublicclassLoggingAspect{@Pointcut("execution(* com.example.service.*.*(..))")publicvoidserviceLayer(){}@Around("serviceLayer()")publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{longstart=System.currentTimeMillis();Objectresult=pjp.proceed();longcost=System.currentTimeMillis()-start;System.out.println(pjp.getSignature().getName()+" 耗时:"+cost+"ms");returnresult;}}五种通知注解:@Before、@After、@AfterReturning、@AfterThrowing、@Around。
3. 自定义注解 + AOP
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceLog{Stringvalue()default"";}@Aspect@ComponentpublicclassLogAspect{@Around("@annotation(log)")publicObjectlog(ProceedingJoinPointpjp,Loglog)throwsThrowable{System.out.println("执行:"+log.value());returnpjp.proceed();}}六、事务全注解
@Configuration@EnableTransactionManagementpublicclassTransactionConfig{@BeanpublicPlatformTransactionManagertransactionManager(DataSourcedataSource){returnnewDataSourceTransactionManager(dataSource);}}业务方法上使用@Transactional:
@ServicepublicclassOrderService{@Transactional(rollbackFor=Exception.class,timeout=30)publicvoidcreateOrder(Orderorder){orderDao.save(order);inventoryDao.decrease(order.getProductId(),order.getQuantity());}}Spring Boot 中DataSourceTransactionManager自动配置,不需要手动声明。
七、Web 层全注解
1. 传统 Spring MVC 配置
XML 时代需要web.xml+spring-mvc.xml。全注解方式用WebApplicationInitializer替代web.xml:
publicclassWebAppInitializerimplementsWebApplicationInitializer{@OverridepublicvoidonStartup(ServletContextservletContext){// 创建 Spring 根容器AnnotationConfigWebApplicationContextrootContext=newAnnotationConfigWebApplicationContext();rootContext.register(AppConfig.class);// 创建 Spring MVC 容器AnnotationConfigWebApplicationContextwebContext=newAnnotationConfigWebApplicationContext();webContext.register(WebConfig.class);// 注册 DispatcherServletServletRegistration.Dynamicdispatcher=servletContext.addServlet("dispatcher",newDispatcherServlet(webContext));dispatcher.setLoadOnStartup(1);dispatcher.addMapping("/");}}2. WebMvcConfigurer
替代spring-mvc.xml:
@Configuration@EnableWebMvc@ComponentScan("com.example.controller")publicclassWebConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newLoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/login","/register");}@OverridepublicvoidaddResourceHandlers(ResourceHandlerRegistryregistry){registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");}@OverridepublicvoidconfigureMessageConverters(List<HttpMessageConverter<?>>converters){converters.add(newMappingJackson2HttpMessageConverter());}}3. 控制器
@RestController@RequestMapping("/api/users")publicclassUserController{@AutowiredprivateUserServiceuserService;@GetMapping("/{id}")publicUsergetUser(@PathVariableLongid){returnuserService.getById(id);}@PostMappingpublicUsercreate(@RequestBody@ValidUseruser){returnuserService.save(user);}}4. 全局异常处理
@RestControllerAdvicepublicclassGlobalExceptionHandler{@ExceptionHandler(BusinessException.class)publicResponseEntity<ErrorResponse>handleBusiness(BusinessExceptione){returnResponseEntity.badRequest().body(newErrorResponse(e.getMessage()));}@ExceptionHandler(Exception.class)publicResponseEntity<ErrorResponse>handleException(Exceptione){returnResponseEntity.status(500).body(newErrorResponse("服务器内部错误"));}}八、全注解项目结构
src/main/java/com/example/ ├── config/ │ ├── AppConfig.java # 根配置 │ ├── WebConfig.java # Web 配置 │ ├── AopConfig.java # AOP 配置 │ └── TransactionConfig.java # 事务配置 ├── controller/ │ └── UserController.java ├── service/ │ ├── UserService.java │ └── impl/ │ └── UserServiceImpl.java ├── dao/ │ └── UserDao.java ├── entity/ │ └── User.java └── WebAppInitializer.java # 替代 web.xml九、全注解 vs XML
| 对比维度 | 全注解 | XML |
|---|---|---|
| 配置位置 | 代码内 | 独立文件 |
| 可读性 | 类与配置合一 | 配置集中但分离 |
| 修改成本 | 需重新编译 | 改 XML 即可 |
| 类型安全 | 编译期检查 | 运行时才发现错误 |
| 重构支持 | IDE 自动重构 | 手动修改 |
| 适用场景 | 业务类、Controller、Service | 第三方类、动态配置 |
| 现代趋势 | 主流 | 逐步淘汰 |
混合使用:自己写的类用注解,第三方类用@Bean方法或 XML。全注解不等于完全不用 XML,而是以注解为主。
十、常见问题
1. @Configuration 与 @Component 混用
@Configuration类会被 CGLIB 代理,@Bean方法间调用返回单例。@Component不会被代理,方法间调用创建新实例。配置类一律用@Configuration。
2. @Bean 方法返回类型与注入类型不匹配
@BeanpublicDataSourcedataSource(){returnnewHikariDataSource();// 返回 HikariDataSource,注册为 DataSource 类型}注入时按DataSource类型查找可以找到,按HikariDataSource查找也可以(Spring 会判断实际类型)。
3. 组件扫描范围
@ComponentScan默认扫描当前类所在包及其子包。启动类放在根包下,确保所有组件都在扫描范围内。
4. 循环依赖
构造器注入的循环依赖无法解决,启动报BeanCurrentlyInCreationException。字段注入可以通过三级缓存解决,但应通过重构消除循环依赖。
5. @EnableWebMvc 与 Spring Boot
Spring Boot 中不要手动加@EnableWebMvc,它会关闭 Spring Boot 的 Web 自动配置。需要自定义 Web 配置时,实现WebMvcConfigurer接口即可。
十一、总结
| 分类 | 核心注解 |
|---|---|
| 声明 Bean | @Component、@Service、@Repository、@Controller、@RestController |
| 配置类 | @Configuration、@Bean、@ComponentScan、@Import |
| 依赖注入 | @Autowired、@Qualifier、@Resource、@Value |
| 配置绑定 | @ConfigurationProperties、@PropertySource |
| 条件化 | @Conditional、@ConditionalOnXxx、@Profile |
| AOP | @Aspect、@Pointcut、@Before、@Around |
| 事务 | @EnableTransactionManagement、@Transactional |
| Web | @EnableWebMvc、WebMvcConfigurer、@RestControllerAdvice |
| 测试 | @SpringBootTest、@MockBean |
全注解开发是 Spring 现代开发的主流方式。它把配置从 XML 转移到 Java 类和注解上,让代码和配置合一,类型安全,重构友好。Spring Boot 在此基础上进一步简化,通过自动配置和 Starter 机制,让开发者几乎不需要写配置类。理解全注解开发,是掌握 Spring Boot 自动配置原理的基础。