Spring之高级装配(二)

来源:互联网 发布:vb控件工具箱 编辑:程序博客网 时间:2024/06/05 01:03

上一节提到Spring之装配bean(一),我们已经了解到了装配的基础知识,这部分是更为高级的bean装配技术。

高级装配内容:

  • spring profile
  • 有条件的bean
  • 处理自动装配的歧义性
  • bean的作用域

1.spring profile

应用程序从一个环境迁移到另一个环境。开发阶段,某些环境相关做法可能并不适合迁移到生产环境中,甚至几遍迁移过去也无法正常工作,所以在不同的环境中使用的bean可能不同。下面以datasource为例:

在Java配置类中配置(“dev”和“prod”生产环境):

@Configurationpublic class DataSourceConfig{    @Bean(destroyMethod="shutdown")    @Profile("dev")    public DataSource dataSource(){        return new EmbeddedDatabaseBuilder()               .addScript("classpath:schema.sqsl")               .addScript("classpath:test-data.sql")               .build();    }    @Bean    @Profile("prod")    public DataSource dataSource(){       JndiObjectFactoryBean jndi=new JndiObjectFactoryBean();        jndi.setJndiName("jdbc/myDS");        jndi.setResourceRef(true);        jndi.setProxyInterface(javax.sql.DataSource.class);        return (DataSource) jndi.getObject();    }}

在xml中配置:

<beans xmlns=".......">     <beans profile="dev">         <jdbc:embedded-database id="dataSource">         <jdbc:script location="classpath:schema.sql" />         <jdbc:script location="classpath:test-data.sql" />         </jdbc:embedded-database>     </beans>     <beans profile="qa">       <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destory-method="close"            p:url="jdbc:h2:tcp://dbserver/~/test"            .......            p:maxActive="30"/>     </beans></beans>

激活(使用)哪一个profile:

<!--为应用山下文设置默认的profile--><context-param>     <param-name>spring.profiles.default</param-name>     <param-name>dev</param-name></context-param><!--为Servlet设置默认的profile--><servlet>     <servlet-name>appServlet</servlet-name>     <servlet-class>      org.springframework.web.servlet.DispatcherServlet     </servlet-class>     <init-param>        <param-name>spring.profiles.default</param-name>        <param-value>dev</param-value>     </init-param>        <load-on-startup>1</load-on-startup></servlet>

2.有条件的bean

假设你希望一个或多个bean只有在应用的类路径下包含特定的库时才创建。或者我们希望某个bean只有当另外某个特定的bean也声明了之后才会创建。我们还可能要求只有某个特定的环境变量设置之后才会创建某个bean。在Spring4之前,很难实现这种级别的条件化配置,但是Spring4引入了一个新的@Conditioinal注解,他可以用到带有@Bean注解的方法上。如果给定的条件计算结果为true,就会创建这个bean,否则的话,这个bean就会被忽略。
有条件的bean一般在java配置注入bean的方法中使用到。

@Configurationpublic class ConditionalConfig {    @Bean    @Conditional(MagicExistsConditional.class) //条件化的创建bean    public MagicBean magicBean() {        return new MagicBean();    }}
public class MagicExistsConditional implements Condition{    @Override    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {        Environment evn = context.getEnvironment();        return evn.containsProperty("magic");    }}

如你所见,设置给@Conditional的类可以是任意实现了Condition接口的的类型。而实现这个接口只需要实现matches方法,如果matches方法返回true就创建该bean,如果返回false则不创建bean,上例中我们就是根据环境变量中是否存在magic变量,来决定matches的返回值,进而决定是否创建MagicBean的。

但是在实际生产中,ConditionContext context, AnnotatedTypeMetadata metadata这两个参数可以对类中的其他信息进行判断,有兴趣可以查一下这两个类的api。

3.处理自动装配的歧义性

如果不仅有一个bean能够匹配结果的话,这种歧义性阻碍Spring自动装配属性、构造器参数或方法参数。 下面是一个例子:

@Autowiredpublic void setDessert(Dessert dessert){   this.dessert = dessert;}
@Componentpublic class Cake implements Dessert{...}@Componentpublic class Cookies implements Dessert{...}@Componentpublic class IceCream implements Dessert{...}

在这里,当Spring试图自动装配setDessert()中的Dessert参数时,它并没有唯一、无歧义的可选值。因而,Spring会抛出NoUniqueBeanDefinitionException;

解决方案1:将其中一个bean设置为首选

@Component@Primarypublic class Cake implements Dessert{...}

或者在xml中

<bean id = "iceCream"class = "com.desserteater.Icecream"primary = "true">  </bean>

解决方法2:使用限定符(qualifier)来帮助Spring将可选的bean的范围缩小

@Autowired@Qualifier("cake")public void setDessert(Dessert dessert){   this.dessert = dessert;}

4、bean的作用域

  • 单例(singleton):在整个应用中,只创建bean的一个实例。
  • 原型(prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
  • 会话(Session):在Web应用中,为每个会话创建一个bean实例。
  • 请求(Request):在Web应用中,为每个请求创建一个bean实例。

单例是默认的作用域,但是正如之前所述,对于易变的类型,这并不合适。如果选择其他的作用域,要使用@Scope注解,他可以与@Component(自动装配)或@Bean(JavaConfig)一起使用。

例如,如果你使用组件扫描来发现声明bean,那么你可以在bean的类上使用@Scope注解,将其声明为原型bean,如下:

@Component@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)//@Scope("prototype")public class Notepad{...}

或者

<bean id="notepad"      class="com.myapp.Notepad"      scope="prototype"  />

使用会话和请求作用域

@Component@Scope(      value=WebApplicationContext.SCOPE_SESSION,      proxyMode=ScopedProxyMode.INTERFACES)public ShoppingCart cart(){...}

这里,我们将value设置成了WebApplicationContext中的SCOPE_SESSION常量(它的值是session)。这会告诉Spring为Web应用中的每个会话创建一个ShoppingCart。这会创建多个ShoppingCart bean 实例,但是对于给定的会话只会创建一个实例,在当前会话相关的操作中,这个bean实际上相当于单例。
@Scope同时还有一个proxyMode属性,它被设置成了ScopedProxyMode.INTERFACES。这个属性解决了将会话或请求作用域的bean注入到单例bean中所遇到的问题。作用域代理能够延迟注入请求和会话作用域的bean。

在xml中注入:

<bean id="cart"         class="com.myapp.ShoppingCart"         scope="session">     <aop:scoped-proxy /></bean>
原创粉丝点击