spring总结(三)-高级装配

一、Spring profile
1.1、目的
  • 软件开发过程一般涉及“开发 -> 测试 -> 部署上线”多个阶段,每个阶段的环境的配置参数会有不同,如数据源,文件路径等。为避免每次切换环境时都要进行参数配置等繁琐的操作,可以通过spring的profile功能来进行配置参数的切换。
1.2、方式
  • 项目目录内容,每个环境各有一个包,包中分别包含数据库、日志、路径等配置文件:在这里插入图片描述
  • spring的xml中声明这些bean,实现了通过profile标记不同的环境
<description>spring profile配置</description>

    <!-- 开发环境配置文件 -->
    <beans profile="development">
        <context:property-placeholder
            location="classpath*:common/*.properties, classpath*:development/*.properties" />
    </beans>

    <!-- 测试环境配置文件 -->
    <beans profile="test">
        <context:property-placeholder
            location="classpath*:common/*.properties, classpath*:test/*.properties" />
    </beans>
    
    <!-- 生产环境配置文件 -->
    <beans profile="production">
        <context:property-placeholder
            location="classpath*:common/*.properties, classpath*:production/*.properties" />
    </beans>
  • 通过设置spring.profiles.default和spring.profiles.active这两个属性来激活和使用对应的配置文件。设置的方式如下:

    • 在集成测试类上,使用@Profile注解配置。这个bean只有在production这个配置类是激活状态的时候才会被创建和生效。
    @Bean
    @Profile("production")    
    public DataSource jndiDataSource(){
        JndiObjectFactoryBean jofb=new JndiObjectFactoryBean();
        jofb.setJndiName("jndi/iDS");
        jofb.setResourceRef(true);
        jofb.setProxyInterface(xxx.class);
        return (DataSource) jofb.getObject();
    }
    
    • 作为JNDI条目;
    • 作为环境变量;
    • 作为JVM的系统属性
    -Dspring.profiles.active="production"
    
    • 在web.xml中作为web应用的上下文参数context-param;
    • 在web.xml中作为DispatcherServlet的初始化参数;
    <!-- 在上下文context-param中设置profile.default的默认值 -->
    <context-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>development</param-value>
    </context-param>
    
    <!-- 在上下文context-param中设置profile.active的默认值 -->
    <!-- 设置active后default失效,web启动时会加载对应的环境信息 -->
    <context-param>
        <param-name>spring.profiles.active</param-name>
        <param-value>development</param-value>
    </context-param>
    
二、自动装配的歧义和解决方式
2.1、一个接口多个实现类的情况
  • 案例:Dessert接口的三个实现类:Cake、IceCream、Cookies。三个实现类均以@Component注解,那么当注入该Dessert接口的时候,不确定具体使用的是哪个实现类
  • 解决办法有几种:
    • (1)、设置实现类为首选项,在实现类中添加@Primary注解。代表注入的时候优先选择。(隐患是无法保证只有一个首选项)

      @Bean
      @Primary
      public Dessert IceCream(){
      	return new IceCream
      }
      
      <bean id = "iceCream"
      	class = "com.xxx.IceCream"
      	primary = "true" />
      
    • (2)、限定注入接口的实现类的具体名称,利用@Qualifier(“xxx”)注解,参数为具体实现类的beanId。(但是其隐患是,若重构该实现类,修改了其类名,那么beanId也会被修改。那样我们这里写死的注入的beanId名称就会有问题。Qualifier指定的限定符和bean的名称紧耦合,对类名称的任务改动都会导致限定符失效。)

      @Autowrired
      @Qualifier("iceCream")
      public void setDessert(Dessert dessert){
      	this.dessert = dessert
      }
      
    • (3)、在类定义中,通过注解@Qualifier(“coldxx”)为类创建自定义的限定符,使限定符的名称不强依赖类名称。那样在引用注入的时候,可以使用这个自定义的限定符那指定引用的实现类。(其隐患是,如果多个bean存在相同的特性,想到的解决办法是在每个bean上注解多个自定义的限定符,直到可以区分唯一bean,但是java中不允许同一个条目中重复出现相同类型的注解)

      @Bean
      @Qualifier("cold")
      public Dessert IceCream(){
      	return new IceCream
      }
      
      @Autowrired
      @Qualifier("cold")
      public void setDessert(Dessert dessert){
      	this.dessert = dessert
      }
      
      @Bean
      @Qualifier("cold")
      @Qualifier("cold2")//这种是不被允许的
      public Dessert IceCream(){
      	return new IceCream
      }
      
    • (4)、把限定符声明为注解的方式,那么每个限定符的注解名类目是不一样的。

      @Target({ElementType.CONSTRUCTOR,ElementType.fIELD,ElementType.METHOD,ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Qualifier
      public @interface Cold{}
      
      @Target({ElementType.CONSTRUCTOR,ElementType.fIELD,ElementType.METHOD,ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @Qualifier
      public @interface Cold2{}
      
      @Bean
      @Cold
      @Cold2
      public Dessert IceCream(){
      	return new IceCream
      }
      
      @Autowrired
      @Cold
      @Cold2
      public void setDessert(Dessert dessert){
      	this.dessert = dessert
      }
      
三、bean的作用域
  • 默认情况下,Spring应用上下文所有bean都是作为单例(singleton)的形式创建的。但是某些类是易变有状态的,这种情况下复用单例的bean是有问题的。
3.1、作用域类型
  • 单例(Singleton):整个应用中,只创建bean一个实例
  • 原型/多例(Propotype):每次的注入或者从Spring应用上下文获取的时候,都会创建一个新的bean。
  • 会话(Session):web应用中,为每个会话创建一个bean实例
  • 请求(Request):web应用中,未每个请求创建一个bean实例
3.2、多例的bean设置
  • 组件扫描和类声明的方式来设置多例

    @Component
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    //@Scope("prototype")//两种方式
    public class Notpad(){xxxx}
    
  • java配置中声明Notpad为多例

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    //@Scope("prototype")//两种方式
    public Notpad notpad(){
    	return new Notpad();
    }
    
    • xmlz中声明
    <bean id = "iceCream"
    		class = "com.xxx.IceCream"
    		scope = "prototype" />
    
3.3、会话作用域与请求作用域
  • 案例:电子商务应用中,购物车的bean,如果是单例,会导致每个用户拥有相同的购物车;如果是多例,会导致不能两个线程同时使用购物车。会话作用域最合适,因为是与给定用户关联性最大

  • 方式:

    • vaule=session,会告知springWeb应用中的每一个会话创建一个shoppingCart。虽然有多个shoppingCart的实例,但是对于每一个会话来说,这个bean相当于是单例的。在用户进入系统,创建了会话之后,才会创建shoppingCart的实例
    • proxyMode=interface,解决了将会话或请求作用域的bean注入到单例ban中所遇到的问题。当shoppingCart注入到另外单例类的方法中,此时shoppingCart的bean并不存在。此时spring的处理方式是将shoppingCart bean的代理,注入到方法中。当调用改方法的时候,代理会对其进行懒解析并调用委托给会话作用域内真正的shoppingCart bean。interface代表代理要实现shoppingCart接口,并将调用委托给实现bean。
    @Component
    	@Scope(value = WebApplicationContext.SCOPE_SEESION, proxyMode = ScopedProxyMode.INTERFACE )
    public ShoppingCat cart(){xxxx}
    
    	<bean id = "cart"
    			class = "com.xxx.ShoppingCart"
    			scope = "seesion" >
    		<aop:scoped-proxy>
    		//<aop:scoped-proxy proxy-target-class = "false">
    	</bean>
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值