Spring 3.0 將 Spring JavaConfig 併入核心,開啟了「純 Java 配置」時代。相比 XML,JavaConfig 兼具解耦性與編譯期類型檢查,重構時 IDE 可直接跳轉、重命名,杜絕拼寫錯誤。本文完整演示如何用 JavaConfig 搭建 SSH(Spring + SpringMVC + Hibernate)棧。
src/main/java
├── config
│ ├── WebInitializer.java // Servlet 3.0+ 啟動入口
│ ├── WebConfig.java // SpringMVC 配置(Web 層)
│ ├── RootConfig.java // 根容器配置(Service/DAO/數據源)
│ └── HibernateConfig.java // Hibernate 專用配置
├── entity
│ └── User.java // JPA 實體
├── service
│ └── UserService.java
├── dao
│ └── UserDao.java
└── test
└── ServiceTest.java // 整合測試

WebInitializer 替代 web.xml@Order(1)
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// 根容器:Service、DAO、DataSource、Hibernate
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfig.class };
}
// Web 容器:Controller、ViewResolver、HandlerMapping
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class };
}
// DispatcherServlet 映射路徑
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
// 可選:註冊 Filter(如 HiddenHttpMethodFilter 支持 PUT/DELETE)
@Override
protected Filter[] getServletFilters() {
return new Filter[] { new HiddenHttpMethodFilter() };
}
}
原理:實現 WebApplicationInitializer,Servlet 3.0 容器啟動時自動發現並調用 onStartup(),完成兩個容器創建與 DispatcherServlet 註冊。

| 容器 | 配置類 | 掃描包 | 典型 Bean |
|---|---|---|---|
| 根容器 (ContextLoaderListener) | RootConfig | scau.zzf 排除 .web | Service、DAO、DataSource、SessionFactory、TransactionManager |
| Web 容器 (DispatcherServlet) | WebConfig | scau.zzf.web | Controller、ViewResolver、Interceptor、Converter |
// RootConfig.java - 排除 Web 層,避免重複掃描
@Configuration
@Import(HibernateConfig.class)
@ComponentScan(basePackages = "scau.zzf", excludeFilters = @ComponentScan.Filter(
type = FilterType.CUSTOM, value = RootConfig.WebPackage.class))
public class RootConfig {
public static class WebPackage extends RegexPatternTypeFilter {
public WebPackage() { super(Pattern.compile("scau\\.zzf\\.web")); }
}
}
WebConfig@Configuration
@EnableWebMvc // 等同 <mvc:annotation-driven>
@ComponentScan("scau.zzf.web") // 掃描 Controller
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
// 靜態資源處理:轉發給容器默認 Servlet
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
HibernateConfig@Configuration
@PropertySource("classpath:ds/ds-jdbc.properties") // 載入外部屬性文件
public class HibernateConfig {
@Value("${jdbc.driverClassName}") private String driver;
@Value("${jdbc.url}") private String url;
@Value("${jdbc.username}") private String user;
@Value("${jdbc.password}") private String pass;
@Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(user);
ds.setPassword(pass);
return ds;
}
@Bean
public SessionFactory sessionFactory() throws IOException {
LocalSessionFactoryBean fb = new LocalSessionFactoryBean();
fb.setDataSource(dataSource());
fb.setPackagesToScan("scau.zzf.entity"); // 自動掃描 @Entity
Properties props = new Properties();
props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
fb.setHibernateProperties(props);
fb.afterPropertiesSet(); // 必須調用,初始化 SessionFactory
return fb.getObject();
}
@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer(); // 解析 ${...}
}
}
// User.java - JPA 註解映射
@Entity
@Table(catalog = "hibernate", name = "users")
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column private String username;
@Column private String password;
// ... getter/setter
}
// ServiceTest.java - 整合測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WebConfig.class, RootConfig.class})
@WebAppConfiguration
public class ServiceTest {
@Autowired private UserService userService;
@Test
public void testFindUser() {
User u = userService.findUser(1);
System.out.println(u.getUsername());
}
}
@Profile、@Conditional 靈活控制 Bean 載入@Import 輕鬆組裝配置類,替代 <import>.xml 配置文件LocalSessionFactoryBean.afterPropertiesSet() 必須顯式調用,否則 getObject() 返回 null@WebAppConfiguration,否則 WebApplicationContext 無法加載configurer.enable() 別漏,否則 CSS/JS 404classpath:ds/ds-jdbc.properties 需確保在 resources 目錄下「JavaConfig 不是為了炫技,而是為了讓配置像業務代碼一樣可維護、可重構、可測試。」
下一步嘗試:引入 @EnableTransactionManagement 開啟註解式事務、整合 HikariCP 替換 DBCP、配置二級緩存與查詢緩存。完整代碼已上傳 GitHub,歡迎 Star!⭐