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:
Juergen Hoeller
2009-05-07 22:29:55 +00:00
parent 4ccb352aac
commit 266a65982d
26 changed files with 582 additions and 167 deletions

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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();
}
}
}
}

View File

@@ -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;
}
}
}