原型模式(Prototype Pattern)在实际的使用中无处不在。比如spring容器中的bean,就有两种存在方式,单例和原型。下面就来仔细看看。
在spring容器中默认使用单例模式。在AbstractBeanDefinition中有相关说明,如下:
/**
* Constant for the default scope name: {@code ""}, equivalent to singleton
* status unless overridden from a parent bean definition (if applicable).
*/
public static final String SCOPE_DEFAULT = "";
下面就来看一下spring容器实现单例的效果
public static void main(String[] args) {
BeanFactory factory = new AnnotationConfigApplicationContext(Config.class);
Student student1 = factory.getBean(Student.class);
Student student2 = factory.getBean(Student.class);
System.out.println(student1);
System.out.println(student2);
System.out.println(student1 == student2);
}
输出:
com.dong.designs.prototype.Student@3012646b
com.dong.designs.prototype.Student@3012646b
true
可以看到,两次获取对象的内存地址是一样的。表明是同一个对象。
Config和Student源码:
@Configuration
public class Config {
@Bean
public Student student(){
return new Student();
}
}
public class Student {
}
在ConfigurableBeanFactory中定义了两个常量来指定Java Bean在spring容器中的scope。
/**
* Scope identifier for the standard singleton scope: {@value}.
* <p>Custom scopes can be added via {@code registerScope}.
* @see #registerScope
*/
String SCOPE_SINGLETON = "singleton";
/**
* Scope identifier for the standard prototype scope: {@value}.
* <p>Custom scopes can be added via {@code registerScope}.
* @see #registerScope
*/
String SCOPE_PROTOTYPE = "prototype";
再来仔细看一下spring容器的原型模式。在Config类做如下修改:
@Configuration
public class Config {
@Bean
@Scope(scopeName = "prototype")
public Student student(){
return new Student();
}
}
因此使用注解@Scope(scopeName = “prototype”)可以来指定这个类为原型模式。再来看一下输出结果:
此时两次获取到的不是同一个对象。原型模式的目的就是要保证每一次获取这个类的实例的时候都是一个新的对象(以及实例中的对象属性都是新的对象)。
下面来仔细看一下spring容器是如何原型模式的。
从getBean(type)来开始,上面创建的是一个AnnotationConfigApplicationContext实例,它的机构图如下:
在看一些getBean方法有那些实现:
可以看到AnnotationConfigApplicationContext实现了AbstractApplicationContext。实现如下:
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}
//模版设计模式,这是模版方法,获取BeanFactory实例。子类实现
@Override
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
而getBeanFactory()方法有两个类实现GenericApplicationContext和AbstractRefreshableApplicationContext
这里GenericApplicationContext刚好又是AnnotationConfigApplicationContext的父类,因此这里getBeanFactory的逻辑实现在GenericApplicationContext中:
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry{
private final DefaultListableBeanFactory beanFactory;
/**
* Create a new GenericApplicationContext.
* @see #registerBeanDefinition
* @see #refresh
*/
public GenericApplicationContext() {
//在构造器中直接new一个DefaultListableBeanFactory实例
this.beanFactory = new DefaultListableBeanFactory();
}
/**
* Return the single internal BeanFactory held by this context
* (as ConfigurableListableBeanFactory).
*/
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
}
现在可以发现,在注解模式的spring容器中,BeanFactory的实现是DefaultListableBeanFactory 。它的结构图如下:
具体实现如下:
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
return getBean(requiredType, (Object[]) null);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
Assert.notNull(requiredType, "Required type must not be null");
Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
if (resolved == null) {
throw new NoSuchBeanDefinitionException(requiredType);
}
return (T) resolved;
}
@Nullable
private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
//获取类的NamedBeanHolder(实例容器,封装bean实例和名称)实例
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
if (namedBean != null) {
return namedBean.getBeanInstance();
}
//获取父容器,这里只有spring容器,没有父容器。parent=null
BeanFactory parent = getParentBeanFactory();
if (parent instanceof DefaultListableBeanFactory) {
return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
}
else if (parent != null) {
ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
if (args != null) {
return parentProvider.getObject(args);
}
else {
return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
}
}
return null;
}
//获取Java实例的核心逻辑
@Nullable
private <T> NamedBeanHolder<T> resolveNamedBean(
ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {
//解析实例的名称,这里结果是student
Assert.notNull(requiredType, "Required type must not be null");
String[] candidateNames = getBeanNamesForType(requiredType);
if (candidateNames.length > 1) {
...
}
//执行到这里 ,通过new方式构造NamedBeanHolder实例,这里又通过 getBean来获取实例
if (candidateNames.length == 1) {
String beanName = candidateNames[0];
return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
}else if (candidateNames.length > 1) {
...
}
return null;
}
}
从这段代码中可以发现,最终获取bean实例的还是调用getBean方法,而从DefaultListableBeanFactory 的结构关系可以看到,它继承了AbstractBeanFactory 。而AbstractBeanFactory中也实现了getBean方法,下面来看是如何实现的:
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
throws BeansException {
//调用doGetBean
return doGetBean(name, requiredType, args, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//继续解析名称,这里还是student
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//从缓存中查找有没有手动注册的实例,这里是没有的
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
//...日志...
//判断是否是factoryBean,这一段和本文无关,返回的缓存中实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
//从父容器中查找,这里没有父容器
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
...
}
//调用时 typeCheckOnly=false ,因此会执行到markBeanAsCreated方法,标记这个实例已经创建
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
//获取这个类的RootBeanDefinition,这是构建实例的模版
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
//初始化bean的依赖,注意,这里就有检查循环依赖的逻辑,本文不着重讨论这个
//本例子中是没有依赖的,这一步可以跳过
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
//这里就是创建实例的真正的逻辑了
if (mbd.isSingleton()) {
//单例的实现。这里就不多讨论了,很简单,就是存在容器中,需要的时候去取
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}else if (mbd.isPrototype()) {
//如果类的注册类型是原型模式。就创建一个新的实例
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
//标记当前名称的原型实例正在创建中
beforePrototypeCreation(beanName);
//调用模版方法createBean来真正的创建实例
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}else {
//自定义的scope,这里就不讨论
}
}catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
//检查required类型是否与实际bean实例的类型匹配。
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
//返回
return (T) bean;
}
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException;
}
AbstractBeanFactory中的模版方法createBean()用于创建真正的类的实例、从上图可以看到,只有它的子类AbstractAutowireCapableBeanFactory实现了这个模版方法:
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
//---------------------------------------------------------------------
// Implementation of relevant AbstractBeanFactory template methods
//---------------------------------------------------------------------
/**
* Central method of this class: creates a bean instance,
* populates the bean instance, applies post-processors, etc.
* 执行创建bean实例,填充bean实例,以及创建完之后对实例的处理等等操作的核心方法
* @see #doCreateBean
*/
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
mbdToUse.prepareMethodOverrides();
}catch (BeanDefinitionValidationException ex) {...}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}catch (Throwable ex) {...}
try {
//doCreateBean才是最终获取或创建对象的方法
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}catch(Exception e){...}
}
/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* <p>Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
* @see #instantiateBean
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
//如果是单例模式,就去缓存中取
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
//如果不是单例模式,就创建一个新的对象
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
...
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}catch (Throwable ex) {...}
return exposedObject;
}
/**
* Create a new instance for the specified bean, using an appropriate instantiation strategy:
* factory method, constructor autowiring, or simple instantiation.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a BeanWrapper for the new instance
* @see #obtainFromSupplier
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
* @see #instantiateBean
*/
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
//默认使用无参构造函数来创建bean实例
return instantiateBean(beanName, mbd);
}
}
从上可以看到,如果不是单例模式的话,会调用createBeanInstance方法来创建一个全新的bean实例。到此spring容器中原型模式的实现源码已经解读完毕。
再来看一下Java源码中是怎么样使用原型模式的。
在Java中的Object对象中,定了一个clone方法。它是一个本地的protect方法,如下:
protected native Object clone() throws CloneNotSupportedException;
如果一个对象想要调用Object的clone方法 ,而不实现Cloneable接口,就会抛出CloneNotSupportedException异常。
由于Object#clone()是用protected修饰,表示只能在Object所在包内或者Object子类中通过super.clone()来进行调用。子类只能调用受保护的 clone方法来克隆它自己的对象 。且 必须将重新定义 的clone方法用 public来修饰, 才能允许所有地方可以调用clone方法来克隆对象
因此想要调用Object提供的clone方法,需要这样来实现:
public class Student implements Cloneable {
private Object object ;
public Student(Object object) {
this.object = object;
}
//将protected改为public
@Override
public Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
public Object getObject() {
return object;
}
}
- 在 Java SE 1.4 之前 , clone 方法的返回类型总是 Object , 而现在可以为你的 clone
方法指定正确的返回类型 。 这是协变返回类型的一个例子
测试一下:
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student(new Object());
Student clone = s1.clone();
System.out.println(s1+"->"+s1.getObject());
System.out.println(clone+"->"+clone.getObject());
}
输出结果:
com.dong.designs.prototype.Student@63947c6b->java.lang.Object@2b193f2d
com.dong.designs.prototype.Student@355da254->java.lang.Object@2b193f2d
可以看到,Object的clone方法实现了对象本身的复制。但是对于对象的引用类型的属性也只是实现了引用地址的复制。如果这个引用属性是可变对象实例。两个Student实例共享这个可变实例则会导致安全问题。
Object提供的clone是一种浅拷贝。使用浅拷贝时一定要确保被拷贝对象的引用类型的属性实例是不可变(如String)。否则需要重写拷贝逻辑,实现所有引用属性的拷贝。
如下改造后可实现深拷贝:
public class Student implements Cloneable {
private Date birthday;
public Student(Date birthday) {
this.birthday = birthday;
}
@Override
public Student clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.birthday = (Date) birthday.clone();
return student;
}
public Date getBirthday() {
return birthday;
}
}
测试代码:
public static void main(String[] args) throws CloneNotSupportedException {
Student s1 = new Student(Date.from(Instant.now()));
Student clone = s1.clone();
//修改克隆对象中引用实例的值,如果原来的对象实例中的值没有发生变化,则表示深克隆成功
clone.getBirthday().setTime(100000000000L);
System.out.println(s1);
System.out.println(clone);
System.out.println(s1.getBirthday());
System.out.println(clone.getBirthday());
}
输出:
com.dong.designs.prototype.Student@63947c6b
com.dong.designs.prototype.Student@2b193f2d
Thu Jul 16 17:51:39 CST 2020
Sat Mar 03 17:46:40 CST 1973
可以看到,这就就成功实现了深拷贝。
- 由于Date日期实现了Cloneable ,这里直接使用Date来作为测试对象。但是有一点特别的时,Date重新了hashCode方法,且hashCode是根据时间的毫秒数来计算的,因此就算两个不同的Date实例,如果它们的时间一样,则它们的hashCode就一样,equals也是true.
- clone会破坏单例模式,因此单例类不要实现Cloneable接口或者重写clone方法时直接返回原来的对象。
还有一种深拷贝的方式,就是利用Java的序列化。Java对象反序列化之后会产生一个全新的实例。但是性能较差。不建议使用。