《Spring实战》笔记(二):装配

2018-03-29  本文已影响0人  JacobY

1 装配机制

Spring具有非常大的灵活性,它提供了三种主要的装配机制:

1.1 自动化装配

Spring从两个角度来实现自动化装配:

  1. 用@Component注解来标记组件类。
    @Component可以设置value属性为bean的id,如果不设置,默认id为类名的首字母缩写。大多数情况下,@Named同@Component
  2. 在配置类中使用@ComponentScan注解来开启组件扫描(也可以使用xml配置 <context:component-scan>)。
    @ComponentScan默认会扫描所在类的包及其所有子包,若果要指定特定的包,可以通过value属性设置,如果要指定一个或多个包,可以使用basePackage类指定或者使用basePackageClassess来指定(指定基础包中的任一个类即可)
  3. 在要使用组件的类中使用@AutoWired注入组件。
    @AutoWired可以声明在类的属性上,也可以声明在方法上。如果没有配置的bean,Spring会抛出异常,使用@AutoWired的required属性设置为false可以避免该情况,但是被尝试注入的参数将会为null。在大多数情况下,@Inject同@AutoWired

1.2 使用Java代码进行装配

你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。
通过代码声明Bean,首先要创建一个方法用以返回一个实例,然后使用@Bean注解来声明。方法名为该Bean的id,也可以使用@Bean的value属性来设定Bean的id。

    @Bean
    public OneBean oneBean() {
      return new OneBean();
    }

1.3 使用XML进行装配:

  1. 使用<bean>元素声明bean
<bean id="myBean" class="myClass"/>
  1. 使用构造器注入初始化bean
    2.1 注入其他bean
<bean id="myBean" class="myClass">
  <constructor-arg ref="anotherBean">
</bean>

也可以使用c命名空间,但在使用之前要在XML顶部声明其模式。

<bean id="myBean" class="myClass" c:arg-ref="anotherBean"/>

在这里,arg表示构造器参数名,有三种写法:

<bean id="myBean" class="myClass" c:_0-ref="anotherBean"/>
<bean id="myBean" class="myClass" c:_-ref="anotherBean"/>

2.2 注入字面量

<bean id="myBean" class="myClass">
  <constructor-arg value="anotherBean">
</bean>

使用c命名空间:

<bean id="myBean" class="myClass" c:arg="anotherBean"/>

其他两种方式同理,和注入bean相比只是去掉了-ref

2.3 注入集合(c命名空间不能注入集合)

<bean id="myBean" class="myClass">
  <constructor-arg></null></constructor-arg>
</bean>
<bean id="myBean" class="myClass">
  <constructor-arg>
    <list>
      <value>xxx</value>
      <value>yyy</value>
    </list>
  </constructor-arg>
</bean>
<bean id="myBean" class="myClass">
  <constructor-arg>
    <list>
      <ref bean=bean1/>
      <ref bean=bean2/>
    </list>
  </constructor-arg>
</bean>
<bean id="myBean" class="myClass">
  <constructor-arg>
    <set>
      <value>xxx</value>
      <value>yyy</value>
    </set>
  </constructor-arg>
</bean>
  1. 使用setter注入初始化bean
    使用setter注入与用构造器注入基本类似,只不过换成了<property>和p命名空间
<bean id="myBean" class="myClass" p:literal="literal" p:pBean-ref="pBean">
  <property name="arg" ref="anotherBean"/>
  <property name="literalValue" value="value"/>
  <property name="collection">
    <list>
      <value>xxx</value>
      <value>yyy</value>
    </list>
  </property>
</bean>
  1. 对于集合类,可以使用util命名空间创建对应的集合bean,再被引用。
<bean id="myBean" class="myClass">
  <property name="arg" ref="testList"/>
</bean>
<util:list name="testList">
  <value>xxx</value>
  <value>yyy</value>
</util:list>

1.4 混合配置

  1. JavaConfig引入JavaConfig,使用@Import注解即可
  2. JavaConfig引入XML配置,使用@ImportResource注解
  3. XML配置引入JavaConfig和XML配置
<bean class="JavaConfig"/>
<import resource="abc.xml"/>

2 高级装配

2.1 profile

@Profile可以用来修饰配置类和声明Bean的方法,用value属性来指定profile。在XML配置中,使用<beans>元素的profile属性来设置profile。激活profile是通过spring.profiles.activespring.profiles.default来确定,如果前者没有设置的话,将会查找后者,如果两者都没有设置的话,则没有激活的profile,只有没有指定profile的bean可以被创建。

2.2 条件化配置

@Conditional可以实现条件化地配置bean。@Conditional的value属性为一个实现了Condition接口的类,需要实现mataches()方法。@Profile本身也使用了@Conditional注解。

    @Bean
    @Conditional(DemoCondition.class)
    public DemoBean demoBean() {
        return new DemoBean();
    }
public class DemoCondition implements Condition {

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

        if (conditionContext.getEnvironment().containsProperty("demo")) {
            return true;
        }
        return false;
    }
}

2.3 处理自动装配的歧义

@Primary用来修饰bean,如果在装配时发生歧义将会优先选择该Bean。@Qualifier用来指定Bean的限定符,与@AutoWired一起使用的时候,将会选择对应限定符的Bean进行装配(默认的限定符为Bean的id)。@Qualifier用来修饰Bean的时候,将会为Bean设置一个限定符。

2.4 bean的作用域

  1. Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
  1. 使用@Scope或者在xml文件中<bean>的scope属性指定作用域。

  2. 使用会话和请求作用域(通过注解)

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.INTERFACES)
    public DemoBean demoBean() {
        return new DemoBean();
    }

要注意的是,@Scope同时还有一个proxyMode属性,它被设置成了ScopedProxyMode.INTERFACES。这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。

作用域代理.JPG

如果DemoBean是一个类的话,就不能使用ScopedProxyMode.INTERFACES,而要使用ScopedProxyMode.TARGET_CLASS

  1. 使用会话和请求作用域(通过XML)
<bean id="demo" class="DemoBean" scope="session">
  <aop:scoped-proxy proxy-target-class="false"/>
</bean>

<aop:scoped-proxy/>表示使用作用域代理,默认创建目标类的代理,如果将proxy-target-class设置为false,将生成基于接口的代理

2.5 运行时注入

Spring提供了两种在运行时求值的方式:

2.5.1 属性占位符

@PropertySource可以引入其他的属性文件,并将属性加载到Spring的Environment对象中属性占位符的格式为${xxx},可以在xml文件中引用,在Java文件中,通过@Value(${xxx})注入到参数中
在使用属性占位符之前,需要先配置一个PropertySourcesPlaceholderConfigurer bean:

@Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
}

如果使用xml配置,则使用 <context:property-holder/>

2.5.2 Spring表达式语言(Spring Expression Language, SpEL)

SpEL使用 #{...}的格式

  1. 表示字面量,直接写。String类型单双引号都可。
#{1} 
#{3.14} 
#{true} 
#{'string'} 
#{"string"}
  1. 引用bean,直接引用bean id即可。链式调用的时候使用 ?. 用以避免空指针异常,如果前一个方法返回为null将不会再调用下一个方法。
#{bean}
#{bean.property1}
#{bean.method1()}
#{bean.method1().method2()}
#{bean.method1()?.method2()}
  1. 访问静态方法和常量,使用T()运算符
#{T(java.util.math).PI}
  1. 运算符 SpEL运算符.JPG

    条件运算中,ternary表示三元运算符。Evlis表示简化的三元运算符,用以判断空值并用默认值代替空值,如:

#{user.name?:"no name"}
  1. 集合,用 [] 获取集合、数组或字符串中的元素。
#{list[0]}
#{studentList.?[age gt 16]} 
#{studentList.^[age gt 16]}
#{studentList.$[age gt 16]}
#{studentList.![name]}
上一篇下一篇

猜你喜欢

热点阅读