Spring 注解编程 Bean

Spring 注解编程 Bean 配置

@Configuration

@Configuration 配置类

1
2
3
4
5
6
7

import org.springframework.context.annotation.*;

@Configuration
public class MainConfig {

}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainConfigTest {

public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

// 注册 MainConfig
ctx.register(MainConfig.class);
// 刷新容器
ctx.refresh();

// 获取容器中定义的所有 bean
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
ctx.close();
}
}

@Bean

xml 配置

1
2

<bean id="user" class="com.github.User"></bean>

java config

1
2
3
4
5

@Bean
public User user() {
return new User();
}

@Import

@Import 导入bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
public class UserConfig {

@Bean
public User user() {
return new User();
}
}


@Configuration
@Import({UserConfig.class}) // 使用 import 导入 UserConfig
public class MainConfig {

}

public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
User user = ctx.getBean(User.class);
System.out.println(user);
}

org.springframework.context.annotation.ImportSelector 选择导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public class UserImportSelector implements ImportSelector {

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[] {"com.github.User"};
}
}

@Configuration
@Import({UserImportSelector.class})
public class MainConfig {

}

public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
User user = ctx.getBean(User.class);
System.out.println(user);
}

org.springframework.context.annotation.ImportBeanDefinitionRegistrar 注册Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class UserRegistrar implements ImportBeanDefinitionRegistrar {

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(User.class);
registry.registerBeanDefinition("user", beanDefinition);
}

}

@Configuration
@Import({UserRegistrar.class})
public class MainConfig {

}

public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
User user = ctx.getBean(User.class);
System.out.println(user);
}

spring 注解编程中 @Enable** 组件,大量使用此种方式注册Bean。例:@EnableAspectJAutoProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class) // 此处导入
public @interface EnableAspectJAutoProxy {

/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}.
*/
boolean proxyTargetClass() default false;

/**
* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
* Off by default, i.e. no guarantees that {@code AopContext} access will work.
* @since 4.3.1
*/
boolean exposeProxy() default false;

}

@ComponentScan

@ComponentScan 指定扫描的包目录

1
2
3
4
5
6
7

package com.github;

@Component
public class User {

}
1
2
3
4
5
@Configuration
@ComponentScan({"com.github"})
public class MainConfig {

}

@Profile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Configuration
public class MainConfig {


@Profile("dev")
@Bean
public User devUser() {
return new User();
}
}


public class MainConfigTest {

public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();

ConfigurableEnvironment env = ctx.getEnvironment();
env.setActiveProfiles("dev"); // 设置环境为 dev

ctx.register(MainConfig.class);
ctx.refresh();

User user = ctx.getBean(User.class);
System.out.println(user);

ctx.close();
}
}

在实际开发中,经常有开发,测试,生产环境的不同配置,使用 @Profile 可以有效解决。 命令行启动设置激活的 Profile java -Dspring.profiles.actives=dev app.jar

initMethod destroyMethod Bean 生命周期

基于 @Bean 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public class User {

public void initMethod() {
System.out.println("user initMethod");
}

public void destroyMethod() {
System.out.println("user destroyMethod");
}
}
@Configuration
public class MainConfig {


@Bean(initMethod="initMethod", destroyMethod="destroyMethod")
public User devUser() {
return new User();
}
}

继承 org.springframework.beans.factory.InitializingBean 和 org.springframework.beans.factory.DisposableBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public class User implements InitializingBean, DisposableBean {

public void afterPropertiesSet() throws Exception {
System.out.println("user initMethod");
}

public destroy() throws Exception {
System.out.println("user destroyMethod");
}
}
@Configuration
public class MainConfig {


@Bean
public User devUser() {
return new User();
}
}

使用 javax.annotation.PostConstruct 和 javax.annotation.PreDestroy。 注意这两个注解是Java规范并不是spring内置的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class User {

@PostConstruct
public void initMethod() {
System.out.println("user initMethod");
}

@PreDestroy
public void destroyMethod() {
System.out.println("user destroyMethod");
}
}
@Configuration
public class MainConfig {


@Bean
public User devUser() {
return new User();
}
}

@Scope

scope 取值:通常使用 singleton, prototype。 web环境有 request, session。 batch 环境 step

1
2
3
4
5
@Bean(initMethod="initMethod", destroyMethod="destroyMethod")
@Scope("prototype")
public User devUser() {
return new User();
}

默认是singleton的,这里比较一下prototype有什么不同:

  • 每次在容器中获取的 User 都会调用 devUser() 方法获取 User 实例。
  • 关闭容器不会调用 destroyMethod() 方法。

org.springframework.beans.factory.config.BeanPostProcessor Bean 实例化前置处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public interface BeanPostProcessor {

/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding {@code bean instanceof FactoryBean} checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

实现此接口会在spring容器初始化bean时调用