Spring framework 系列之依赖注入,将对象之间的依赖关系减弱,进而其实例化也交给 Spring,Spring 维护一个上下文,并以声明式的方式对依赖进行满足(注入),进一步的强化面向接口编程的思想。

测试代码搭建

面向接口编程,接口的实现就可以通过依赖注入进来,这种模式又叫做依赖注入或者控制反转:

  • IOC (inversion of control) 控制反转
  • DI (dependency injection) 依赖注入

为了阐述 Spring 的依赖注入,先规划下这样的场景,在 Service 里为了减小耦合,使用 Dao 的接口变量来操作数据库,这时候这个 Service 是 has Dao 的结构。

新建一个工程名为 demo 的 maven 空项目,以在 Web 开发中常用的 Dao->Service 的关系来做讲解,部分示例代码如下:

实体类(POJO)(getter/setter 最好使用 IDEA 自动生成):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// path: src/main/java/demo/entity/User.java
public class User {
    private String username;
    private String password;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

接口和接口的实现如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// path: src/main/java/demo/dao/UserDao.java
public interface UserDao {
    public void save(User user);
}

// path: src/main/java/demo/dao/impl/UserDaoImpl.java
public class UserDaoImpl implements UserDao {
    public void save(User user) {
        System.out.println("user saved!");
    }
}

数据库访问层:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// path: src/main/java/demo/service/UserService.java
public class UserService {
    private UserDao userDao;

    public UserService(UserDao userDao) {
        super();
        this.userDao = userDao;
    }
    public UserService() {}

    public void add(User user) {
        userDao.save(user);
    }

    public UserDao getUserDao() {
        return userDao;
    }
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}

spring 的依赖利用 maven 下面的两种方式,推荐使用新方式,老方式在 jdk 1.8 时候会有 …… are only available on JDK 1.5 and higher 的错误,参见,而新的方式是把 spring 拆解成为 Spring core、Spring beans、Spring contexts 等。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- 这是较为新的方式,spring 的版本应该是 5 左右 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>RELEASE</version>
</dependency>

<!-- 老方式,这个最后停留在 2.5.6,Nov, 2008 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring</artifactId>
    <version>RELEASE</version>
</dependency>

常规的方式是在 service 代码里初始化时候 new 一个接口实现,然后传递给接口变量。这样代码入侵程度较高。spring 的 IOC/DI 提供了较好的解决方案,以上面代码为例下面进行讲解。

配置方式

创建 src/main/resources/beans.xml 文件,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="u" class="demo.dao.impl.UserDaoImpl" />

    <bean id="userService" class="demo.service.UserService">
        <property name="userDao" ref="u" />
    </bean>
</beans>

bean 的可以理解为最终会由 spring 根据指定的 class 参数 new 出来的对象,而子节点 property 对应着该对象中由 name 指定名称的某个属性,ref 参数指定了其依赖(即 UserDaoImpl)。

spring 会在创建 userService 这个 bean 时候将根据反射调用 userDao 属性的 set 方法(需要注入的属性 get/set 方法),将提前创建完成的 UserDaoImpl 的 bean 赋值给其属性。这了就实现了 bean 的装配、依赖注入。

最后在测试代码里可以编写下面的测试用例用于后续的测试。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// src/test/java/demo/service/UserServiceTest.java
public class UserServiceTest {
    @Test
    public void testAdd() throws Exception {
        // 加载配置文件,实现 beans.xml 文件里 bean 的构造和装配
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        // 根据 id/name 获取 bean,也可根据 class 获取
        UserService service = (UserService)ctx.getBean("userService");
        User user = new User();
        user.setUsername("zhangsan");
        user.setPassword("zhangsan");
        service.add(user);
    }
}

测试用例使用 junit,所以需要添加 maven 依赖:

1
2
3
4
5
6
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

spring 有三种注入方法:

  1. setter 注入,也就是上面的使用,使用自动生成的 get/set 即可,这种方式是最主要的

  2. 构造方法注入,需要提前写好构造方法,bean 会将以参数的形式传递传入,所以需要自己完成(ide也可以自动生成属性在构造里的业务)属性赋值的业务。配置需要更改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    
    <bean id="userService" class="demo.service.UserService">
    <constructor-arg>
        <!-- 参考已经初始化的 bean -->
        <ref bean="u"/>
        <!-- 重新初始化一个 bean -->
        <!-- <bean class="demo.dao.impl.UserDaoImpl" /> -->
    </constructor-arg>
    </bean>

    除了 bean 的注入也可以直接使用简单属性值注入,构造函数参数不只一个的时候,可以使用 index 属性指定参数顺序,或者根据 type

    1
    2
    
    <constructor-arg index="0" value="12" />
    <constructor-arg index="1" value="34" />

    spring 3.0 之后可以使用命名空间 c 的方式将构造参数简化,但是这种方式不支持集合。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    <!-- 需要加入命名空间 -->
    ...
    xmlns:c="http://www.springframework.org/schema/c"
    ...
    
    <!-- 为构造方法中名为 ver 的参数注入名为 BeanName 的 bean -->
    <bean id="u" c:ver-ref="BeanName" class="demo.dao.impl.UserDaoImpl" >
    <!-- 为构造方法中的第 0(第一个)个参数注入名为 BeanName 的 bean -->
    <bean id="u" c:ver:_0-ref="BeanName" class="demo.dao.impl.UserDaoImpl" >
    <!-- 为构造方法中名为 ver 的参数注入值 123 -->
    <bean id="u" c:ver="123" class="demo.dao.impl.UserDaoImpl" >

    同样的可以使用命名空间 p 来替代简单的 property 标签,语法和 c 命名空间类似。

  3. 接口注入,这个使用不多,就不再浪费篇幅。

注入类型除了上面提到的 bean 和简单的值类型,还可以是集合类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<bean name="xxx" class="xxx">
      <property name="sets">
          <set>
              <value>1</value>
              <value>2</value>
          </set>
      </property>
      <property name="lists">
          <list>
              <value>1</value>
              <value>2</value>

          </list>
      </property>
      <property name="maps">
          <map>
              <entry key="1" value="1"></entry>
              <entry key="2" value="2"></entry>
          </map>
      </property>
</bean>

bean 生存范围和生命周期

实例化的 bean 在装配时候其生生存范围是非常重要的,实际应用中会使用到的有两种:

  • singleton,单例,即只会创建一个,默认值
  • prototype,原型,每次有 ref 时候会创建一个 bean。

在配置里可以使用 scope 属性来指定,如 <bean id="u" class="demo.dao.impl.UserDaoImpl" scope="prototype" />

spring 默认情况下 bean 会全部初始化生成,可以在 bean 标签上加上 lazy-init="true" 来实现依赖时候的懒加载。

由于 bean 的声明周期由 spring 来管理,所以可以声明式的将指定其初始化和销毁时候调用的方法,如:

1
 <bean id="userService" class="demo.service.UserService" init-method="init" destroy-method="destroy"/>

需要注意的是这只适用于 scope 为 prototype 的情况下,在实验时候需要显式的调用 ctx.destroy(); 才能触发销毁。

autowire 自动装配

userService 的 bean 需要注入的 bean 除了通过 property 子标签显式指定,还可以通过 autowire 属性如 <bean id="userService" class="demo.service.UserService" autowire="byType"/> 进行自动装配,自动装配方式如下几种常用的:

  • no: 不匹配,但是 bean 必须定义ref元素
  • byName: 根据名称,通过 bean 的 name 值来匹配需要装配 bean 下同名的属性名的值
  • byType: 根据类型,根据 bean 的类型匹配需要装配 bean 下同名同类型的属性,在有多个该类型的 bean 时候,会冲突
  • constructor: 构造函数

也可以写在 beans 里如 <beans autowire="byName">

注解方式

使用注解之前需要修改下 beans.xml配置文件,添加上 context 的提示,并引入 <context:annotation-config /> 这个标签会初始化一些用于处理注解的 bean。

The implicitly registered post-processors include AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor, and the aforementioned RequiredAnnotationBeanPostProcessor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config />
    <bean id="u" class="demo.dao.impl.UserDaoImpl" />
    <bean name="userService" class="demo.service.UserService"/>
</beans>

Autowired

上面的配置就舍弃了在配置里组装 bean,只是声明了 bean,那么在 java 代码里就需要使用注解来完成装配的功能。在 UserService.java 中:

1
2
3
4
5
6
7
// 推荐的是在 setter 方法上使用注解
@Autowired
public void setUserDao(UserDao userDao)

// 或者另外一种方式在属性上使用注解
@Autowired
private UserDao userDao;

@Autowired 注解是根据参数类型而非名称来进行装配的,所以可以修饰多个参数的 set 方法,或者构造函数,所以这里的方法名就没有配置方式的那么严谨了。如果出现多个同类型的 bean 如果其中一个指定了和参数类似名一致的 name ,spring 会自动装配该 bean,如果没有指定 name 那么可以使用 @Qualifier 注解指定其需要注入的 bean 名称,使用注解之后其匹配权重最高:类型 < 名称 < Qualifier注解:

1
2
3
// 假如有 name 为 u1 u2 的两个 bean
@Autowired
public void setUserDao(@Qualifier("u2") UserDao userDao)

默认的 @Autowired 是 required = true 的,可以使用 @Autowired(required=false) ,这样就不一定需要需要注入。为了规范性,就有 @Required 这个单独的注解,声明式的要求注入。

Resource

Resource 注解是 JSR-250 规范里的,spring 里实现了该注解,该注解基于所修饰的属性、set方法的 名称 来装配,也可以利用 name 参数显式指定名称:

1
2
3
4
5
// 由于是规范,这里导入的包是 javax
import javax.annotation.Resource;
...
@Resource(name="userDao1")
public void setUserDao(UserDao userDao)

在 JSR-300 规范里还可以使用 @Inject@Named,由于 Inject 没有 required 属性,所以需要使用 @Nullable 来指定其可不用注入。

1
2
3
4
5
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder)

@Inject
public void setMovieFinder(@Nullable MovieFinder movieFinder)

@javax.inject.Named@javax.annotation.ManagedBean 还可以用于声明 bean,类似于下面将要提到的 @Component

Component

除了装配关系可以使用注解完成,bean 的声明也是可以使用注解来完成的,需要在 beans.xml 里添加上扫描的包的路径,以实现 bean 的发现。

1
2
3
4
5
6
7
<context:annotation-config />
<!-- 递归扫描该包 -->
<context:component-scan base-package="demo"/>

<!-- bean 声明也不需要了 -->
<!-- <bean id="u" class="demo.dao.impl.UserDaoImpl" />
<bean name="userService" class="demo.service.UserService"/> -->

实际上使用 <context:component-scan> 时候是默认能使了 <context:annotation-config /> 了的。

The use of <context:component-scan> implicitly enables the functionality of <context:annotation-config>. There is usually no need to include the <context:annotation-config> element when using <context:component-scan>.

之后为需要用 spring 来接管的 bean 添加上注解

1
2
3
4
5
6
7
// 如果不指定名字,以类名的为名(首字母小写)
@Component
public class UserService {}

// 这里就必须指定下名字了
@Component("userDao")
public class UserDaoImpl implements UserDao {}

除了 @Component 实际上还可以使用 ,@Repository @Service @Controller 来注解,3 个注释分别和持久层(Dao)、业务层和控制层(Web 层)相对应,实质上是没有区别的只是语义显得明确些(唯一的区别是这三个的 scope 默认 singleton),这一点这会在 Spring MVC 里体现的淋漓尽致。

注解细则

对应配置方式,注解方式还有下面的些细节:

  • @Scope("xxx") 注解为该 bean 指定生存范围
  • @PostConstruct 对应 init-method
  • @PreDestroy 对应 destroy-method
  • @Lazy 对应配置式里的懒加载

JavaConfig

在第三方库不可修改的情况下,要想将里面的资源装配到自己的应用上,使用注解的自动装配的入侵代码的方式是不容易实现的,这时候需要使用显式的装配。显式装配有两种可选方案:JavaConfig 和 XML ,XML 配置式前面已经提到,下面介绍 JavaConfig 的方式。

JavaConfig 的方式就是需要自己实现 第三方到自己的转换 的过程,JavaConfig 又称 配置类,实现步骤分为以下几部:

  1. 创建一个配置类,使用 @Configuration 来注解
  2. 在该类中,创建多个返回第三方对象的方法,并用 @Bean 注解,最后如不使用注解里的 name 属性显式指定名称,返回的对象会以 ID 名为 方法名 为标识放入spring 的容器池/上下文(context)中,以供装配。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// demo.dao.config.UserDaoConfig
@Configuration
public class UserDaoConfig {

    // 或者指定名称 @Bean(name="userDao")
    @Bean
    public UserDao userDao() {
        return new UserDaoImpl();
    }
}

之后去掉 UserDaoImpl 的 @Component 的注解,在测试用例中发现也是能够完成装配的。

配置类的测试

编写测试类的时候可以不使用 beans.xml 文件而使用 JavaConfig 的方式初始化 Spring context,如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@org.junit.jupiter.api.Test
void javaConfig() {
    // 这里获取配置类和需要注入的类,构成上下文,完成自动注入
   ApplicationContext ctx = new AnnotationConfigApplicationContext(UserDaoConfig.class, UserService.class);
   // 以 class 的方式获取 bean 不用强制转换
   UserService userService = ctx.getBean(UserService.class);
   User user = new User();
   user.setUsername("Jack");
   userService.addUser(user);
   }

要是嫌麻烦的还可以以下面的方式完成 spring 上下文的构建,在测试类里面手动扫描包:

1
2
3
4
5
6
// test.java

// 提升下接口,才会有 scan 方法
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("demo");
ctx.refresh();

进一步的,在配置类里使用 @ComponentScan 注解指定需要扫描的包完成 Spring context 的构建,之后只需要指定配置类 AnnotationConfigApplicationContext(UserDaoConfig.class) 即可,不需要书写多个 bean 的类。

1
2
3
@Configuration
@ComponentScan(basePackages = "demo")
public class UserDaoConfig

配置的模块化

使用 @Import 注解可以实现配置的模块化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@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();
    }
}

之后就只需要 AnnotationConfigApplicationContext(ConfigB.class) 即可,该注解可以以 list 的方式导入多个配置类:

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

类似前面 XML 为主混入注解方式,这里也可以对偶的注解为主混入 XML,即使用 @ImportResource 注解可以将配置类需要的 xml 方式什么的上下文导入,如:

1
2
3
4
5
6
7
8
9
@Configuration
@Import(ServiceConfig.class)
@ImportResource("classpath:beans.xml")
public class D{
    @Bean
    public A a(){
        ...
    }
}

如果需要在 xml 里使用 JavaConfig/xml 也非常方便

1
2
3
4
5
<!-- 引入 xml 作为子模块 -->
<import resource="new-beans.xml">

<!-- 引入 JavaConfig 作为子模块 -->
<bean class="xxx.xxxConfig">

纵观前面的示例可以看出 spring 的 xml 配置式、JavaConfig、注解方式可以混搭在一起。

环境抽象

开发过程中经常会遇到生产和开发的环境切换问题,特别是数据库的连接,为了方便在不同环境下激活不同的 beans , spring 中可以使用 @Profile 注解来限定 bean 加入上 spring 下文的条件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// profile 为 development 时候启用
@Configuration
@Profile("development")
public class StandaloneDataConfig {
    ...
}
// profile 为 production 时候启用
@Configuration
@Profile("production")
public class StandaloneDataConfig {
    ...
}

同时你还可以使用 ! & | 的语法衔接 profile。除了修饰配置类,还可以直接修饰 Bean 一级别。

1
2
3
4
5
@Bean
@Profile("development|test")
public A a(){
  ...
}

在 xml 中使用的话,需要使用内嵌的 <beans> 标签加上 profile="xxx" 属性来完成,这时候 profile 的与或非的关系就由 <beans> 的树形结构来表示了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">
    <!-- 这里的解构就表现了 production&us-east -->
    <beans profile="production">
        <beans profile="us-east">
            ...
        </beans>
    </beans>
</beans>

当前激活 profile 目标由两个独立的属性决定 spring.profiles.activespring.profiles.default, 如果 active 为空,使用 default 值,而默认情况下 active 是为空。而设定这两个参数的方法有下列多种

  • 作为 DispatcherServlet 的初始化参数。
  • 作为 web 应用的上下文,即在 servlet 配置中设置 <init-param> <context-param> 的 active 或者 default 的参数。
  • 作为 JNDI 条目(java:comp/env/entries))。
  • 作为系统的环境变量。
  • 作为 JVM 的系统属性,即 -Dspring.profiles.active="profile1,profile2"
  • 在集成测试上使用 @ActiveProfile 的配置。

在构建上下文时候设置

1
2
3
4
5
// 需要使用 AnnotationConfigApplicationContext,需要在开始构建上下文之前设置
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();

或者给配置类加上配置注解 @PropertySource

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Configuration
@ComponentScan(basePackages = "demo")
@PropertySource("classpath:application.properties")
public class UserDaoConfig {

    @Bean
    @Profile("dev")
    public UserDao userDao() {
        return new UserDaoImpl("7");
    }
}

src/main/resources/application.properties 文件里

1
spring.profiles.active=dev

值得注意的几点

  • 没有限定 profile 的资源都是默认加入上下文的。
  • 设置 profile 是同时含多个的,如上面的举例 -Dspring.profiles.active="profile1,profile2"

spring.profiles.active 配置除了在代码里根据 @Profile 区分加入上下文的资源,还可以将配置分块,根据场景引入不同的配置(连接信息等),参见

处理外部值

处理外部值最简单方式是声明属性源(使用 @PropertySource),之后通过注入 spring 注入 env 成员变量,或是是使用占位符 @Value("${}")

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Configuration
@ComponentScan(basePackages = "demo")
@PropertySource("classpath:application.properties")
public class UserDaoConfig {
    // Autowired 以类型注入属性变量,以供程序后续使用
    @Autowired
    Environment env;
    // 直接将属性值注入,以供使用
    @Value("${spring.profiles.active}")
    String actice;

    @Bean
    @Profile("dev")
    public UserDao userDao() {
        // 手动获取和自动 Value 注入
        System.out.println(env.getProperty("spring.profiles.active") + ", " + actice);
        return new UserDaoImpl();
    }
}

除了属性值之外,@Value 注解还可以注入简单值,这样注解方式也是可以直接注入 value 的,将其应用到构造函数参数上也是可以实现 构造函数参数的简单值注入

1
2
3
4
@Value("hello")
String val;

public ImConstructor(@Value("hello") String str) { }

对应 xml 的配置,需要这样

1
2
<context:property-placeholder location="classpath:foo.properties" />
<bean id="u" class="xxx" value="${spring.profiles.active}" />

Environment&Properties

org.springframework.core.env.Environment 接口提供了多种获取和检查属性值的方式,包括提供默认值,提供转换类,检查是否存在

1
2
3
4
5
6
7
boolean containsProperty(String var1);
@Nullable
String getProperty(String var1);
String getProperty(String var1, String var2);
@Nullable
<T> T getProperty(String var1, Class<T> var2);
<T> T getProperty(String var1, Class<T> var2, T var3);

Properties 文件里是可以衔接系统环境变量的,如有系统环境变量 ENV_MYSQL_HOST=localhost ENV_MYSQL_PORT=3306 可以在 properties 文件里引用。properties 还支持数组,和 Random,支持以 ${} 的方式引用其他的属性

1
2
3
4
5
6
mysql.url = ${ENV_MYSQL_HOST}:${ENV_MYSQL_PORT}/"nameofDB"
mysql.host = ${ENV_MYSQL_HOST}

base.module.elementToSearch = 1,2,3,4,5,6

database.host = ${mysql.host}

spring 默认 propertiesproperties文档,除了 properties 格式的配置文件,还可以使用 yaml 文件来编写配置。

SpEL 表达式

类似于与占位符的 ${} 形式,SpEL(Spring Expression Language) 为 #{},其中为表达式。SpEL 用法较多,见文档,下面简单介绍几种用法

  • 引用其他其他 bean 的属性、调用方法,如 #{userDao.version} #{userDao.getSomething()}
  • 字面量,如 #{3.14} 为浮点数类型,#{Hello} 为字符串类型,#{false} 为 Boolean
  • 表达式中支持运算符,支持包的引入,如 #{T(java.lang.Math).PI * 2}
  • 正则表达式
  • 计算集合类型,索引,过滤,投影

借助 SpEL 表达式可以方便的利用 @Value 注入 map 数据:

1
propertyname={key1:'value1',key2:'value2',....}
1
@Value("#{${propertyname}}")  private Map<String,String> propertyname;

配置类细则

  • 可以使用 @Bean(initMethod = "init")@Bean(destroyMethod = "cleanup") 指定生命周期的钩子。

  • 同样使用 @Scope("prototype") 指定其生存范围。

  • 可以指定多个名称 @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})

  • 如果需要其他的 bean 来完成,直接使用方法即可(spring 会拦截该方法,直接注入上下文空间中的 bean):

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

    @Bean
    public A a() {
        return new A();
    }

    @Bean
    public B b() {
        // 这里的方法调用会被 spring 拦截,并注入
        return new B(a());
    }

    // 这里的传参会被 spring 拦截,并注入
    @Bean
    public C c(A a) {
        return new C(a);
    }
}
  • 同样的,如果需要其他的 bean 来完成,也可以通过参数的类型注入,当有多个时候相同类型的 bean 时候,参数可以使用 @Qualifier 注解来区分类型。

  • 除了使用 @Qualifier 来区别 autowire 时候多个同类型对象,还可以对 @Component 上附加加上 @Primary 注解声明其自动装配时候的权重。

参考

JSR-300 与 spring 的注解对比

Spring javax.inject.*
@Autowired @Inject
@Component @Named / @ManagedBean
@Scope(“singleton”) @Singleton
@Qualifier @Qualifier / @Named
@Value -
@Required -
@Lazy -
ObjectFactory Provider