Spring Java-based容器配置(二)

### 组装Java-based的配置

* 使用@Import注解

跟在Spring XML文件中使用`<import>`元素添加模块化的配置类似,@Import注解允许你加载其他配置类中的@Bean定义:

```java

@Configuration

public class ConfigA {

@Bean

public A a() {

return new A();

}

}

@Configuration

@Import(ConfigA.class)

public class ConfigB {

@Bean

public B b() {

return new B();

}

}

```

现在,当实例化上下文时,你只需要显式的指定ConfigB,而不需要既提供ConfigA.class,又提供ConfigB.class:

```java

public static void main(String[] args) {

ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

// now both beans A and B will be available...

A a = ctx.getBean(A.class);

B b = ctx.getBean(B.class);

}

```

这种方式简化了容器的初始化,因为只需要处理一个类,而不是让开发者记住构造期间的大量@Configuration类。

* 导入@Bean的依赖注入

上面的示例可以工作,但太简单。在大多数实际的场景中,beans会依赖另一个跨配置类的bean。当使用XML时,这不是问题,因为不涉及到编译,其中一个bean只需要声明`ref="someBean"`,剩下的交给Spring在容器初始化期间处理即可。当然,当使用@Configuration类时,Java编译器对配置模式产生一些限制,对其他beans的引用必须是合法的java语法。

幸运的是,解决该问题是很容易的。正如我们[已经讨论](http://docs.spring.io/spring/docs/4.2.0.RC1/spring-framework-reference/htmlsingle/#beans-java-dependencies)的,@Bean可以有任意多个用来描述bean依赖的参数。让我们探讨一个更现实的场景,在这里将使用一些彼此依赖的@Configuration类:

```java

@Configuration

public class ServiceConfig {

@Bean

public TransferService transferService(AccountRepository accountRepository) {

return new TransferServiceImpl(accountRepository);

}

}

@Configuration

public class RepositoryConfig {

@Bean

public AccountRepository accountRepository(DataSource dataSource) {

return new JdbcAccountRepository(dataSource);

}

}

@Configuration

@Import({ServiceConfig.class, RepositoryConfig.class})

public class SystemTestConfig {

@Bean

public DataSource dataSource() {

// return new DataSource

}

}

public static void main(String[] args) {

ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);

// everything wires up across configuration classes...

TransferService transferService = ctx.getBean(TransferService.class);

transferService.transfer(100.00, "A123", "C456");

}

```

这里有另外的方法可以达到相同的效果。记住,@Configuration根本上只是容器中的另一个bean-这意味着它们可以像其他bean那样充分利用@Autowired注入元数据。

**注:** 确保以这种方式注入的都是简单类型的。@Configuration类在容器初始化时被处理的相当早,用这种方式强制注入依赖可能导致无法预料地过早初始化问题。只要有可能就采用上面示例中基于参数的注入方式。

```java

@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 DataSource

}

}

public static void main(String[] args) {

ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);

// everything wires up across configuration classes...

TransferService transferService = ctx.getBean(TransferService.class);

transferService.transfer(100.00, "A123", "C456");

}

```

在上面的示例中,使用@Autowired工作的很好,并且提供了想要的模块化,但要确切地指明自动注入的bean定义在哪声明依旧有点模糊。例如,一个开发者正在查看ServiceConfig,那你怎么准确地知道`@Autowired AccountRepository` bean在哪声明的?在代码中并不明确,不过有时候这样就行。记着[Spring Tool Suite](https://spring.io/tools/sts)可以提供渲染图的工具,这些图展示了Spring Bean之间是怎么连起来的-这可能是你需要的。同时,你的Java
IDE可以轻松的找到所有声明和使用AccountRepository类型的bean,并为你快速展现返回该类型的@Bean方法位置。

如果你不能接受这种模糊性,并希望在你的IDE中可以从一个@Configuration类导航到另一个,那就考虑注入配置类本身:

```java

@Configuration

public class ServiceConfig {

@Autowired

private RepositoryConfig repositoryConfig;

@Bean

public TransferService transferService() {

// navigate ‘through‘ the config class to the @Bean method!

return new TransferServiceImpl(repositoryConfig.accountRepository());

}

}

```

在上面的解决方案中,我们可以很明确地知道AccountRepository定义的地方。然而,ServiceConfig现在紧紧地跟RepositoryConfig耦合了。这就是权衡。紧耦合在某种程度上可以通过使用基于接口或抽象类的@Configuration类来减轻。考虑以下内容:

```java

@Configuration

public class ServiceConfig {

@Autowired

private RepositoryConfig repositoryConfig;

@Bean

public TransferService transferService() {

return new TransferServiceImpl(repositoryConfig.accountRepository());

}

}

@Configuration

public interface RepositoryConfig {

@Bean

AccountRepository accountRepository();

}

@Configuration

public class DefaultRepositoryConfig implements RepositoryConfig {

@Bean

public AccountRepository accountRepository() {

return new JdbcAccountRepository(...);

}

}

@Configuration

@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config!

public class SystemTestConfig {

@Bean

public DataSource dataSource() {

// return DataSource

}

}

public static void main(String[] args) {

ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);

TransferService transferService = ctx.getBean(TransferService.class);

transferService.transfer(100.00, "A123", "C456");

}

```

现在,ServiceConfig跟具体的DefaultRepositoryConfig类是松耦合的关系,并且内嵌的IDE工具依旧有用:它很容易为开发者获取RepositoryConfig实现的类型层次。采用这种方式,导航@Configuration和它们的依赖就变得跟平常处理基于接口的代码导航没区别了。

* 有条件的包含@Configuration类或@Beans

基于任意的系统状态,有条件地禁用一个完整的@Configuration类,甚至单独的@Bean方法通常是很有用的。一个常见的示例是,当一个特定的profile在Spring Environment中启用时,使用@Profile注解激活beans。

@Profile注解实际上实现了一个非常灵活的注解:[@Conditional](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html)。@Conditional注解意味着在注册@Bean之前,必须先咨询指定的`org.springframework.context.annotation.Condition`实现。

Condition接口的实现者只需简单地提供一个返回true或false的`matches(…?)`方法。例如,下面是@Profile注解采用的Condition实现:

```java

@Override

public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

if (context.getEnvironment() != null) {

// Read the @Profile annotation attributes

MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());

if (attrs != null) {

for (Object value : attrs.get("value")) {

if (context.getEnvironment().acceptsProfiles(((String[]) value))) {

return true;

}

}

return false;

}

}

return true;

}

```

具体参考[@Conditional javadocs](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html)。

* 结合Java和XML配置

Spring @Configuration类支持目的不是想要100%的替换Spring XML。一些设施,比如Spring XML命名空间仍旧是配置容器的完美方式。在XML很方便或必须的情况下,你有个选择:采用"XML为中心"的方式实例化容器,比如ClassPathXmlApplicationContext,或使用AnnotationConfigApplicationContext以"Java为中心"的方式,并使用@ImportResource注解导入需要的XML。

* 在以"XML为中心"的情况下使用@Configuration类

从XML启动Spring容器,以特设模式包含@Configuration类可能是个更可选的方式。例如,在一个已经存在的使用Spring XML的大型代码库中,遵循按需原则创建@Configuration,并从现有的XML文件中包括它们是非常容易的。下面你将找到在这样的"XML为中心"的解决方案中使用@Configuration类的可选项。

记着@Configuration类本质上只是容器中的bean定义。在下面的示例中,我们创建了一个名称为AppConfig的@Configuration类,并将它作为`<bean/>`定义包含到`system-test-config.xml`中。因为`<context:annotation-config/>`是开启的,容器将会识别@Configuration注解,并正确地处理AppConfig中声明的@Bean方法。

```java

@Configuration

public class AppConfig {

@Autowired

private DataSource dataSource;

@Bean

public AccountRepository accountRepository() {

return new JdbcAccountRepository(dataSource);

}

@Bean

public TransferService transferService() {

return new TransferService(accountRepository());

}

}

```

system-test-config.xml如下:

```xml

<beans>

<!-- enable processing of annotations such as @Autowired and @Configuration -->

<context:annotation-config/>

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

<bean class="com.acme.AppConfig"/>

<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>

```

jdbc.properties如下:

```properties

jdbc.properties

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb

jdbc.username=sa

jdbc.password=

```

main方法如下:

```java

public static void main(String[] args) {

ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");

TransferService transferService = ctx.getBean(TransferService.class);

// ...

}

```

**注:** 在上面的`system-test-config.xml`中,`AppConfig<bean/>`没有声明一个`id`元素。如果没有bean引用它,那就没有必要指定`id`元素,否则就要通过name从容器获取bean(name对应bean定义中声明的id)。DataSource也一样-它只是通过类型自动注入(autowired by type),所以并不需要显式的分配一个bean id。

由于@Configuration被@Component元注解了(被注解注解,很拗口),所以被@Configuration注解的类自动成为组件扫描(component scanning)的候选者。同样使用上面的场景,我们可以重新定义`system-test-config.xml`来充分利用组件扫描。注意在这个示例中,我们不需要明确声明`<context:annotation-config/>`,因为`<context:component-scan/>`启用了同样的功能。

system-test-config.xml如下:

```xml

<beans>

<!-- picks up and registers AppConfig as a bean definition -->

<context:component-scan base-package="com.acme"/>

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

<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>

```

* 在@Configuration"类为中心"的情况下使用@ImportResourcedaoru导入XML

在将@Configuration类作为配置容器的主要机制的应用中,仍旧存在对XML的需求。在那些场景中,可以使用@ImportResource,并定义所需的XML。这样做可以实现以"Java为中心"的方式配置容器,并保留最低限度的XML。

```java

@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如下:

```xml

<beans>

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

</beans>

```

jdbc.properties如下:

```properties

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb

jdbc.username=sa

jdbc.password=

```

main方法如下:

```java

public static void main(String[] args) {

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);

TransferService transferService = ctx.getBean(TransferService.class);

// ...

}

```

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-29 11:06:57

Spring Java-based容器配置(二)的相关文章

Spring中基于Java的容器配置(二)

使用@Configuration注解 @Configuration注解是一个类级别的注解,表明该对象是用来指定Bean的定义的.@Configuration注解的类通过@Bean注解的方法来声明Bean.通过调用注解了@Bean方法的返回的Bean可以用来构建Bean之间的相互依赖关系,可以通过前文来了解其基本概念. 注入inter-bean依赖 当@Bean方法依赖于其他的Bean的时候,可以通过在另一个方法中调用即可. @Configuration public class AppConfi

Spring Ioc 基于Java的容器配置

一.基于Java的容器配置 @Configuration & @Bean 注解: 在Spring的新的Java-Configuration的中间产物是基于类的@Configuration的注解和基于方法的@Bean注解.         @Bean注解是用来指明方法的实例化,配置和初始化一个对象是通过Spring的IoC容器来管理的.对于那些熟悉使用以XML配置Spring的<beans /> 标签,@Bean注解和<bean />标签是起相同作用的.你能和Spring的@

Spring 核心技术IoC容器(二)

本文将继续前文,描述Spring IoC中的依赖处理. 依赖 一般的企业应用也不会只有一个对象(或者是Spring Bean).甚至最简单的应用都要有一些对象来协同工作来让终端用户看到一个完整的应用.下一部分将解释开发者从单独的定义Bean,到让这些对象在一个应用中协同工作. 依赖注入 依赖注入是一个让对象只通过构造参数,工厂方法的参数或者配置的属性来定义他们的依赖.这些依赖也是对象所需要协同工作的对象.容器在之后会在创建Bean的时候注入这些依赖.整个过程完全反转了Bean自己控制实例化或者,

Spring - Java Based Configuration

So far you have seen how we configure Spring beans using XML configuration file. If you are comfortable with XML configuration, then it is really not required to learn how to proceed with Java-based configuration as you are going to achieve the same

spring 4 + jpa(hibernate 3/4) + spring mvc 多数据源配置(二)+Druid连接池

接上一个博文(http://www.loveweir.com/html/18.html),没有数据库连接池,纯粹用jpa的官方链接. 所以这次要加上连接池本文用Druid连接池来实现多数据源的配置. persistence.xml 这个文件可以省略了,全部配置在applicationContext.xml 里面: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www

java计划任务调度框架quartz结合spring实现调度的配置实例代码分享

点击链接加入群[JavaEE(SSH+IntelliJIDE+Maven)]:http://jq.qq.com/?_wv=1027&k=L2rbHv 一:quartz简介 OpenSymphony 的Quartz提供了一个比较完美的任务调度解决方案. Quartz 是个开源的作业调度框架,定时调度器,为在 Java 应用程序中进行作业调度提供了简单却强大的机制. Quartz中有两个基本概念:作业和触发器.作业是能够调度的可执行任务,触发器提供了对作业的调度 二:quartz spring配置详

跟着刚哥学习Spring框架--Spring容器(二)

Spring容器 启动Spring容器(实例化容器) -- IOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化(加载启动),这样才可以从容器中获取Bean的实例并使用.  Bean是Spring管理的基本单位,任何的Java对象和组件都被当成Bean处理,容器还负责管理Bean与Bean之间的依赖关系.  两种类型的启动实现   1.BeanFactory:IOC容器的基本实现,是Spring框架的基础设施,面向Spring本身: -- Spring容器最基本的接口就是BeanF

Spring 源码学习(二) IOC容器启动过程

这一节主要是记录一下Spring Ioc 容器的启动过程. Spring 的 Ioc 容器是怎么被加载和使用的? web容器为它提供了宿主环境 ServlectContext,  Tomcat 启动时会读取web.xml. 并且实例化web.xml中配置的ContextLoaderListener ,下面看一下ContextLoaderListener的创建过程: 在实例化ContextLoaderListener 之后,通过接口回调执行ContextLoaderListener 类中的cont

Spring讲解二:Spring中的Bean配置1---基于XML文件的方式

一.在Spring的IOC容器中配置Bean 在xml文件中通过bean节点配置bean id:Bean的名称: (1) 在IOC容器中必须是唯一的 (2) 若id没有指定,Spring自动将权限限定性类名作为bean的名字 (3) id可以指定多个名字,名字之间可以用逗号.分号.或空格分隔 二.Spring容器 在Spring IOC容器读取Bean配置创建Bean实例之前,必须对它进行初始化.只有在容器实例化后,才可以从IOC容器中获取Bean实例并使用. Spring提供了两种类型的IOC