欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Java-based容器配置详解

程序员文章站 2024-02-24 17:30:16
装java-based的配置 使用 @import 跟在spring xml文件中使用元素添加模块化的配置类似,@import注解...

装java-based的配置

使用 @import

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

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

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语法。

幸运的是,解决该问题是很容易的。正如我们已经讨论的,@bean可以有任意多个用来描述bean依赖的参数。让我们探讨一个更现实的场景,在这里将使用一些彼此依赖的@configuration类:

@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类在容器初始化时被处理的相当早,用这种方式强制注入依赖可能导致无法预料地过早初始化问题。只要有可能就采用上面示例中基于参数的注入方式。

@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可以提供渲染图的工具,这些图展示了spring bean之间是怎么连起来的-这可能是你需要的。同时,你的java ide可以轻松的找到所有声明和使用accountrepository类型的bean,并为你快速展现返回该类型的@bean方法位置。

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

@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类来减轻。考虑以下内容:

@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。@conditional注解意味着在注册@bean之前,必须先咨询指定的org.springframework.context.annotation.condition实现。

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

@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

结合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方法。

@configuration
public class appconfig {
	@autowired
	  private datasource datasource;
	@bean
	  public accountrepository accountrepository() {
		return new jdbcaccountrepository(datasource);
	}
	@bean
	  public transferservice transferservice() {
		return new transferservice(accountrepository());
	}
}

ystem-test-config.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如下:

jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=

main方法如下:

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

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

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

jdbc.properties如下:

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=

main方法如下:

public static void main(string[] args) {
  applicationcontext ctx = new annotationconfigapplicationcontext(appconfig.class);
  transferservice transferservice = ctx.getbean(transferservice.class);
  // ...
}

总结

以上就是本文关于spring java-based容器配置详解的全部内容,感兴趣的朋友可以继续参阅:java之spring注解配置bean实例代码解析浅谈java注解和动态代理等,希望对大家有所帮助。有不足之处,欢迎留言指正。感谢朋友们对网站的支持!