http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-java-instantiating-container-web ##7.12 Java-based container configuration (基于java的容器配置) ###7.12.1 Basic concepts:@Bean and @Configuration spring对于新的java配置的支持的核心武器是@Configuration-annotated类以及@bean-annotated方法.html
@Bean注解是来标记一个能够实例化,配置,初始化一个新的有spring-ioc容器管理的新的对象.这里有些像spring的xml配置中的<beans/>标签,@Bean则至关于里面的<bean>标签.你能够讲@Bean标记到任何@Components类的方法上,可是,它们通常只在@Configuration的beans里使用.java
用@Configuration代表他的主要目标是做为bean定义的资源.另外,@Configuration类容许经过调用同一个类里面的@Bean标注的方法定义的内部bean依赖.最简单的@Configuration类用法以下:web
@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
上面的AppConfig类等价于下面的<beans/> xml:spring
<beans> <bean id="myService" class="com.acme.services.MyServiceImpl"/> </beans>
####Full @Configuration vs 'lite' @Beans mode? 当 @Bean方法出如今没有@Configuration注解标志的类中时,它们其实是在'lite'模式下处理的.例如,在@Component类里或一个POJO类里定义的bean方法都认为是'lite';sql
不一样于full @Confuguration,lite @Bean 方法不易申明内部bean依赖.通常在'lite'模式下,一个@Bean方法不能调用其余的@Bean方法.数组
只有使用在@Configuration类里的@Bean方法才是保证'full'模式常常被使用的好方法.它能够阻止相同的@Bean方法意外的被屡次调用,并有助于减小在'lite'模式下很难被追踪的微妙的bugs.缓存
@Bean和@Configuration注解会在之后的部分深刻讨论.可是,首先让咱们来了解使用java-based configuration来建立spring容器的几种方式吧. ###7.12.2 Instantiating the Spring container using AnnotationConfigApplicationContext(用AnnotationConfigApplicationContext来实例化spring上下文容器) 本节中的AnnotationConfigApplicationContext,是spring3.0的新加内容.这个ApplicationContext的变种实现,不只可以接受@Configuration的类,还能够接受日常的@Component类和JSR-330元数据标注的类.app
当@Configuration类做为输入类时,@Configuration类自己会注册为一个bean,且全部声明的@Bean方法也会注册为一个bean定义.async
当@component和JSR-330类被提供时,他们会被注册为bean定义.而且全部的依赖注入元数据例如@Autowired或@Inject将会被使用在这些类须要的地方.ide
###简单的构造器 同实例化一个ClassPathXmlApplicationContext时使用spring xml文件同样,实例化一个AnnotationConfigApplicationContext时也会使用@Configuration类.它容许的spring容器彻底不使用xml.
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
和上文提到的同样,AnnotationConfigApplicationContext不止限于@Configuration类.任何@Componnet和用JSR-330标记的类均可以做为入参提供到构造器里,以下:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
以上认为MyServiceImpl, Dependency1 and Dependency2等类使用了spring依赖注入的注解如@Autowired.
####Building the container programmatically using register(Class<?>...) 使用注册器来动态建立容器 一个AnnotationConfigApplicationContext能够被一个无参的构造器实例化并用register()方法进行注册.这个方式在动态构建一个AnnotationConfigApplicationContext是特别有用.
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(AppConfig.class, OtherConfig.class); ctx.register(AdditionalConfig.class); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
####Enabling component scanning with scan(String ..) 可用组件扫描 要保证组件扫描,只以下标志你的@Configuration类便可:
@Configuration @ComponentScan(basePackages = "com.acme") public class AppConfig { ... }
有经验的spring用户能够发现其用法和xml使用context命名空间类似:
<beans> <context:component-scan base-package="com.acme"/> </beans>
在上面的例子中,com.acme包将被扫描,查找任意被@Component标志的类,这些类将注册为spring容器里的bean定义.
AnnotationConfigApplicationContext 暴露了scan(String..)方法,使其有component-scanning的功能.
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan("com.acme"); ctx.refresh();//进行注册 MyService myService = ctx.getBean(MyService.class); }
记住@Configuratin类的元注解也是@Component,因此它们是组件扫描的候选者.在上面的例子中,默认AppConfig是在com.acme包里的,在调用scan()方法它会被扫描,在refrsh()方法以后全部的@Bean方法会被容器处理并注册为一个bean定义. ####Support for web applications with AnnotationConfigWebApplicationContext(对web应用的支持)
AnnotationConfigApplicationContext的WebApplicationContext的版本能够经过AnnotationConfigWebApplicationContext实现.当你配置spring的ContextLoaderListener servlet Listener,spring MVC DispatcherServlet时会很是有用.下面是一个典型的spring MVC的web应用配置的web.xml.记住context-param和init-param参数的使用.
<web-app> <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext --> <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </context-param> <!-- Configuration locations must consist of one or more comma- or space-delimited fully-qualified @Configuration classes. Fully-qualified packages may also be specified for component-scanning --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.acme.AppConfig</param-value> </context-param> <!-- Bootstrap the root application context as usual using ContextLoaderListener --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Declare a Spring MVC DispatcherServlet as usual --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext instead of the default XmlWebApplicationContext --> <init-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </init-param> <!-- Again, config locations must consist of one or more comma- or space-delimited and fully-qualified @Configuration classes --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>com.acme.web.MvcConfig</param-value> </init-param> </servlet> <!-- map all requests for /app/* to the dispatcher servlet --> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping> </web-app>
###7.12.3 使用@Bean注解 @Bean是一个方法级别的注解,且是xml中<bean/>元素的替代物.这个注解支持<bean/>里的一些属性,例如:init-method,destroy-method,autowiring ,name.
你能够在用@Configuration或@Component标志的类里使用@Bean注解. ####Declaring a bean 声明一个bean 要申明一个bean,能够简单的用@Bean来标注一个方法.你能够在ApplicationContext中用这个方法的特定返回值类型来注册一个bean定义.默认,bean的名称同方法名相同.下面是使用@Bean方法申明的简单例子;
@Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } }
上面的配置等价于下面的spring xmlns
<beans> <bean id="transferService" class="com.acme.TransferServiceImpl"/> </beans>
这两个申明均可以在ApplicationContext中注册transferService,限制这个实例类型为TransferServiceImpl:
transferService->com.acme.TransferServiceImpl
####Bean dependencies (Bean的依赖) 一个@bean标记的方法能够有任意数量的参数来描述构建这个bean所必需的依赖.例如若是咱们的TransferService须要一个AccountRepository,咱们能够经过方法参数来实现这个依赖.
@Configuration public class AppConfig {
@Bean public TransferService transferService(AccountRepository accountRepository) { return new TransferServiceImpl(accountRepository); }
}
这个解决机制是基于构造器依赖注入的替代物,查看相关片断已查看更多细节.
任何被@Bean注解标记的类都支持常规的生命周期回调,并可使用@PostConstruct和@PreDestroy注解.
常规的spring生命周期回调一样支持.若是一个bean实现了InitializingBean,DisposableBean或者Lifecycle,他们的表现方法会被容器调用.
也彻底支持*Aware接口的标准的设置,如BeanFactoryAware,BeanNameAware,MessageSourceAware,ApplicationContextAware等.
这个@Bean注解支持指定的任意初始化和销毁回调方法,同spring xml元素里的init-mehod,destroy-method属性一致.
public class Foo { public void init() { // initialization logic } } public class Bar { public void cleanup() { // destruction logic } } @Configuration public class AppConfig { @Bean(initMethod = "init") public Foo foo() { return new Foo(); } @Bean(destroyMethod = "cleanup") public Bar bar() { return new Bar(); } }
通常而言,使用java配置定义的beans都有一个公共的能够被销毁回调调用的close或shutdown方法.若是你有一个公共的close或shutdown方法且你不但愿在容器关闭时它们被调用,简单的在你的bean定义上加上@Bean(destroyMethos="")来屏蔽这个默认模式.你可能想这么作,经过默认的JNDI来获取资源但它的生命周期管理须要在应用以外.特别,DataSource须要这么处理,由于在Java EE应用服务中它是个众所周知的问题.
@Bean(destroyMethod="") public DataSource dataSource() throws NamingException { return (DataSource) jndiTemplate.lookup("MyDS"); }
另外,用@Bean标注的方法,你通常会选择使用动态JNDI查找策略:spring的JndiTemplate或JndiLocateDelegate helpers或者直接的JNDI InitialContext用法,而不是JndiObjectFactoryBean的变种.JndiObjectFactoryBean会强迫你将返回类型定义为FactoryBean而不是实际的目标类型,结果在其余@Bean方法中须要指定该bean提供资源时会很难交叉引用调用.
固然,在上个例子的Foo中,等同于下面在构造器中调用init()方法.
@Configuration public class AppConfig { @Bean public Foo foo() { Foo foo = new Foo(); foo.init(); return foo; } // ... }
当你直接使用java时,你能够直接同你的对象来作任何事,且不须要依赖容器的生命周期.
###Specifying bean scope 指定bean的做用域 ####Using the @Scope annotation使用@Scope注解 你能够用指定你的用@Bean标记的bean定义有一个特定的做用域.你可使用在Bean Scope节里的任意标准的做用域.
默认做用域是单例(singleton),但你能够用@Scope注解重写该做用域
@Configuration public class MyConfiguration { @Bean @Scope("prototype") public Encryptor encryptor() { // ... } }
###@Scope and scoped-proxy 做用域和做用域代理 spring经过scopes proxies提供一个合适的方式来同做用域依赖工做.最简单建立代理这种代理的方式是使用XML配置时的aop:scoped-proxy/元素.用@Scope配置你的beans用proxyMode属性提供了相同的支持.默认是没有代理的(ScopedProxyMode.NO),你能够指定为ScopedProxyMode.TRAGET_CLASS或ScopedPrxoyMode.INTERFACES. 若是你要将scoped proxy从xml引用文档转为使用java配置的Bean,你能够以下使用:
@Bean @SessionScope public UserPreferences userPreferences() { return new UserPreferences(); } @Bean public Service userService() { UserService service = new SimpleUserService(); // a reference to the proxied userPreferences bean service.setUserPreferences(userPreferences()); return service; }
####自定义bean命名 通常,配置类使用@bean方法的名字做为该bean的名字.不过,这个功能可使用name属性来重写.
@Configuration public class AppConfig { @Bean(name = "myFoo") public Foo foo() { return new Foo(); } }
###Bean aliasing (Bean的别名) 同7.3.1'Naming beans'讨论的那样,有时要给单个的bean多个名字,这就是Bean aliasing.@Bean注解的name属性接受一个String数组,这样能够了.
@Configuration public class AppConfig { @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" }) public DataSource dataSource() { // instantiate, configure and return DataSource bean... } }
####Bean description bean描述 有时给bean提供详细的文本描述很是有帮助.特别是当这些bean暴露(经过JMX)出来以用于监控时. 你能够这样给一个Bean添加描述
@Configuration public class AppConfig { @Bean @Description("Provides a basic example of a bean") public Foo foo() { return new Foo(); } }
###7.12.4 Using the @Configuration annotation(使用@Configuration 注解) @Configuration是一个类级别的注解来标注该对象是一个bean定义的资源.@Configuration类经过@Bean注解方法来声明beans定义.调用@Configuration类的@Bean方法也能够用于定义内部bean依赖.
####Injecting inner-bean dependencies(注入内置的依赖) 当一个Bean有其余依赖时,你能够这样表示一个依赖有一个bean方法来调用其余bean:
@Configuration public class AppConfig { @Bean public Foo foo() { return new Foo(bar()); } @Bean public Bar bar() { return new Bar(); } }
在上面的例子中,foo的bean经过构造器注入来接受bar的引用;;
注:只有在@Configuration类使用@Bean注解申明的方法时,这种申明内部bean依赖的方法才有效.你不能在日常的@Component类里使用内部bean依赖.
####Lookup method injection(查找方法注入) 和之前提过的同样,查找方法注入是一个咱们不多使用的高级功能.当一个单例bean有一个原生做用域依赖时,这点会颇有用.为这种配置类型使用java配置,它为实现这个模式提供了一个合适的方式;
public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand(); }
使用java配置支持,你能够建立一个CommandManager的子类并使用这种方式来重写它的createCommand()方法,这样它就能查找一个新的command对象.
@Bean @Scope("prototype") public AsyncCommand asyncCommand() { AsyncCommand command = new AsyncCommand(); // inject dependencies here as required return command; } @Bean public CommandManager commandManager() { // return new anonymous implementation of CommandManager with command() overridden // to return a new prototype Command object return new CommandManager() { protected Command createCommand() { return asyncCommand(); } } }
貌似也要在@Configuration类中.一个单例bean有一个原生bean的引用. ####Further information about how java-based configuration works internally 基于java配置协同工做的更多信息 下面的例子,一个@Bean方法被调用两次:
@Configuration public class AppConfig { @Bean public ClientService clientService1() { ClientServiceImpl clientService = new ClientServiceImpl(); clientService.setClientDao(clientDao()); return clientService; } @Bean public ClientService clientService2() { ClientServiceImpl clientService = new ClientServiceImpl(); clientService.setClientDao(clientDao()); return clientService; } @Bean public ClientDao clientDao() { return new ClientDaoImpl(); } }
clientDao()方法在clientService1()和clientService2()方法里分别调用一次.既然这个方法串讲一个新的ClientDaoImpl的实例并返回它,那么你应该期待两个实例(一个service一个).这个定义会出现问题:在spring里,实例bean默认是单例做用域.这个关键点在于,全部的@Configuration类在启动时都是用CGLIB代理的子类启动的.在子类中,子方法在父方法调用和建立新实例以前首先查找容器中缓存的bean.记住在spring3.2中,你不须要额外引用CGLIB的jar包了,由于它已经在org.springframework.cglib的包下了,并收到了spring核心jar包里了.
这个行为会根据做用域的不一样而差别.咱们只讨论单例状况.
由于CGLIB动态代理在启动时须要添加功能因此这里有些限制,特别是配置类必须不是final.可是,4.3以后,配置类中容许全部的构造器,包括使用了@Autowired的,或无参构造器来申明默认注入.
若是你更倾向于避免CGLIB强加的限制,能够考虑在非@Configuration类里申明@Bean方法,例如,使用@Component类替代.@Bean方法之间的交叉调用不会被拦截,全部你必须依赖构造器或方法级别的依赖注入.
###7.12.5 Composing java-based configurations 集成java配置 ####Using the @Import annotaion 就像<import/>元素使用在spring的xml文件里的模块化配置中使用的同样,这个@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(); } }
如今,当你实例化上下文时不须要指定ConfigA.class或ConfigB.class,你只需明确的提供ConfigB的类.
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类.
在spring 4.2中,@Import还支持常规组件类,与AnnotationConfigContext.register()方法类似.当你要避免组件扫描时,使用少许的配置类做为定义大家全部组件的关键点. ####Injecting dependencies on imported @Bean definitions 经过@Import来引入bean定义 上面的例子虽然很简洁,但仍能够其做用.在大多数实际场景里,bean会用其余配置文件里的依赖.当使用xml时,这不是问题.由于这里没有编译器,且能够简单的宣布ref="someBean",并相信spring会在容器初始化时解决好.固然,当使用@Configuration类时,在这种配置模式下java编译器会提出一些限制,就是这些引用的bean必须通过java语法的检验;
可是,解决这个问题很简单,就想咱们以前讨论的,@Bean方法能够有任意数量的参数来描述bean的依赖.让咱们讨论一个使用@Configuration类的真实场景,每一个bean都有其余类中的依赖:
@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和@Value进行注入.
保证你用最简单的方式来注入依赖.@Configuration类会在容器初始化时就会处理,并强制要注入的依赖超出预期的过早初始化.若有可能,重组上个例子中的基于参数的注入.
另外,经过@Bean来处理BeanPostProcessor和BeanFactoryPostProcessor定义时要特别当心.他们通常要申明为静态Bean方法,无需触发到包含它们的配置类的实例化.另外,@Autowired和@Value不会对配置类自身其做用,由于它做为一个bean实例建立太早了.
@Configuration public class ServiceConfig { @Autowired private AccountRepository accountRepository; @Bean public TransferService transferService() { return new TransferServiceImpl(accountRepository); } } @Configuration public class RepositoryConfig { private final DataSource dataSource; @Autowired public RepositoryConfig(DataSource dataSource) { this.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"); }
spring4.3支持@COnfiguration类里的构造器注入.记住若是目标bean只有一个构造器,那就不须要指定@Autowired的值.上面的例子,Repository的构造器无需添加@Autowired注解.
在上面的例子中,使用@Autowired做用得当,并提供了理想的模块,但在表现注入的bean定义须要申明这方面仍有些模糊.例如,一个开发者查找ServiceConfig,你怎么能准确的知道@Autowired AccounRepository注入的bean是哪个呢.它可能在代码中不明确,但倒是最好的那个.Spring Tool Suite提供了一个工具,它能够画出全部须要注入bean的层次图表,多是全部你须要的bean.固然,你的其余java IDE也能够轻易的发现全部的AccountRepository类的申明和使用,并快速的代表全部返回该类型的@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类或单个的@bean方法有效或无效颇有用.一个日常的例子是当spring环境中只有一个特定的profile可用时,使用@Profile注解来启动beans.
@Profile注解其实是使用更灵活的@Conditional注解实现的.这个@Conditional注解是特定的org.springframework.context.annotation.Condition的实现,代表在@Bean注册以前应该先询问它.
实现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的javadoc来获取更多内容.
####Combining java and Xml configuration 基础java和XML配置
spring的@Configuration类的支持并不打算100%的替代spring的xml配置.一些基础设施如spring的xml命名空间还是解决容器配置的理想方式.当xml适使或必须时,你能够如此:能够用XML-centric的方式实例化你的容器,例如,ClassPathXmlApplicationContext,或者用"java-centric"方式使用AnnotationConfigApplicationContext或@ImportResource注解来引入须要的XML.
####XML-centric use of @Configuration classes 以xml为中心使用@Configuration
当你倾向于从xml里使用spring容器并以ad-hoc的方式引入@Configuration类.例如,若是已存在大量的使用spring XML的代码,那么在需求上建立@Configuration类和在xml里引入它们会更加容易.下面你会找到在"XML-centric"情景下使用@Configuration类的技巧;
记住@Configuration里的类最后都是容器里的bean定义.在这个例子,咱们新建了一个名为AppConfig的@Configuration类,并在system-test-config.xml里做为<bean/>定义引入它.由于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()); } }
system-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.username=sa jdbc.password=
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引用时不须要命名,并且容器也不大可能经过命名来获取它.对比DataSource的bean,它只能经过类型进行注入,因此一个明确的bean的id属性不是必须的.
针对以上场景,你能够设置路径扫描;component-scan包含annotation-scan的功能,因此不须要annotation-scan,经过设置component-scan的base-package属性来定义;如今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 class-centric use of XML with @ImportResource 经过使用@ImportResource来引入xml文件,这个是java-centric的作法,该注解需指明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=
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); TransferService transferService = ctx.getBean(TransferService.class); // ... }
而后咱们能够像在xml里同样使用property-placeholder里的属性了.