Spring之高级装配

内容:条件创建bean;指定Scope;解决自动装配多个实现类;表达式语言SpEL

1,Spring之profile,通过@Profile注解给相应的bean设置创建条件

以下展示3种环境下DataSource的条件创建。

//只有当规定的profile激活时,其下的bean才会创建,未设置profile不受影响
@Configuration
public class DataSourceConfig {

    @Bean(destroyMethod = "shutdown")
    @Profile("dev")
    //开发环境
    public DataSource embeddedDataSource() {
        return (DataSource) new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:schema.sql").addScript("classpath:test-data.sql").build();

    }

    @Bean
    @Profile("prod")
    //生产环境
    public DataSource jndiDataSource() {
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("jdbc/myDS");
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
        return (DataSource) jndiObjectFactoryBean.getObject();
    }

    @Bean(destroyMethod = "close")
    @Profile("qa")
    //QA环境
    public DataSource basicDataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setUrl("jdbc:mysql:///mybase");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setInitialSize(10);
        dataSource.setMaxTotal(20);
        return (DataSource) dataSource;
    }
}

测试类里,要激活设置的条件,即使用哪个环境下的DataSource,通过@ActiveProfiles注解进行激活。如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=DataSourceConfig.class)
@ActiveProfiles("dev")//设置为开发环境,其他环境的bean不创建
public class TestProfile {
……
}

当然,也可以通过xml进行profile的设置,即在beans下使用beans。如下:

<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>
<!-- QA环境 -->
<beans profile="qa">
<bean id="dataSource" class="org.apache.tomcat.dbcp.dbcp2.BasicDataSource"
 destroy-method="close" p:url="jdbc:mysql:///mybase" p:driverClassName="com.mysql.jdbc.Driver" p:username="root"
 p:password="root" p:initialSize="10" p:maxTotal="20">
 </bean>
</beans>
<!-- 生产环境 -->
<beans profile="prod">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatabase" resource-ref="true" proxy-interface="javax.sql.DataSource"></jee:jndi-lookup>
</beans>

</beans>

2,使用@Conditional+Condition接口实现条件化创建bean

先实现Condition接口:

public class MagicExistCondition implementsCondition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata atm) {
        Environment en = context.getEnvironment();
        return en.containsProperty("magic");
    }
}

然后在创建bean的时候引入@Conditional注解:

public class ConditionalCreateMagicBean {
    @Bean
    @Conditional(MagicExistCondition.class)
    public MagicBean getMagicBean() {
        return new MagicBean();
    }
}

3,Spring默认创建的是单例,通过设置bean上的@scope注解,可以进行修改。

如:

@Component("banana")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)//比直接使用"prototype"更加安全
public class Banana implements Fruit {
    @Bean
 @Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES)
    //Scope里第二个属性解决了会话bean注入到单例bean的问题,因为会话bean是访问时才有的
    //会话bean是委托给一个域代理bean的,
    //然后域代理注入单例bean(cglib代理第二个属性值INTERFACES改为TARGET_CLASS)
    public Orange getOrange() {
        return new Orange();
    }
}

当然,也可以通过xml配置:

<bean name="orange" class="scopeChoice.Orange"
scope="session">
<aop:scoped-proxy/><!-- 默认使用cglib代理 -->
<aop:scoped-proxy proxy-target-class="false"/><!-- 使用接口代理方式 -->
</bean>

4,我们使用自动装配@Autowired注解,如果一个接口有多个实现类,那么它不知道该自动装配哪个bean,此时可以使用@Primary 注解,表示优先使用此实现类。也可以通过具名化即@Qualifier解决。使用如下:

@Component("orange")
@Primary // 对应xml中bean属性设置primary="true"
public class Orange implements Fruit {

}

@Component("banana")
public class Banana implements Fruit {
    @Bean
    @Qualifier("method-orange")
    public Orange getOrange() {
        return new Orange();
    }
}

测试类:对于非优先装配bean,要同时使用 @Autowired@Qualifier注解

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = FruitConfig.class)
public class TestFruit {
    @Autowired //优先orange
    private Fruit f1;
    @Autowired
    @Qualifier
("method-orange")    
    private Fruit f2;
    @Autowired
    @Qualifier("banana")
    private Fruit f3;

    @Test
    public void test() {
        System.out.println(f1);
        System.out.println(f2);
        System.out.println(f3);
    }

}

5,Spring强大的表达式语言SpEL

a,占位符:${}   用于从外部文件读取数据

在类的上面引入外部文件:

@Configuration
@PropertySource("classpath:/fruit.properties")

要使用占位符,必须配置一个PropertySourcesPlaceholderConfigurer bean(推荐)或一个PropertyPlaceholderConfigurer bean,如:

@Bean
    //对应xml文件配置是context中的<context:property-placeholder/>
    public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

b.T()运算符   将括号里内容认为是java类型,直接调用常量或静态方法。如

#{T(System).currentTimeMillis()}

c,其他SpEL用法:#{}  如下:

/**
 * #{'hello'}计算字面值
 * #{user}引用其他id的bean,#{user.setUser()}  调用方法
 * #{user.getUser()?.setName()}  多了一个问号,进行非空检查,如果空,后面的方法不予执行,返回null
 * #{user.getUser()?:user}  常用三元表达式,如果空就以默认值代替
 * #{str matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}  正则表达式
 * #{users[2].setName()}  处理集合
 * #{array.?[str eq 'hehe']}  过滤集合,属于hehe的放在一个子集中
 * 上面.?[] 还可以用.^[] 或者.$[] 或.![](投影算符)代替,返回第一个匹配项或最后一个匹配项或非指定项目的子集
 *
 */

猜你喜欢

转载自blog.csdn.net/qq_26567507/article/details/78795694