加粗样式@TOC
@Bean和@Configuration
Spring的基于java的配置的核心是@Configuration注解的类和@Bean注解的方法。
@Bean注解用来指明一个方法实例化、配置和初始化Spring IoC容器管理的一个新对象。@Bean的作用和配置文件中的<beans/>。
用@Configuration注解的类表明它是用作bean定义的源。另外,@Configuration注解类允许内部的bean依赖被定义,仅仅通过调用相同类中的其他@Bean方法。最简单的@Configuration的像下面这样:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
AnnotationConfigApplicationContext
使用AnnotationConfigApplicationContext初始化Spring容器
ApplicationContext实现不仅能接收@Configuration类作为输入,而且@Component注解类和JSR-330 metadata注解的类。当@Configuration类作为输入时,@Configuration注解的类本身被作为一个bean定义,在其中@Bean注解的方法也作为bean定义。
和使用ClassPathXmlApplicationContext通过配置文件创建bean一样,可以使用AnnotationConfigApplicationContext通过@Configuration类作为bean的输入。这允许spring容器完全摆脱xml配置文件。
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
AnnotationConfigApplicationContext 不仅限于@Configuration注解的类。任何@Component和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();
}
使用register(Class<?>…)构建容器
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();
}
启用扫描组件
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
AnnotationConfigWebApplicationContext支持web应用程序
AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext用于支持web的变体。当配置Spring ContextLoaderListener监听器、Spring MVC DispatcherServlet时使用这个实现。
<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>
Using the @Bean annotation
声明bean
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
等同于之前的
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
接收生命周期回调
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();
}
}
等同于
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
Foo foo = new Foo();
foo.init();
return foo;
}
// ...
}
指定作用域
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
Using the @Configuration annotation
注入bean间的依赖
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
组合基于java的配置
使用@Import注解
像在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();
}
}
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);
}
导入的@Bean的依赖间注入
@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");
}
有条件地包含@Configuration classes or @Beans
通常根据系统的某个状态有条件地启用或禁用一个完整的@Configuration类或者单个的@Bean方法是有用的。一个常用的例子是使用@Profile注解,在Spring环境中只有特定的模式启用是才激活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;
}
给容器中注册组件的方式
- 包扫描+组件标注注解(@Controller、@Service、@Repository、@Component)
- @Bean[导入第三方包里面的组件]
- @Import[快速给容器中导入一个组件]
1). @Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认为全类名;
2). ImportSelector:返回需要导入的组件的全类名数组;
3). ImportBeanDefinitionRegistrar:手动注册bean容器中 - 使用Spring提供给FactoryBean(工厂Bean)