Servlet/Portlet ApplicationContexts use a specific id based on servlet/portlet name; DefaultListableBeanFactory references are serializable now when initialized with an id; scoped proxies are serializable now, for web scopes as well as for singleton beans; injected request/session references are serializable proxies for the current request now
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -236,6 +236,9 @@ public abstract class AbstractFactoryBean<T>
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reflective InvocationHandler for lazy access to the actual singleton object.
|
||||
*/
|
||||
private class EarlySingletonInvocationHandler implements InvocationHandler {
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
|
||||
@@ -868,12 +868,12 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
|
||||
if (mbd.isSingleton() || mbd.isPrototype()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Bean name '" + beanName + "' does not correspond to an object in a Scope");
|
||||
"Bean name '" + beanName + "' does not correspond to an object in a mutable scope");
|
||||
}
|
||||
String scopeName = mbd.getScope();
|
||||
Scope scope = this.scopes.get(scopeName);
|
||||
if (scope == null) {
|
||||
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
|
||||
throw new IllegalStateException("No Scope SPI registered for scope '" + scopeName + "'");
|
||||
}
|
||||
Object bean = scope.remove(beanName);
|
||||
if (bean != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,13 +17,18 @@
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -123,4 +128,55 @@ abstract class AutowireUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given autowiring value against the given required type,
|
||||
* e.g. an {@link ObjectFactory} value to its actual object result.
|
||||
* @param autowiringValue the value to resolve
|
||||
* @param requiredType the type to assign the result to
|
||||
* @return the resolved value
|
||||
*/
|
||||
public static Object resolveAutowiringValue(Object autowiringValue, Class requiredType) {
|
||||
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue) &&
|
||||
autowiringValue instanceof Serializable && requiredType.isInterface()) {
|
||||
autowiringValue = Proxy.newProxyInstance(
|
||||
requiredType.getClassLoader(), new Class[] {requiredType},
|
||||
new ObjectFactoryDelegatingInvocationHandler((ObjectFactory) autowiringValue));
|
||||
}
|
||||
return autowiringValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reflective InvocationHandler for lazy access to the current target object.
|
||||
*/
|
||||
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
|
||||
|
||||
private final ObjectFactory objectFactory;
|
||||
|
||||
public ObjectFactoryDelegatingInvocationHandler(ObjectFactory objectFactory) {
|
||||
this.objectFactory = objectFactory;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
String methodName = method.getName();
|
||||
if (methodName.equals("equals")) {
|
||||
// Only consider equal when proxies are identical.
|
||||
return (proxy == args[0]);
|
||||
}
|
||||
else if (methodName.equals("hashCode")) {
|
||||
// Use hashCode of proxy.
|
||||
return System.identityHashCode(proxy);
|
||||
}
|
||||
else if (methodName.equals("toString")) {
|
||||
return this.objectFactory.toString();
|
||||
}
|
||||
try {
|
||||
return method.invoke(this.objectFactory.getObject(), args);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -81,7 +85,14 @@ import org.springframework.util.StringUtils;
|
||||
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
|
||||
*/
|
||||
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
|
||||
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
|
||||
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
|
||||
|
||||
/** Map from serialized id to factory instance */
|
||||
private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
|
||||
new ConcurrentHashMap<String, Reference<DefaultListableBeanFactory>>();
|
||||
|
||||
/** Optional id for this factory, for serialization purposes */
|
||||
private String serializationId;
|
||||
|
||||
/** Whether to allow re-registration of a different definition with the same name */
|
||||
private boolean allowBeanDefinitionOverriding = true;
|
||||
@@ -127,6 +138,20 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify an id for serialization purposes, allowing this BeanFactory to be
|
||||
* deserialized from this id back into the BeanFactory object, if needed.
|
||||
*/
|
||||
public void setSerializationId(String serializationId) {
|
||||
if (serializationId != null) {
|
||||
serializableFactories.put(serializationId, new WeakReference<DefaultListableBeanFactory>(this));
|
||||
}
|
||||
else if (this.serializationId != null) {
|
||||
serializableFactories.remove(this.serializationId);
|
||||
}
|
||||
this.serializationId = serializationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether it should be allowed to override bean definitions by registering
|
||||
* a different definition with the same name, automatically replacing the former.
|
||||
@@ -724,9 +749,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
for (Class autowiringType : this.resolvableDependencies.keySet()) {
|
||||
if (autowiringType.isAssignableFrom(requiredType)) {
|
||||
Object autowiringValue = this.resolvableDependencies.get(autowiringType);
|
||||
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
|
||||
autowiringValue = ((ObjectFactory) autowiringValue).getObject();
|
||||
}
|
||||
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
|
||||
if (requiredType.isInstance(autowiringValue)) {
|
||||
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
|
||||
break;
|
||||
@@ -824,4 +847,46 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Serialization support
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
protected Object writeReplace() throws ObjectStreamException {
|
||||
if (this.serializationId != null) {
|
||||
return new SerializedBeanFactoryReference(this.serializationId);
|
||||
}
|
||||
else {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Minimal id reference to the factory.
|
||||
* Resolved to the actual factory instance on deserialization.
|
||||
*/
|
||||
private static class SerializedBeanFactoryReference implements Serializable {
|
||||
|
||||
private final String id;
|
||||
|
||||
public SerializedBeanFactoryReference(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
Reference ref = serializableFactories.get(this.id);
|
||||
if (ref == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot deserialize BeanFactory with id " + this.id + ": no factory registered for this id");
|
||||
}
|
||||
Object result = ref.get();
|
||||
if (result == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot deserialize BeanFactory with id " + this.id + ": factory has been garbage-collected");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user