Spring 全注解开发详解

Spring 全注解开发详解

一、什么是全注解开发

全注解开发是指完全摆脱 XML 配置文件,用 Java 类 + 注解来构建 Spring 应用。配置类替代了 applicationContext.xml,注解替代了 <bean>、<property>、<constructor-arg> 等标签。

传统 XML 方式需要维护多个配置文件,配置和代码分离,修改配置要改 XML。全注解方式把配置和代码放在一起,类上标注解就能被 Spring 管理,方法参数自动注入依赖,配置类替代 XML 文件。

Spring Boot 就是全注解开发的极致体现,但理解全注解开发,需要先掌握 Spring Framework 层面的注解体系。

二、从 XML 到注解的演进

XML 时代:

xml 复制代码
<bean id="userDao" class="com.example.UserDao"/>

<bean id="userService" class="com.example.UserService">
    <property name="userDao" ref="userDao"/>
</bean>

注解时代:

java 复制代码
@Repository
public class UserDao { }

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
}

配置从 XML 转移到了 Java 类上。类的职责和依赖关系一目了然,不需要在 XML 和 Java 之间来回切换。

三、核心注解体系

1. 声明 Bean

注解 语义 适用层
@Component 通用组件 任意
@Service 业务层 Service
@Repository 数据访问层 Dao
@Controller 控制层 Controller
@RestController REST 控制器 Controller
@Configuration 配置类 配置
java 复制代码
@Service
public class UserService { }

@Repository
public class UserDao { }

Bean 名称默认是类名首字母小写,可以显式指定:

java 复制代码
@Service("userService")
public class UserService { }

2. 配置类与 @Bean

@Configuration 标记一个类为配置类,替代 XML 文件。@Bean 标记方法,把返回值注册为 Bean。

java 复制代码
@Configuration
public class AppConfig {
    
    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        return ds;
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

@Configuration 类会被 CGLIB 代理,@Bean 方法之间互相调用返回容器中的单例。@Component 不会被代理,方法间调用直接执行方法体。

3. 组件扫描

java 复制代码
@Configuration
@ComponentScan("com.example")
public class AppConfig { }

@SpringBootApplication 已经包含了 @ComponentScan,默认扫描启动类所在包及其子包。

4. 依赖注入

java 复制代码
@Service
public class UserService {
    private final UserDao userDao;
    private final EmailService emailService;
    
    public UserService(UserDao userDao, EmailService emailService) {
        this.userDao = userDao;
        this.emailService = emailService;
    }
}

构造器注入是 Spring 官方推荐的方式。类只有一个构造方法时,@Autowired 可以省略。

5. 配置值注入

java 复制代码
@Component
public class AppConfig {
    @Value("${app.name}")
    private String appName;
    
    @Value("${app.timeout:30}")
    private int timeout;
}

或者用 @ConfigurationProperties 批量绑定:

java 复制代码
@Component
@ConfigurationProperties(prefix = "app")
@Getter
@Setter
public class AppProperties {
    private String name;
    private int timeout;
    private List<String> servers;
}

四、配置类详解

1. @Configuration 的本质

@Configuration 本身被 @Component 标注,所以它也是一个 Bean。但 Spring 对 @Configuration 类做了特殊处理------通过 CGLIB 生成子类,拦截 @Bean 方法的调用。

java 复制代码
@Configuration
public class AppConfig {
    
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }
    
    @Bean
    public UserService userService() {
        // 由于 CGLIB 代理,userDao() 返回容器中的单例
        return new UserService(userDao());
    }
}

如果换成 @Component,userDao() 每次调用都会执行方法体,创建新实例。

2. @Bean 方法参数注入

java 复制代码
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
    return new JdbcTemplate(dataSource);
}

Spring 会自动从容器中查找 DataSource 类型的 Bean 注入到方法参数。

3. @Bean 的生命周期回调

java 复制代码
@Bean(initMethod = "init", destroyMethod = "cleanup")
public MyBean myBean() {
    return new MyBean();
}

也可以在 Bean 类中用 @PostConstruct 和 @PreDestroy。

4. 条件化配置

java 复制代码
@Configuration
public class AppConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
    
    @Bean
    @ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true")
    public CacheManager cacheManager() {
        return new CaffeineCacheManager();
    }
}

5. 环境化配置

java 复制代码
@Configuration
@Profile("dev")
public class DevConfig {
    @Bean
    public DataSource devDataSource() { ... }
}

@Configuration
@Profile("prod")
public class ProdConfig {
    @Bean
    public DataSource prodDataSource() { ... }
}

五、AOP 全注解

1. 开启 AOP

java 复制代码
@Configuration
@EnableAspectJAutoProxy
public class AopConfig { }

Spring Boot 中引入 spring-boot-starter-aop 后自动生效。

2. 定义切面

java 复制代码
@Aspect
@Component
public class LoggingAspect {
    
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}
    
    @Around("serviceLayer()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = pjp.proceed();
        long cost = System.currentTimeMillis() - start;
        System.out.println(pjp.getSignature().getName() + " 耗时:" + cost + "ms");
        return result;
    }
}

五种通知注解:@Before、@After、@AfterReturning、@AfterThrowing、@Around。

3. 自定义注解 + AOP

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

@Aspect
@Component
public class LogAspect {
    @Around("@annotation(log)")
    public Object log(ProceedingJoinPoint pjp, Log log) throws Throwable {
        System.out.println("执行:" + log.value());
        return pjp.proceed();
    }
}

六、事务全注解

java 复制代码
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

业务方法上使用 @Transactional:

java 复制代码
@Service
public class OrderService {
    
    @Transactional(rollbackFor = Exception.class, timeout = 30)
    public void createOrder(Order order) {
        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:

java 复制代码
public class WebAppInitializer implements WebApplicationInitializer {
    
    @Override
    public void onStartup(ServletContext servletContext) {
        // 创建 Spring 根容器
        AnnotationConfigWebApplicationContext rootContext = 
            new AnnotationConfigWebApplicationContext();
        rootContext.register(AppConfig.class);
        
        // 创建 Spring MVC 容器
        AnnotationConfigWebApplicationContext webContext = 
            new AnnotationConfigWebApplicationContext();
        webContext.register(WebConfig.class);
        
        // 注册 DispatcherServlet
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
            "dispatcher", new DispatcherServlet(webContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }
}

2. WebMvcConfigurer

替代 spring-mvc.xml:

java 复制代码
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/register");
    }
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/static/");
    }
    
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new MappingJackson2HttpMessageConverter());
    }
}

3. 控制器

java 复制代码
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getById(id);
    }
    
    @PostMapping
    public User create(@RequestBody @Valid User user) {
        return userService.save(user);
    }
}

4. 全局异常处理

java 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusiness(BusinessException e) {
        return ResponseEntity.badRequest()
                .body(new ErrorResponse(e.getMessage()));
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception e) {
        return ResponseEntity.status(500)
                .body(new ErrorResponse("服务器内部错误"));
    }
}

八、全注解项目结构

复制代码
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 方法返回类型与注入类型不匹配

java 复制代码
@Bean
public DataSource dataSource() {
    return new HikariDataSource();  // 返回 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 自动配置原理的基础。

相关推荐
福建佰胜张工5 小时前
SMC 薄型气爪 MHF2-16D1 深度解析:平行开闭、低剖面结构在自动化装配中的选型与落地
java·服务器·安全·自动化
念何架构之路5 小时前
go-grpc设计思想
开发语言·后端·golang
catoop5 小时前
API Key 设计与安全生成方案:结构拆解、数据库设计与 Java 实现
java·数据库·安全
system_error_x5 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
驭渊的小故事6 小时前
Spring AI Alibaba 实战:从maven依赖治理到声明式工具调用
java·人工智能·spring
Elastic 中国社区官方博客6 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1886 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn7 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
y1su7 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
码事漫谈8 小时前
AI 读你的提示词,其实很没耐心
后端