Spring 全注解开发详解一、什么是全注解开发全注解开发是指完全摆脱 XML 配置文件用 Java 类 注解来构建 Spring 应用。配置类替代了applicationContext.xml注解替代了bean、property、constructor-arg等标签。传统 XML 方式需要维护多个配置文件配置和代码分离修改配置要改 XML。全注解方式把配置和代码放在一起类上标注解就能被 Spring 管理方法参数自动注入依赖配置类替代 XML 文件。Spring Boot 就是全注解开发的极致体现但理解全注解开发需要先掌握 Spring Framework 层面的注解体系。二、从 XML 到注解的演进XML 时代beaniduserDaoclasscom.example.UserDao/beaniduserServiceclasscom.example.UserServicepropertynameuserDaorefuserDao//bean注解时代RepositorypublicclassUserDao{}ServicepublicclassUserService{AutowiredprivateUserDaouserDao;}配置从 XML 转移到了 Java 类上。类的职责和依赖关系一目了然不需要在 XML 和 Java 之间来回切换。三、核心注解体系1. 声明 Bean注解语义适用层Component通用组件任意Service业务层ServiceRepository数据访问层DaoController控制层ControllerRestControllerREST 控制器ControllerConfiguration配置类配置ServicepublicclassUserService{}RepositorypublicclassUserDao{}Bean 名称默认是类名首字母小写可以显式指定Service(userService)publicclassUserService{}2. 配置类与 BeanConfiguration标记一个类为配置类替代 XML 文件。Bean标记方法把返回值注册为 Bean。ConfigurationpublicclassAppConfig{BeanpublicDataSourcedataSource(){HikariDataSourcedsnewHikariDataSource();ds.setJdbcUrl(jdbc:mysql://localhost:3306/mydb);returnds;}BeanpublicJdbcTemplatejdbcTemplate(DataSourcedataSource){returnnewJdbcTemplate(dataSource);}}Configuration类会被 CGLIB 代理Bean方法之间互相调用返回容器中的单例。Component不会被代理方法间调用直接执行方法体。3. 组件扫描ConfigurationComponentScan(com.example)publicclassAppConfig{}SpringBootApplication已经包含了ComponentScan默认扫描启动类所在包及其子包。4. 依赖注入ServicepublicclassUserService{privatefinalUserDaouserDao;privatefinalEmailServiceemailService;publicUserService(UserDaouserDao,EmailServiceemailService){this.userDaouserDao;this.emailServiceemailService;}}构造器注入是 Spring 官方推荐的方式。类只有一个构造方法时Autowired可以省略。5. 配置值注入ComponentpublicclassAppConfig{Value(${app.name})privateStringappName;Value(${app.timeout:30})privateinttimeout;}或者用ConfigurationProperties批量绑定ComponentConfigurationProperties(prefixapp)GetterSetterpublicclassAppProperties{privateStringname;privateinttimeout;privateListStringservers;}四、配置类详解1. Configuration 的本质Configuration本身被Component标注所以它也是一个 Bean。但 Spring 对Configuration类做了特殊处理——通过 CGLIB 生成子类拦截Bean方法的调用。ConfigurationpublicclassAppConfig{BeanpublicUserDaouserDao(){returnnewUserDao();}BeanpublicUserServiceuserService(){// 由于 CGLIB 代理userDao() 返回容器中的单例returnnewUserService(userDao());}}如果换成ComponentuserDao()每次调用都会执行方法体创建新实例。2. Bean 方法参数注入BeanpublicJdbcTemplatejdbcTemplate(DataSourcedataSource){returnnewJdbcTemplate(dataSource);}Spring 会自动从容器中查找DataSource类型的 Bean 注入到方法参数。3. Bean 的生命周期回调Bean(initMethodinit,destroyMethodcleanup)publicMyBeanmyBean(){returnnewMyBean();}也可以在 Bean 类中用PostConstruct和PreDestroy。4. 条件化配置ConfigurationpublicclassAppConfig{BeanConditionalOnMissingBeanpublicObjectMapperobjectMapper(){returnnewObjectMapper();}BeanConditionalOnProperty(nameapp.cache.enabled,havingValuetrue)publicCacheManagercacheManager(){returnnewCaffeineCacheManager();}}5. 环境化配置ConfigurationProfile(dev)publicclassDevConfig{BeanpublicDataSourcedevDataSource(){...}}ConfigurationProfile(prod)publicclassProdConfig{BeanpublicDataSourceprodDataSource(){...}}五、AOP 全注解1. 开启 AOPConfigurationEnableAspectJAutoProxypublicclassAopConfig{}Spring Boot 中引入spring-boot-starter-aop后自动生效。2. 定义切面AspectComponentpublicclassLoggingAspect{Pointcut(execution(* com.example.service.*.*(..)))publicvoidserviceLayer(){}Around(serviceLayer())publicObjectaround(ProceedingJoinPointpjp)throwsThrowable{longstartSystem.currentTimeMillis();Objectresultpjp.proceed();longcostSystem.currentTimeMillis()-start;System.out.println(pjp.getSignature().getName() 耗时costms);returnresult;}}五种通知注解Before、After、AfterReturning、AfterThrowing、Around。3. 自定义注解 AOPTarget(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)publicinterfaceLog{Stringvalue()default;}AspectComponentpublicclassLogAspect{Around(annotation(log))publicObjectlog(ProceedingJoinPointpjp,Loglog)throwsThrowable{System.out.println(执行log.value());returnpjp.proceed();}}六、事务全注解ConfigurationEnableTransactionManagementpublicclassTransactionConfig{BeanpublicPlatformTransactionManagertransactionManager(DataSourcedataSource){returnnewDataSourceTransactionManager(dataSource);}}业务方法上使用TransactionalServicepublicclassOrderService{Transactional(rollbackForException.class,timeout30)publicvoidcreateOrder(Orderorder){orderDao.save(order);inventoryDao.decrease(order.getProductId(),order.getQuantity());}}Spring Boot 中DataSourceTransactionManager自动配置不需要手动声明。七、Web 层全注解1. 传统 Spring MVC 配置XML 时代需要web.xmlspring-mvc.xml。全注解方式用WebApplicationInitializer替代web.xmlpublicclassWebAppInitializerimplementsWebApplicationInitializer{OverridepublicvoidonStartup(ServletContextservletContext){// 创建 Spring 根容器AnnotationConfigWebApplicationContextrootContextnewAnnotationConfigWebApplicationContext();rootContext.register(AppConfig.class);// 创建 Spring MVC 容器AnnotationConfigWebApplicationContextwebContextnewAnnotationConfigWebApplicationContext();webContext.register(WebConfig.class);// 注册 DispatcherServletServletRegistration.DynamicdispatcherservletContext.addServlet(dispatcher,newDispatcherServlet(webContext));dispatcher.setLoadOnStartup(1);dispatcher.addMapping(/);}}2. WebMvcConfigurer替代spring-mvc.xmlConfigurationEnableWebMvcComponentScan(com.example.controller)publicclassWebConfigimplementsWebMvcConfigurer{OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newLoginInterceptor()).addPathPatterns(/**).excludePathPatterns(/login,/register);}OverridepublicvoidaddResourceHandlers(ResourceHandlerRegistryregistry){registry.addResourceHandler(/static/**).addResourceLocations(classpath:/static/);}OverridepublicvoidconfigureMessageConverters(ListHttpMessageConverter?converters){converters.add(newMappingJackson2HttpMessageConverter());}}3. 控制器RestControllerRequestMapping(/api/users)publicclassUserController{AutowiredprivateUserServiceuserService;GetMapping(/{id})publicUsergetUser(PathVariableLongid){returnuserService.getById(id);}PostMappingpublicUsercreate(RequestBodyValidUseruser){returnuserService.save(user);}}4. 全局异常处理RestControllerAdvicepublicclassGlobalExceptionHandler{ExceptionHandler(BusinessException.class)publicResponseEntityErrorResponsehandleBusiness(BusinessExceptione){returnResponseEntity.badRequest().body(newErrorResponse(e.getMessage()));}ExceptionHandler(Exception.class)publicResponseEntityErrorResponsehandleException(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 BootSpring Boot 中不要手动加EnableWebMvc它会关闭 Spring Boot 的 Web 自动配置。需要自定义 Web 配置时实现WebMvcConfigurer接口即可。十一、总结分类核心注解声明 BeanComponent、Service、Repository、Controller、RestController配置类Configuration、Bean、ComponentScan、Import依赖注入Autowired、Qualifier、Resource、Value配置绑定ConfigurationProperties、PropertySource条件化Conditional、ConditionalOnXxx、ProfileAOPAspect、Pointcut、Before、Around事务EnableTransactionManagement、TransactionalWebEnableWebMvc、WebMvcConfigurer、RestControllerAdvice测试SpringBootTest、MockBean全注解开发是 Spring 现代开发的主流方式。它把配置从 XML 转移到 Java 类和注解上让代码和配置合一类型安全重构友好。Spring Boot 在此基础上进一步简化通过自动配置和 Starter 机制让开发者几乎不需要写配置类。理解全注解开发是掌握 Spring Boot 自动配置原理的基础。