博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring - 高级装配
阅读量:6241 次
发布时间:2019-06-22

本文共 9682 字,大约阅读时间需要 32 分钟。

高级装配

  • Spring profile
  • 条件化的bean
  • 自动装配与歧义性
  • bean的作用域
  • Spring表达式语言

环境与profile

  • profile可以为不同的环境(dev、prod)提供不同的数据库配置、加密算法等
  • @Profile注解可以在类级别和方法级别,没有指定profile的bean始终都会被创建
  • XML的方式配置 datasource.xml
import javax.sql.DataSource;import org.apache.commons.dbcp.BasicDataSource;import org.springframework.context.annotation.Bean;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;import org.springframework.jndi.JndiObjectFactoryBean;/** * 不同环境所需要的数据库配置 */public class DataSourceDemo {    /**     * EmbeddedDatabaseBuilder会搭建一个嵌入式的Hypersonic数据库     * 模式(schema)定义在schema.sql     * 测试数据通过test-data.sql加载     */    @Bean(destroyMethod="shutdown")    public DataSource dataSource_1(){        return new EmbeddedDatabaseBuilder()                .addScript("classpath:schema.sql")                .addScript("classpath:test-data.sql")                .build();    }        /**     * 使用JNDI从容器中获取一个DataSource     */    @Bean    public DataSource dataSource_2(){        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();        jndiObjectFactoryBean.setJndiName("jdbc/myDs");        jndiObjectFactoryBean.setResourceRef(true);        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);        return (DataSource) jndiObjectFactoryBean.getObject();    }        /**     * 配置DBCP的连接池     */    @Bean(destroyMethod="close")    public DataSource dataSource(){        BasicDataSource dataSource = new BasicDataSource();        dataSource.setUrl("jdbc:h2:tcp://dbserver/~/test");        dataSource.setDriverClassName("org.h2.Driver");        dataSource.setUsername("root");        dataSource.setPassword("root");        dataSource.setInitialSize(20);        dataSource.setMaxActive(30);        return dataSource;    }    }

激活profile

  • 依赖属性:spring.profiles.active和spring.profiles.default

    • 作为DispatcherServlet的初始化参数
    • 作为Web应用的上下文参数
    • 作为JNDI条目
    • 作为环境变量
    • 作为JVM的系统属性
    • 在集成测试类上,使用@ActiveProfile注解设置
  • 在web.xml中配置 见web.xml03
import javax.sql.DataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Profile;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;import org.springframework.jndi.JndiObjectFactoryBean;/** * 不同环境数据源部署配置类 * */@Configurationpublic class DataSourceConfig {    /**     * 为dev profile装配的bean     * @return     */    @Bean(destroyMethod="shutdown")    @Profile("dev")    public DataSource embeddedDataSource(){        return new EmbeddedDatabaseBuilder()                .setType(EmbeddedDatabaseType.H2)                .addScript("classpath:schema.sql")                .addScript("classpath:test-data.sql")                .build();    }        /**     * 为prod profile装配的bean     * @return     */    @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();    }}import javax.sql.DataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Profile;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;/** * 开发环境部署配置类 * */@Configuration@Profile("dev")public class DevelopmentProfileConfig {    @Bean(destroyMethod="shutdown")    public DataSource dataSource(){        return new EmbeddedDatabaseBuilder()                .setType(EmbeddedDatabaseType.H2)                .addScript("classpath:schema.sql")                .addScript("classpath:test-data.sql")                .build();    }}import org.junit.runner.RunWith;import org.springframework.test.context.ActiveProfiles;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/** * 使用profile进行测试 * 当运行集成测试时,Spring提供了@ActiveProfiles注解,使用它来指定运行测试时要激活哪个profile * */@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes={PersistenceConfig.class})@ActiveProfiles("dev")public class PersistenceTest {}

条件化的bean

  • 假设你希望一个或多个bean只有在应用的类路径下包含特定库时才创建
  • 或者希望某个bean只有当另外某个特定的bean也声明了之后才会创建
  • 还可能要求只有某个特定的环境变量设置之后,才会创建某个bean
  • Spring4引入一个新的@Conditional注解,它可以用在带有@Bean注解的方法上
  • 例如,假设有个名为MagicBean的类,我们希望只有设置了magic环境属性的时候,Spring才会实例化这个类
  • 通过ConditionContext,可以:

    • 借助getRegistry()返回的BeanDefinitionRegistry检查bean定义
    • 借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在,甚至检查bean的属性
    • 借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么
    • 读取并探查getResourceLoader()返回的ResourceLoader所加载的资源
    • 借助getClassLoader()返回的ClassLoader加载并检查类是否存在
  • @Profile这个注解本身也使用了@Conditional注解,并且引用ProfileCondition作为条件

    • ProfileCondition会检查value属性,该属性包含了bean的profile名称。检查profile是否激活
import org.springframework.context.annotation.Condition;import org.springframework.context.annotation.ConditionContext;import org.springframework.core.env.Environment;import org.springframework.core.type.AnnotatedTypeMetadata;public class MagicExistsConditional implements Condition {    /**     * 方法简单功能强大     */    @Override    public boolean matches(ConditionContext ctxt, AnnotatedTypeMetadata metadata) {        Environment env = ctxt.getEnvironment();        //检查magic属性        return env.containsProperty("magic");    }}

处理自动装配的歧义

  • 仅有一个bean匹配所需的结果时,自动装配才是有效的,如果不仅有一个bean能够匹配结果的话,这种歧义性会阻碍Spring自动装配属性、构造器参数或方法参数
  • 标示首选(primary)的bean

    • @Component注解上加@Primary注解
    • 在Java配置显示声明 中@Bean注解上加@Primary注解
    • XML配置bean primary="true"
  • 限定自动装配的bean

    • 不止一个限定符时,歧义性问题依然存在
    • 限定bean:在注解@Autowired上加@Qualifier("beanName")
    • 限定符和注入的bean是耦合的,类名称的任意改动都会导致限定失败

* 创建自定义的限定符

* 在@Component注解上加@Qualifier("selfDefinedName")注解* 可以在@Autowired上加@Qualifier("selfDefinedName")* 也可以在Java配置显示定义bean时,在@Bean注解上加@Qualifier("selfDefinedName")
  • 使用自定义的限定符注解

    • 如果有多个相同的限定符时,又会导致歧义性
    • 在加一个@Qualifier注解:Java不允许在同一条目上出现相同的多个注解
      (JDK8支持,注解本身在定义时带有@Repeatable注解就可以,不过Spring的@Qualifier注解上没有)
    • 定义不同的注解,在注解上加@Qualifier,例如在bean上加@Cold和@Creamy(自定义注解)

bean的作用域

  • 默认情况下,Spring应用上下文所有bean都是作为以单例(singleton)的形式创建的
  • Spring定义了多种作用域

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

    • 购物车bean,会话作用域最合适,因为它与给定的用户关联性最大 例如:ShoppingCart
import org.springframework.context.annotation.Scope;import org.springframework.context.annotation.ScopedProxyMode;import org.springframework.stereotype.Component;import org.springframework.web.context.WebApplicationContext;/** * value=WebApplicationContext.SCOPE_SESSION值是session,每个会话会创建一个ShoppingCart * proxyMode=ScopedProxyMode.INTERFACES创建类的代理,确保当前购物车就是当前会话所对应的那一个,而不是其他用户 *  * XML的配置 * 
*
*
* * 使用的是CGLib生成代理类 */@Component@Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)public class ShoppingCart { }

运行时值注入

  • Spring提供两种运行的值注入

    • 属性占位符(Property placeholder)
    • Spring表达式语言(SpEL)
  • 深入学习Spring的Environment

    • String getProperty(String key) 获取属性值
    • String getProperty(String key, String defaultValue) 获取属性值,不存在时返回给定默认值
    • <T> T getProperty(String key, Class<T> targetType); 获取指定类型的属性值
    • <T> T getProperty(String key, Class<T> targetType, T defaultValue); 获取指定类型的属性值,不存在时返回默认值
    • boolean containsProperty(String key); 检查指定属性值是否存在
    • String[] getActiveProfiles(); 返回激活profile名称数组
    • String[] getDefaultProfiles(); 返回默认profile名称数组
    • boolean acceptsProfiles(String... profiles); 如果environment支持给定profile的话,就返回true
  • 解析属性占位符

    • Spring一直支持将属性定义到外部的属性文件中,并使用占位符值将其插入到Spring bean中
    • 在Spring装配中,占位符的形式为使用"${...}"包装的属性名称
    • 在JavaConfig使用 @Value
    • 在XML中 <context:property-placeholder location="classpath:/conf/*.properties" />
  • 使用Spring表达式语言进行装配

    • SpEl拥有特性:

      • 使用bean的ID来引用bean
      • 调用方法和访问对象的属性
      • 对值进行算术、关系和逻辑运算
      • 正则表达式匹配
      • c集合操作
      • Spring Security支持使用SpEL表达式定义安全限制规则
      • 在Thymeleaf模板视图中使用SpEL表达式引用模型数据
    • SpEL表达式要放到 "#{...}"之中:
* #{1}         - 1        * #{T(System).currentTimeMillis()}    -当前毫秒数        * #{sgtPeppers.artist}          - ID为sgtPeppers的bean的artist属性        * #{systemProperties['disc.title']}        -通过systemProperties对象引用系统属性
* 表示字面值    * 浮点数:#{3.14159}    科学计数法:#{9.87E4}        字符串:#{'hello'}    boolean类型:#{false}* 引入bean、属性和方法
* #{sgtPeppers} -bean        #{sgtPeppers.artist} -属性        * #{sgtPeppers.selectArtist()} -方法                * #{sgtPeppers.selectArtist()?.toUpperCase()} -?.判断非空情况调用toUpperCase方法
* 在表达式中使用类型    * 如果要在SpEL中访问类作用域的方法和常量的话,要依赖T()这个关键的运算符。    * 表达Java的Math类    T(java.lang.Math)    * 把PI属性装配待bean属性    T(java.lang.Math).PI    * 调用T()运算符所得到的静态方法    T(java.lang.Math).random()* SpEL运算符
* 算术运算、比较运算、逻辑运算、条件运算、正则表达式、三元运算符        * #{2 * T(java.lang.Math).PI * circle.radius}        -计算圆周长        * #{T(java.lang.Math).PI * circle.radius ^ 2}        -计算圆面积        * #{disc.title + 'by' + disc.artist}                -String类型的连接操作        * #{counter.total == 100}或#{counter.total eq 100} -比较运算        * #{scoreboard.score > 1000 ? 'Winner!' : 'Losser'}    -三元运算        * #{disc.title ?: 'Rattle and Hum'}                -检查null,使用默认值代替null        * #{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}        -匹配有效邮箱        * #{jukebox.songs[4].title}    -计算ID为jukebox的bean的songs集合中第五个元素的title属性        * #{jukebox.songs[T(java.lang.Math).random() * jukebox.songs.size].title}        -获取随机歌曲的title        * #{jukebox.songs.?[artist eq 'Aerosmith']}        -用来对集合过滤查询        * ‘.^[]’ 和 ‘.$[]’分别用来在集合中查询第一个匹配项和最后一个匹配项        * 投影运算符 (.![]),会从集合的每个成员中选择特定的属性放到另外一个集合中
引用:《Spring In Action 4》第3章

转载地址:http://sqdia.baihongyu.com/

你可能感兴趣的文章
Java+SpringBoot实现四则运算
查看>>
【转载】Discriminative Learning和Generative Learning
查看>>
Git中的AutoCRLF与SafeCRLF换行符问题
查看>>
通过Process启动外部程序
查看>>
那些在django开发中遇到的坑
查看>>
cocos2dx lua 绑定之二:手动绑定自定义类中的函数
查看>>
IE CSS HACK
查看>>
北风设计模式课程---深入理解[代理模式]原理与技术
查看>>
php课程 4-14 数组如何定义使用
查看>>
winform托盘时,要运行一个实例,解决办法
查看>>
vagrant up 失败解决办法
查看>>
mysql AM/PM to 24 hours fromat
查看>>
远程唤醒UP Board
查看>>
网页打印
查看>>
Loading——spin.js
查看>>
Hadoop完全分布式环境搭建(四)——基于Ubuntu16.04安装和配置Hadoop大数据环境...
查看>>
Mule ESB工程的部署
查看>>
分离被碰撞物体, 求碰撞冲量
查看>>
js移动端 可移动滑块
查看>>
【kruscal】【最小生成树】poj3522 Slim Span
查看>>