首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Spring JavaConfig 完整指南:從 @Configuration 到混合 XML/Java 配置實戰

     編輯:Java 綜合教學

為什麼需要基於 Java 的配置?

Spring 3.0 引入 JavaConfig,旨在提供類型安全、IDE 友好、重構安全的配置方式。相比 XML,JavaConfig 具有編譯期檢查、代碼補全、易於調試等優勢,且能自然表達複雜的依賴關係。

核心註解速覽

註解作用關鍵屬性
@Configuration聲明配置類,內部含 @Bean 方法proxyBeanMethods (默認 true)
@Bean聲明 Bean 定義,相當於 XML <bean>name, initMethod, destroyMethod, autowire, autowireCandidate
@ComponentScan啟用組件掃描basePackages, basePackageClasses, includeFilters, excludeFilters
@Import導入其他配置類value (Class[])
@ImportResource導入 XML 配置文件locations
@PropertySource加載屬性文件value, encoding
@Value注入屬性值value (SpEL 表達式)

容器啟動三種方式

1. 純 JavaConfig 啟動

@Configuration
@ComponentScan("com.acme")
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

// 啟動
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService svc = ctx.getBean(MyService.class);

2. 多配置類組合(@Import)

@Configuration
public class ConfigA {
    @Bean public A a() { return new A(); }
}

@Configuration
@Import(ConfigA.class) // 導入 ConfigA
public class ConfigB {
    @Bean public B b() { return new B(); }
}

// 僅需啟動 ConfigB
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// A 和 B 均可獲取
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);

3. 編程式註冊

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh(); // 必須調用完成初始化
MyService svc = ctx.getBean(MyService.class);

@Bean 進階用法

生命周期回調

@Configuration
public class AppConfig {
    @Bean(initMethod = "init", destroyMethod = "cleanup")
    public Foo foo() { return new Foo(); }
    
    @Bean(destroyMethod = "close")
    public DataSource dataSource() { return new HikariDataSource(); }
}

// 等價於直接調用(更直觀)
@Bean
public Foo foo() {
    Foo foo = new Foo();
    foo.init(); // 手動調用
    return foo;
}

作用域與代理

@Bean
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Encryptor encryptor() { return new AesEncryptor(); }

// Web 作用域示例
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserPreferences userPreferences() { return new UserPreferences(); }

@Bean
public Service userService() {
    UserService svc = new SimpleUserService();
    svc.setUserPreferences(userPreferences()); // 注入代理對象
    return svc;
}

proxyMode 解決不同作用域 Bean 互相注入問題:單例注入會話作用域時,注入的是代理,實際調用時委託給當前會話的真實實例。

Bean 名稱與別名

@Bean(name = "myFoo", aliases = {"fooAlias1", "fooAlias2"})
public Foo foo() { return new Foo(); }

依賴注入:跨配置類裝配

關鍵原則:@Configuration 類本身也是 Bean,可使用 @Autowired

@Configuration
public class ServiceConfig {
    @Autowired private AccountRepository accountRepository; // 跨配置類注入
    
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {
    @Autowired private DataSource dataSource;
    
    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
    @Bean
    public DataSource dataSource() { return new EmbeddedDatabaseBuilder().build(); }
}

啟動 SystemTestConfig 即可完成全鏈路裝配。Spring 會自動解析配置類間的依賴圖。

Web 環境集成

AnnotationConfigWebApplicationContext

<web-app>
  <context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
  </context-param>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.acme.AppConfig</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextClass</param-name>
      <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </init-param>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>com.acme.web.MvcConfig</param-value>
    </init-param>
  </servlet>
</web-app>

混合 XML 與 Java 配置

XML 為主,導入 @Configuration 類

<beans>
  <context:annotation-config/> <!-- 啟用 @Autowired/@Configuration 等 -->
  <bean class="com.acme.AppConfig"/> <!-- 作為普通 Bean 註冊 -->
  <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  </bean>
</beans>

Java 為主,導入 XML(@ImportResource)

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
    @Value("${jdbc.url}") private String url;
    @Value("${jdbc.username}") private String username;
    @Value("${jdbc.password}") private String password;
    
    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}

// properties-config.xml
<beans>
  <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>

CGLIB 代理機制深度解析

@Configuration
public class AppConfig {
    @Bean
    public ClientService clientService1() {
        ClientServiceImpl svc = new ClientServiceImpl();
        svc.setClientDao(clientDao()); // 調用同類 @Bean 方法
        return svc;
    }
    
    @Bean
    public ClientService clientService2() {
        ClientServiceImpl svc = new ClientServiceImpl();
        svc.setClientDao(clientDao()); // 再次調用
        return svc;
    }
    
    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl(); // 單例,只會執行一次
    }
}

原理:Spring 啟動時通過 CGLIB 生成 AppConfig$$EnhancerBySpringCGLIB$$xxx 子類,重寫 @Bean 方法:首次調用創建實例並緩存,後續調用直接返回緩存對象。這保證了單例語義,也意味著 @Configuration 類不能是 final,方法不能是 private/final/static。

最佳實踐建議

  1. 配置類拆分:按業務域/層劃分(DataConfig, WebConfig, SecurityConfig),用 @Import 組合
  2. 優先使用構造器注入:@Bean 方法參數自動按類型裝配,無需 @Autowired
  3. 條件化配置:善用 @ConditionalOnMissingBean、@ConditionalOnProperty 實現模塊化開關
  4. 避免邏輯過重:@Bean 方法應保持簡單,複雜初始化移至 @PostConstruct 或 InitializingBean
  5. 測試友好:配置類即普通類,單元測試可直接 new 並手動裝配,無需啟動容器

JavaConfig 不會完全取代 XML(如命名空間、舊系統遷移),但作為現代 Spring 應用的首選配置方式,它將配置從「數據描述」提升為「可執行代碼」,賦予了開發者前所未有的控制力與表達力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved