一、纯注解配置核心价值
传统 Spring 开发中大量 XML 配置存在配置繁琐、可读性差、维护成本高的问题,纯注解配置通过 Java 代码替代 XML 文件,实现配置与代码一体化,具备以下优势:
- 类型安全:编译期即可发现配置错误,而非运行期
- 灵活性高:可通过 Java 代码实现动态配置逻辑
- 简洁易维护:配置集中在注解类中,便于团队协作与版本管理
二、核心注解深度解析
1. 配置类核心注解
|-------------------------------|-----------------------------------------------------------|----------------------------------|-------------------------------------------------------------------------------|
| 注解 | 作用 | 替代 XML 标签 | 关键属性 |
| @Configuration | 标记类为 Spring 配置类 | <beans> | 无核心属性,仅作标识 |
| @ComponentScan | 扫描指定包下的组件(@Controller/ @Service/ @Repository/ @Component) | <context: component-scan> | basePackages/value:指定扫描包;excludeFilters:排除指定组件;includeFilters:仅包含指定组件 |
| @PropertySource | 加载外部 properties 配置文件 | <context:property-placeholder> | value:文件路径(类路径需加classpath:);encoding:指定文件编码 |
| @Import | 导入其他配置类,实现配置拆分 | <import resource=""> | value:需导入的配置类字节码数组 |
| @EnableWebMvc | 开启 SpringMVC 注解驱动 | <mvc:annotation-driven> | 无,自动注册 HandlerMapping/HandlerAdapter 等核心组件 |
| @EnableTransaction Management | 开启注解事务支持 | <tx:annotation-driven> | proxyTargetClass:是否使用 CGLIB 代理(默认 false) |
2. Bean 定义核心注解
| 注解 | 作用 | 替代 XML 标签 | 关键属性 |
|---|---|---|---|
@Bean |
方法级别注解,将方法返回对象注册为 Spring Bean | <bean> |
name/value:指定 Bean 名称;initMethod:初始化方法;destroyMethod:销毁方法 |
@Value |
注入配置属性或字面量 | <property value=""> |
支持 SpEL 表达式(如${jdbc.url}、#{T(java.lang.Math).random()}) |
三、分模块纯注解配置实战
1. 工程结构规划(分层配置)
com.hg
├── config // 配置类目录
│ ├── JdbcConfig.java // 数据源配置
│ ├── MyBatisConfig.java // MyBatis配置
│ ├── TxConfig.java // 事务配置
│ ├── ServiceConfig.java // 服务层配置
│ ├── SpringMvcConfig.java // SpringMVC配置
│ ├── SpringConfig.java // 核心配置(整合所有子配置)
│ └── WebConfig.java // Web容器配置(替代web.xml)
├── controller // 控制器层
├── service // 服务层
├── mapper // Mapper接口层
├── pojo // 实体类
├── converter // 类型转换器
├── interceptor // 拦截器
└── resources
└── db.properties // 数据库配置文件
2. 各模块配置实现(完整可运行版)
(1)数据库配置(JdbcConfig.java)
java
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.SQLException;
// 加载数据库配置文件,指定UTF-8编码防止中文乱码
@PropertySource(value = "classpath:db.properties", encoding = "UTF-8")
public class JdbcConfig {
// 注入配置文件中的属性
@Value("${jdbc.driver}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.maxActive:10}") // 设置默认值,避免配置缺失报错
private int maxActive;
@Value("${jdbc.minIdle:5}")
private int minIdle;
/**
* 配置Druid数据源
* @return 数据源Bean
*/
@Bean(name = "dataSource", destroyMethod = "close") // 指定销毁方法,释放连接池
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
// 连接池参数配置
dataSource.setMaxActive(maxActive);
dataSource.setMinIdle(minIdle);
// 可选:配置连接池其他参数
dataSource.setInitialSize(5);
dataSource.setMaxWait(60000);
// 开启Druid监控(可选)
try {
dataSource.setFilters("stat,wall");
} catch (SQLException e) {
throw new RuntimeException("Druid过滤器配置失败", e);
}
return dataSource;
}
}
(2)MyBatis 配置(MyBatisConfig.java)
java
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
public class MyBatisConfig {
/**
* 配置SqlSessionFactory
* @param dataSource 自动注入JdbcConfig中配置的数据源
* @return SqlSessionFactoryBean
*/
@Bean(name = "sqlSessionFactoryBean")
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
// 设置数据源
factoryBean.setDataSource(dataSource);
// 设置实体类别名包(无需在Mapper中写全类名)
factoryBean.setTypeAliasesPackage("com.hg.pojo");
// 可选:配置MyBatis全局配置文件
// factoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
// 可选:配置Mapper文件位置(若Mapper接口与XML文件分离)
// factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return factoryBean;
}
/**
* 配置Mapper扫描器,自动生成Mapper代理类
* @return MapperScannerConfigurer
*/
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
// 指定Mapper接口所在包
scannerConfigurer.setBasePackage("com.hg.mapper");
// 指定SqlSessionFactory名称(与上面的Bean名称对应)
scannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactoryBean");
return scannerConfigurer;
}
}
(3)事务配置(TxConfig.java)
java
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
// 开启事务注解支持
@EnableTransactionManagement
public class TxConfig {
/**
* 配置事务管理器
* @param dataSource 自动注入数据源
* @return 事务管理器
*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
(4)服务层配置(ServiceConfig.java)
java
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
// 扫描服务层组件,开启AOP代理(可选,若使用AOP)
@ComponentScan("com.hg.service")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ServiceConfig {
}
(5)Spring 核心配置(SpringConfig.java)
java
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
// 核心配置类,整合所有子配置
@Configuration
@Import({
JdbcConfig.class,
MyBatisConfig.class,
TxConfig.class,
ServiceConfig.class
})
public class SpringConfig {
}
(6)SpringMVC 配置(SpringMvcConfig.java)
java
import com.hg.converter.MyDateConverter;
import com.hg.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
// 扫描控制器、异常处理器等组件
@ComponentScan({"com.hg.controller", "com.hg.exception"})
// 开启SpringMVC注解驱动
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
/**
* 注册日期转换器
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new MyDateConverter());
}
/**
* 配置视图解析器
*/
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/pages/"); // JSP文件前缀
resolver.setSuffix(".jsp"); // JSP文件后缀
resolver.setContentType("text/html;charset=UTF-8"); // 响应编码
return resolver;
}
/**
* 配置文件上传解析器
*/
@Bean(name = "multipartResolver") // 名称必须为multipartResolver,否则SpringMVC无法识别
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(5 * 1024 * 1024); // 最大上传大小5MB
resolver.setDefaultEncoding("UTF-8"); // 上传文件编码
resolver.setMaxInMemorySize(1024 * 1024); // 内存缓存大小
return resolver;
}
/**
* 配置静态资源映射(放行无需DispatcherServlet处理的资源)
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/head/**") // 访问路径
.addResourceLocations("/head/"); // 实际文件路径
// 可添加多个静态资源映射
// registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}
/**
* 配置拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/account/**") // 拦截/account下所有请求
.excludePathPatterns("/user/login", "/user/register"); // 排除登录/注册请求
}
}
(7)Web 容器配置(WebConfig.java,替代 web.xml)
java
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import javax.servlet.Filter;
/**
* 替代web.xml的核心类,Servlet3.0+容器会自动检测并初始化
*/
public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
/**
* 配置Spring根容器(非Web层)配置类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/**
* 配置SpringMVC容器(Web层)配置类
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
/**
* 配置DispatcherServlet的映射路径
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; // 拦截所有请求(除静态资源)
}
/**
* 配置过滤器(解决POST请求中文乱码)
*/
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceEncoding(true); // 强制所有请求使用指定编码
return new Filter[]{encodingFilter};
}
}
3. 简化后的 web.xml
四、关键注意事项
- Bean 名称匹配 :
@Bean注解的 name 属性需与依赖注入处的名称一致(如multipartResolver不可改名); - 依赖注入顺序 :通过
@Import导入的配置类,Spring 会按数组顺序加载,需保证先加载数据源,再加载 MyBatis / 事务配置; - Servlet 版本要求 :
AbstractAnnotationConfigDispatcherServletInitializer依赖 Servlet3.0+,需保证 web.xml 版本≥3.0 或使用无 web.xml 的纯注解部署; - Druid 数据源销毁 :配置
@Bean(destroyMethod = "close")确保容器关闭时释放连接池; - 静态资源放行 :必须通过
addResourceHandlers配置,否则 DispatcherServlet 会拦截静态资源请求; - 事务注解生效 :Service 层方法需添加
@Transactional,且事务管理器 Bean 名称需为transactionManager(默认匹配)。
五、纯注解配置与 XML 配置对比
| 功能 | XML 配置 | 注解配置 |
|---|---|---|
| 数据源配置 | <bean class="DruidDataSource"> + <property> |
@Bean + @Value + @PropertySource |
| 组件扫描 | <context:component-scan> |
@ComponentScan |
| 事务管理 | <tx:annotation-driven> + <bean class="DataSourceTransactionManager"> |
@EnableTransactionManagement + @Bean(事务管理器) |
| SpringMVC 注解驱动 | <mvc:annotation-driven> |
@EnableWebMvc |
| Web 容器配置 | web.xml(监听器 / 过滤器 / Servlet) | AbstractAnnotationConfigDispatcherServletInitializer |
六、扩展与优化
- 配置拆分与复用 :按功能拆分配置类(如 JdbcConfig/MyBatisConfig),通过
@Import整合,便于不同环境复用; - 多环境配置 :结合
@Profile注解实现开发 / 测试 / 生产环境配置隔离(如@Profile("dev")标注开发环境数据源); - 配置校验 :通过
@Validated+ JSR303 注解校验配置属性(如@Value("${jdbc.maxActive}") @Min(5) private int maxActive); - 注解属性占位符 :使用
@PropertySources(复数)加载多个配置文件,解决@PropertySource单次只能加载一个的问题。
通过以上配置,可完全脱离 XML 文件实现 Spring + SpringMVC + MyBatis 的全注解开发,大幅提升开发效率与配置可维护性。