设计模式里面有一种模式叫做享元模式。
①. 共享的对象创建一次即可,之后缓存起来,放入工厂,避免频繁对象创建,回收的的开销
②. 将变化的对象剥离开来,将大粒度的对象拆分成小粒度的对象,不用重复创建的可以不再重复创建,避免大量相似类的开销,从而提高系统资源的利用率。
我们以商品的秒杀活动为例,当客户下完订单后,变化的是商品的库存,而商品的其他属性仍然不变。 所以可以将商品活动类(Activity)缓存到商品工厂(ActivityFactory)里,每次库存改变时,new一个新的库存类Stock塞到商品活动类中即可。
示意图:
实际上spring中也有这样的类似的情况:
spring 中的 bean 默认的是单例的(spring中单例bean也是存在map中的),这样和享元模式一样已经将对象放入到工厂里面了,剩下的就是缓存对象中可变的属性了,到这里你可能会和我一样想到:将单例对象中的属性设置成原型不就行了?
但细想好像又不辣么对,Spring在实例化单例 bean的时候,只会实例化一次,相关属性注入也只会注入一次,之后就放进singletonObjects里面了,以后每次获取时都从singletonObjects里面获取了,其引用的原型对象应该也是同一个了,这就有点矛盾了。
百想不如一动,写代码试试呗。
定义spring.xml:
<bean id="myTestBean" class="org.springframework.beans.factory.test.bean.MyTestBean" scope="prototype">
<property name="userId" value="1"></property>
<property name="age" value="18"></property>
<property name="userName" value="akun" ></property>
</bean>
<bean id="singletonReferencePrototype" class="org.springframework.beans.factory.test.bean.SingletonReferencePrototype">
<property name="myTestBean" ref="myTestBean"></property>
</bean>
定义一个SingletonReferencePrototype 类,其中包含引用myTestBean,将myTestBean的scope 设置为prototype,OK,编写测试代码:
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.loadBeanDefinitions(new ClassPathResource("spring.xml"));
SingletonReferencePrototype referencePrototype = (SingletonReferencePrototype) beanFactory.getBean("singletonReferencePrototype");
System.out.println(referencePrototype.getMyTestBean() == referencePrototype.getMyTestBean());
}
查看打印结果:
true
额,referencePrototype.getMyTestBean() == referencePrototype.getMyTestBean() 打印出来是true,这说明从缓存里面获取到的单例对象 所引用的对象并不是“prototype”的,这就有点违背我们的初衷了。
那我们应该怎样让获取到的引用对象 是 “prototype” 的呢?答案很简单,使用spring的方法注入(请注意这里是方法注入,不是setter方法注入,)。
上代码,为了区分我们使用SingletonReferencePrototype1 类
<bean id="myTestBean" class="org.springframework.beans.factory.test.bean.MyTestBean" scope="prototype">
<property name="userId" value="1"></property>
<property name="age" value="18"></property>
<property name="userName" value="akun" ></property>
</bean>
<bean id="singletonReferencePrototype1" class="org.springframework.beans.factory.test.bean.SingletonReferencePrototype1">
<lookup-method name="getMyTestBean" bean="myTestBean"></lookup-method>
</bean>
通过spring的 lookup-method 指定方法名是getMyTestBean(getter方法,在这里你高兴的话可以称之为gerter方法注入),引用的bean 是 myTestBean, 而此时myTestBean声明的scope依然是“prototype” 的。
再测试:
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.loadBeanDefinitions(new ClassPathResource("spring.xml"));
SingletonReferencePrototype1 referencePrototype = (SingletonReferencePrototype1) beanFactory.getBean("singletonReferencePrototype1");
System.out.println(referencePrototype.getMyTestBean() == referencePrototype.getMyTestBean());
false
这样就OK啦。
附录:
spring官网
相关博客:
使用scoped-proxy解决单例依赖原型:
源码分析spring的scoped-proxy