15.8. 依赖注入

我们知道,标准的JSF提供了通过faces-config.xml配置文件来定义ManagedBean同时进行ManagedProperty注入的方案。例如,以下配置定义了两个ManagedBean:personBean和communityBean,并对communityBean的name属性进行了初始化与对leader属性进行了依赖注入:

<managed-bean>
    <managed-bean-name>personBean</managed-bean-name>
    <managed-bean-class>test.PersonBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<managed-bean>
    <managed-bean-name>communityBean</managed-bean-name>
    <managed-bean-class>test.CommunityBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
        <property-name>name</property-name>
        <property-class>java.lang.String</例如配置property-class>
        <value>Operamasks</value>
    </managed-property>
    <managed-property>
        <property-name>leader</property-name>
        <property-class>test.PersonBean</property-class>
        <value>#{personBean}</value>
    </managed-property>
</managed-bean>

为了减轻编程工作量,OperaMasks提供了注解@ManagedProperty来进行同样功能的依赖注入。例如,在OperaMasks中可以用以下代码直接注入personBean或aSpringBean或者它们的一个属性值:

@ManagedBean(name="communityBean", scope=ManagedBeanScope.SESSION)
public class Test2Bean implements Serializable {

    //注入另一个LiteBean
    @ManagedProperty(value = "#{personBean}")
    private PersonBean leader;
    
    //注入另一个LiteBean的一个属性
    @ManagedProperty(value = "#{personBean.name}")
    private String leaderName;
    
    //注入一个Spring bean,aSpringBean是Spring配置文件中定义的bean的id值
    @ManagedProperty(value = "#{aSpringBean}")
    private IPersonSpringService personService;
    

通过注解进行注入与通过配置文件注入两种方式可以结合起来使用,遵循以下规则:

既然@ManagedProperty注解是标准JSF注入的一种简写形式,那么注入的发生时机与标准JSF的ManagedBean生命周期是一致的,也就是说,注入动作发生在ManagedBean创建之后(构造方法执行之后)。当ManagedBean中所有ManagedProperty注入完成后,容器将调用@PostConstruct注解的生命周期回调方法。

可以看出,@ManagedProperty注解所定义的注入是与ManagedBean自身的生命周期相关的。OperaMasks中同时还提供了另一种与JSF请求处理生命周期相关的注入动作,通过@Inject注解进行定义。与@ManagedProperty类似,使用@Inject注解也可以用来注入另一个ManagedBean或Spring bean或它们的一个属性值。

@ManagedBean(name="communityBean", scope=ManagedBeanScope.SESSION)
public class CommunityBean implements Serializable {

    //注入另一个LiteBean
    @Inject(value = "#{personBean}")
    private PersonBean leader;
    
    //注入另一个LiteBean的一个属性
    @Inject(value = "#{personBean.name}")
    private String leaderName;
    
    //注入一个Spring bean,aSpringBean是Spring配置文件中定义的bean的id值
    @Inject(value = "#{aSpringBean}")
    private IPersonSpringService personService;

与@ManagedBean相比,使用@Inject进行注入具有这些特点:

OperaMasks中还加入了一个与注入和初始化有关的注解@Init。这个注解作用在ManagedBean的方法上,通过注解的value属性或方法签名(init开头后续首字大写的属性名)指定一个Bean属性,当这个属性进行IoVC绑定之前,如果属性值为null,引擎会回调被注解的方法。例如:

@Bind(id="name")
private String name;

//通过命名规则指定属性
@Init
public void initName() {
    System.out.println("init name called");
    name = "OperMasks";
}

@Bind
private String gender = "male";

//由于属性已有初值,此方法不会被调用
@Init
public void initGender() {
    System.out.println("init gender called");
    gender = "female";
}

@Inject(value = "#{personBean}")
private PersonBean leader;

//通过注解的value属性指定属性
@Init("leader")
public void LeaderInit() {
    System.out.println("init leader called");
}

//由于没有此属性,此方法不会被引擎回调
@Init
pubilc initNoSuchField() {
     System.out.println("init for nothing");
}

这个注解的主要作用是在绑定之前对属性值进行初始化。

注意由于@Inject注入动作也作为IoVC绑定处理,因此也会触发对应的@Init方法,此时属性值还未被注入,对于renew为false的@Inject注入,如果在对应@Init方法中将待注入属性的初值设为非空值,则不会发生后续注入动作。而对于renew为true的@Inject注入,对应的@Init方法将不被执行。