diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java index 5cfcb090..5799f912 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java @@ -1,409 +1,409 @@ -/* - * 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. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package org.springframework.cloud.context.scope; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.ObjectFactory; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.config.Scope; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.cloud.context.config.BeanLifecycleDecorator; -import org.springframework.cloud.context.config.BeanLifecycleDecorator.Context; -import org.springframework.cloud.context.config.StandardBeanLifecycleDecorator; -import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.ParseException; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.util.StringUtils; - -/** - *

- * A generic Scope implementation. - *

- * - * @author Dave Syer - * - * @since 3.1 - * - */ -public class GenericScope implements Scope, BeanFactoryPostProcessor, DisposableBean { - - private static final Log logger = LogFactory.getLog(GenericScope.class); - - public static final String SCOPED_TARGET_PREFIX = "scopedTarget."; - - private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache( - new StandardScopeCache()); - - private String name = "generic"; - - private boolean proxyTargetClass = true; - - private ConfigurableListableBeanFactory beanFactory; - - private StandardEvaluationContext evaluationContext; - - private String id; - - private BeanLifecycleDecorator lifecycle; - - private Map errors = new ConcurrentHashMap<>(); - - /** - * Manual override for the serialization id that will be used to identify the bean - * factory. The default is a unique key based on the bean names in the bean factory. - * - * @param id the id to set - */ - public void setId(String id) { - this.id = id; - } - - /** - * The name of this scope. Default "generic". - * - * @param name the name value to set - */ - public void setName(String name) { - this.name = name; - } - - /** - * Flag to indicate that proxies should be created for the concrete type, not just the - * interfaces, of the scoped beans. - * - * @param proxyTargetClass the flag value to set - */ - public void setProxyTargetClass(boolean proxyTargetClass) { - this.proxyTargetClass = proxyTargetClass; - } - - /** - * The cache implementation to use for bean instances in this scope. - * - * @param cache the cache to use - */ - public void setScopeCache(ScopeCache cache) { - this.cache = new BeanLifecycleWrapperCache(cache); - } - - /** - * Helper to manage the creation and destruction of beans. - * - * @param lifecycle the bean lifecycle to set - */ - public void setBeanLifecycleManager(BeanLifecycleDecorator lifecycle) { - this.lifecycle = lifecycle; - } - - /** - * A map of bean name to errors when instantiating the bean. - * - * @return the errors accumulated since the latest destroy - */ - public Map getErrors() { - return this.errors; - } - - @Override - public void destroy() { - List errors = new ArrayList(); - Collection wrappers = this.cache.clear(); - for (BeanLifecycleWrapper wrapper : wrappers) { - try { - wrapper.destroy(); - } - catch (RuntimeException e) { - errors.add(e); - } - } - if (!errors.isEmpty()) { - throw wrapIfNecessary(errors.get(0)); - } - this.errors.clear(); - } - - /** - * Destroy the named bean (i.e. flush it from the cache by default). - * - * @param name the bean name to flush - * @return true if the bean was already cached, false otherwise - */ - protected boolean destroy(String name) { - BeanLifecycleWrapper wrapper = this.cache.remove(name); - if (wrapper != null) { - wrapper.destroy(); - this.errors.remove(name); - return true; - } - return false; - } - - @Override - public Object get(String name, ObjectFactory objectFactory) { - if (this.lifecycle == null) { - this.lifecycle = new StandardBeanLifecycleDecorator(this.proxyTargetClass); - } - BeanLifecycleWrapper value = this.cache.put(name, - new BeanLifecycleWrapper(name, objectFactory, this.lifecycle)); - try { - return value.getBean(); - } - catch (RuntimeException e) { - this.errors.put(name, e); - throw e; - } - } - - @Override - public String getConversationId() { - return this.name; - } - - @Override - public void registerDestructionCallback(String name, Runnable callback) { - BeanLifecycleWrapper value = this.cache.get(name); - if (value == null) { - return; - } - value.setDestroyCallback(callback); - } - - @Override - public Object remove(String name) { - BeanLifecycleWrapper value = this.cache.remove(name); - if (value == null) { - return null; - } - // Someone might have added another object with the same key, but we - // keep the method contract by removing the - // value we found anyway - return value.getBean(); - } - - @Override - public Object resolveContextualObject(String key) { - Expression expression = parseExpression(key); - return expression.getValue(this.evaluationContext, this.beanFactory); - } - - private Expression parseExpression(String input) { - if (StringUtils.hasText(input)) { - ExpressionParser parser = new SpelExpressionParser(); - try { - return parser.parseExpression(input); - } - catch (ParseException e) { - throw new IllegalArgumentException("Cannot parse expression: " + input, - e); - } - - } - else { - return null; - } - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { - beanFactory.registerScope(this.name, this); - setSerializationId(beanFactory); - } - - /** - * If the bean factory is a DefaultListableBeanFactory then it can serialize scoped - * beans and deserialize them in another context (even in another JVM), as long as the - * ids of the bean factories match. This method sets up the serialization id to be - * either the id provided to the scope instance, or if that is null, a hash of all the - * bean names. - * - * @param beanFactory the bean factory to configure - */ - private void setSerializationId(ConfigurableListableBeanFactory beanFactory) { - - if (beanFactory instanceof DefaultListableBeanFactory) { - - String id = this.id; - if (id == null) { - List list = new ArrayList<>( - Arrays.asList(beanFactory.getBeanDefinitionNames())); - Collections.sort(list); - String names = list.toString(); - logger.debug("Generating bean factory id from names: " + names); - id = UUID.nameUUIDFromBytes(names.getBytes()).toString(); - } - - logger.info("BeanFactory id=" + id); - ((DefaultListableBeanFactory) beanFactory).setSerializationId(id); - - } - else { - logger.warn( - "BeanFactory was not a DefaultListableBeanFactory, scoped proxy beans " - + "cannot be serialized."); - } - - } - - static RuntimeException wrapIfNecessary(Throwable throwable) { - if (throwable instanceof RuntimeException) { - return (RuntimeException) throwable; - } - if (throwable instanceof Error) { - throw (Error) throwable; - } - return new IllegalStateException(throwable); - } - - protected String getName() { - return this.name; - } - - private static class BeanLifecycleWrapperCache { - - private final ScopeCache cache; - - public BeanLifecycleWrapperCache(ScopeCache cache) { - this.cache = cache; - } - - public BeanLifecycleWrapper remove(String name) { - return (BeanLifecycleWrapper) this.cache.remove(name); - } - - public Collection clear() { - Collection values = this.cache.clear(); - Collection wrappers = new LinkedHashSet(); - for (Object object : values) { - wrappers.add((BeanLifecycleWrapper) object); - } - return wrappers; - } - - public BeanLifecycleWrapper get(String name) { - return (BeanLifecycleWrapper) this.cache.get(name); - } - - public BeanLifecycleWrapper put(String name, BeanLifecycleWrapper value) { - return (BeanLifecycleWrapper) this.cache.put(name, value); - } - - } - - /** - * Wrapper for a bean instance and any destruction callback (DisposableBean etc.) that - * is registered for it. Also decorates the bean to optionally guard it from - * concurrent access (for instance). - * - * @author Dave Syer - * - */ - private static class BeanLifecycleWrapper { - - private Object bean; - - private Context context; - - private final String name; - - @SuppressWarnings("rawtypes") - private final BeanLifecycleDecorator lifecycle; - - private final ObjectFactory objectFactory; - - @SuppressWarnings("rawtypes") - public BeanLifecycleWrapper(String name, ObjectFactory objectFactory, - BeanLifecycleDecorator lifecycle) { - this.name = name; - this.objectFactory = objectFactory; - this.lifecycle = lifecycle; - } - - public void setDestroyCallback(Runnable callback) { - this.context = this.lifecycle.decorateDestructionCallback(callback); - } - - @SuppressWarnings("unchecked") - public Object getBean() { - if (this.bean == null) { - synchronized (this.name) { - if (this.bean == null) { - this.bean = this.lifecycle.decorateBean( - this.objectFactory.getObject(), this.context); - } - } - } - return this.bean; - } - - public void destroy() { - if (this.context == null) { - return; - } - Runnable callback = this.context.getCallback(); - if (callback != null) { - callback.run(); - } - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - BeanLifecycleWrapper other = (BeanLifecycleWrapper) obj; - if (this.name == null) { - if (other.name != null) { - return false; - } - } - else if (!this.name.equals(other.name)) { - return false; - } - return true; - } - - } - -} +/* + * 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. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package org.springframework.cloud.context.scope; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.Scope; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.cloud.context.config.BeanLifecycleDecorator; +import org.springframework.cloud.context.config.BeanLifecycleDecorator.Context; +import org.springframework.cloud.context.config.StandardBeanLifecycleDecorator; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.ParseException; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.util.StringUtils; + +/** + *

+ * A generic Scope implementation. + *

+ * + * @author Dave Syer + * + * @since 3.1 + * + */ +public class GenericScope implements Scope, BeanFactoryPostProcessor, DisposableBean { + + private static final Log logger = LogFactory.getLog(GenericScope.class); + + public static final String SCOPED_TARGET_PREFIX = "scopedTarget."; + + private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache( + new StandardScopeCache()); + + private String name = "generic"; + + private boolean proxyTargetClass = true; + + private ConfigurableListableBeanFactory beanFactory; + + private StandardEvaluationContext evaluationContext; + + private String id; + + private BeanLifecycleDecorator lifecycle; + + private Map errors = new ConcurrentHashMap<>(); + + /** + * Manual override for the serialization id that will be used to identify the bean + * factory. The default is a unique key based on the bean names in the bean factory. + * + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * The name of this scope. Default "generic". + * + * @param name the name value to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * Flag to indicate that proxies should be created for the concrete type, not just the + * interfaces, of the scoped beans. + * + * @param proxyTargetClass the flag value to set + */ + public void setProxyTargetClass(boolean proxyTargetClass) { + this.proxyTargetClass = proxyTargetClass; + } + + /** + * The cache implementation to use for bean instances in this scope. + * + * @param cache the cache to use + */ + public void setScopeCache(ScopeCache cache) { + this.cache = new BeanLifecycleWrapperCache(cache); + } + + /** + * Helper to manage the creation and destruction of beans. + * + * @param lifecycle the bean lifecycle to set + */ + public void setBeanLifecycleManager(BeanLifecycleDecorator lifecycle) { + this.lifecycle = lifecycle; + } + + /** + * A map of bean name to errors when instantiating the bean. + * + * @return the errors accumulated since the latest destroy + */ + public Map getErrors() { + return this.errors; + } + + @Override + public void destroy() { + List errors = new ArrayList(); + Collection wrappers = this.cache.clear(); + for (BeanLifecycleWrapper wrapper : wrappers) { + try { + wrapper.destroy(); + } + catch (RuntimeException e) { + errors.add(e); + } + } + if (!errors.isEmpty()) { + throw wrapIfNecessary(errors.get(0)); + } + this.errors.clear(); + } + + /** + * Destroy the named bean (i.e. flush it from the cache by default). + * + * @param name the bean name to flush + * @return true if the bean was already cached, false otherwise + */ + protected boolean destroy(String name) { + BeanLifecycleWrapper wrapper = this.cache.remove(name); + if (wrapper != null) { + wrapper.destroy(); + this.errors.remove(name); + return true; + } + return false; + } + + @Override + public Object get(String name, ObjectFactory objectFactory) { + if (this.lifecycle == null) { + this.lifecycle = new StandardBeanLifecycleDecorator(this.proxyTargetClass); + } + BeanLifecycleWrapper value = this.cache.put(name, + new BeanLifecycleWrapper(name, objectFactory, this.lifecycle)); + try { + return value.getBean(); + } + catch (RuntimeException e) { + this.errors.put(name, e); + throw e; + } + } + + @Override + public String getConversationId() { + return this.name; + } + + @Override + public void registerDestructionCallback(String name, Runnable callback) { + BeanLifecycleWrapper value = this.cache.get(name); + if (value == null) { + return; + } + value.setDestroyCallback(callback); + } + + @Override + public Object remove(String name) { + BeanLifecycleWrapper value = this.cache.remove(name); + if (value == null) { + return null; + } + // Someone might have added another object with the same key, but we + // keep the method contract by removing the + // value we found anyway + return value.getBean(); + } + + @Override + public Object resolveContextualObject(String key) { + Expression expression = parseExpression(key); + return expression.getValue(this.evaluationContext, this.beanFactory); + } + + private Expression parseExpression(String input) { + if (StringUtils.hasText(input)) { + ExpressionParser parser = new SpelExpressionParser(); + try { + return parser.parseExpression(input); + } + catch (ParseException e) { + throw new IllegalArgumentException("Cannot parse expression: " + input, + e); + } + + } + else { + return null; + } + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + throws BeansException { + beanFactory.registerScope(this.name, this); + setSerializationId(beanFactory); + } + + /** + * If the bean factory is a DefaultListableBeanFactory then it can serialize scoped + * beans and deserialize them in another context (even in another JVM), as long as the + * ids of the bean factories match. This method sets up the serialization id to be + * either the id provided to the scope instance, or if that is null, a hash of all the + * bean names. + * + * @param beanFactory the bean factory to configure + */ + private void setSerializationId(ConfigurableListableBeanFactory beanFactory) { + + if (beanFactory instanceof DefaultListableBeanFactory) { + + String id = this.id; + if (id == null) { + List list = new ArrayList<>( + Arrays.asList(beanFactory.getBeanDefinitionNames())); + Collections.sort(list); + String names = list.toString(); + logger.debug("Generating bean factory id from names: " + names); + id = UUID.nameUUIDFromBytes(names.getBytes()).toString(); + } + + logger.info("BeanFactory id=" + id); + ((DefaultListableBeanFactory) beanFactory).setSerializationId(id); + + } + else { + logger.warn( + "BeanFactory was not a DefaultListableBeanFactory, scoped proxy beans " + + "cannot be serialized."); + } + + } + + static RuntimeException wrapIfNecessary(Throwable throwable) { + if (throwable instanceof RuntimeException) { + return (RuntimeException) throwable; + } + if (throwable instanceof Error) { + throw (Error) throwable; + } + return new IllegalStateException(throwable); + } + + protected String getName() { + return this.name; + } + + private static class BeanLifecycleWrapperCache { + + private final ScopeCache cache; + + public BeanLifecycleWrapperCache(ScopeCache cache) { + this.cache = cache; + } + + public BeanLifecycleWrapper remove(String name) { + return (BeanLifecycleWrapper) this.cache.remove(name); + } + + public Collection clear() { + Collection values = this.cache.clear(); + Collection wrappers = new LinkedHashSet(); + for (Object object : values) { + wrappers.add((BeanLifecycleWrapper) object); + } + return wrappers; + } + + public BeanLifecycleWrapper get(String name) { + return (BeanLifecycleWrapper) this.cache.get(name); + } + + public BeanLifecycleWrapper put(String name, BeanLifecycleWrapper value) { + return (BeanLifecycleWrapper) this.cache.put(name, value); + } + + } + + /** + * Wrapper for a bean instance and any destruction callback (DisposableBean etc.) that + * is registered for it. Also decorates the bean to optionally guard it from + * concurrent access (for instance). + * + * @author Dave Syer + * + */ + private static class BeanLifecycleWrapper { + + private Object bean; + + private Context context; + + private final String name; + + @SuppressWarnings("rawtypes") + private final BeanLifecycleDecorator lifecycle; + + private final ObjectFactory objectFactory; + + @SuppressWarnings("rawtypes") + public BeanLifecycleWrapper(String name, ObjectFactory objectFactory, + BeanLifecycleDecorator lifecycle) { + this.name = name; + this.objectFactory = objectFactory; + this.lifecycle = lifecycle; + } + + public void setDestroyCallback(Runnable callback) { + this.context = this.lifecycle.decorateDestructionCallback(callback); + } + + @SuppressWarnings("unchecked") + public Object getBean() { + if (this.bean == null) { + synchronized (this.name) { + if (this.bean == null) { + this.bean = this.lifecycle.decorateBean( + this.objectFactory.getObject(), this.context); + } + } + } + return this.bean; + } + + public void destroy() { + if (this.context == null) { + return; + } + Runnable callback = this.context.getCallback(); + if (callback != null) { + callback.run(); + } + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + BeanLifecycleWrapper other = (BeanLifecycleWrapper) obj; + if (this.name == null) { + if (other.name != null) { + return false; + } + } + else if (!this.name.equals(other.name)) { + return false; + } + return true; + } + + } + +} diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java index 8017a1d4..e27cb492 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/refresh/RefreshScope.java @@ -1,153 +1,153 @@ -/* - * 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. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package org.springframework.cloud.context.scope.refresh; - -import java.io.Serializable; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.cloud.context.scope.GenericScope; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.core.Ordered; -import org.springframework.jmx.export.annotation.ManagedOperation; -import org.springframework.jmx.export.annotation.ManagedResource; - -/** - *

- * A Scope implementation that allows for beans to be refreshed dynamically at runtime - * (see {@link #refresh(String)} and {@link #refreshAll()}). If a bean is refreshed then - * the next time the bean is accessed (i.e. a method is executed) a new instance is - * created. All lifecycle methods are applied to the bean instances, so any destruction - * callbacks that were registered in the bean factory are called when it is refreshed, and - * then the initialization callbacks are invoked as normal when the new instance is - * created. A new bean instance is created from the original bean definition, so any - * externalized content (property placeholders or expressions in string literals) is - * re-evaluated when it is created. - *

- * - *

- * Note that all beans in this scope are only initialized when first accessed, so - * the scope forces lazy initialization semantics. The implementation involves creating a - * proxy for every bean in the scope, so there is a flag - * {@link #setProxyTargetClass(boolean) proxyTargetClass} which controls the proxy - * creation, defaulting to JDK dynamic proxies and therefore only exposing the interfaces - * implemented by a bean. If callers need access to other methods then the flag needs to - * be set (and CGLib present on the classpath). Because this scope automatically proxies - * all its beans, there is no need to add <aop:auto-proxy/> to any bean - * definitions. - *

- * - *

- * The scoped proxy approach adopted here has a side benefit that bean instances are - * automatically {@link Serializable}, and can be sent across the wire as long as the - * receiver has an identical application context on the other side. To ensure that the two - * contexts agree that they are identical they have to have the same serialization id. One - * will be generated automatically by default from the bean names, so two contexts with - * the same bean names are by default able to exchange beans by name. If you need to - * override the default id then provide an explicit {@link #setId(String) id} when the - * Scope is declared. - *

- * - * @author Dave Syer - * - * @since 3.1 - * - */ -@ManagedResource -public class RefreshScope extends GenericScope - implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor, Ordered { - - private ApplicationContext context; - private BeanDefinitionRegistry registry; - private boolean eager = true; - private int order = Ordered.LOWEST_PRECEDENCE - 100; - - /** - * Create a scope instance and give it the default name: "refresh". - */ - public RefreshScope() { - super.setName("refresh"); - } - - @Override - public int getOrder() { - return this.order; - } - - public void setOrder(int order) { - this.order = order; - } - - /** - * Flag to determine whether all beans in refresh scope should be instantiated eagerly - * on startup. Default true. - * - * @param eager the flag to set - */ - public void setEager(boolean eager) { - this.eager = eager; - } - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) - throws BeansException { - this.registry = registry; - } - - @EventListener - public void start(ContextRefreshedEvent event) { - if (event.getApplicationContext() == this.context) { - if (this.eager && this.registry != null) { - for (String name : this.context.getBeanDefinitionNames()) { - BeanDefinition definition = this.registry.getBeanDefinition(name); - if (this.getName().equals(definition.getScope()) - && !definition.isLazyInit()) { - this.context.getBean(name); - } - } - } - } - } - - @ManagedOperation(description = "Dispose of the current instance of bean name provided and force a refresh on next method execution.") - public boolean refresh(String name) { - if (!name.startsWith(SCOPED_TARGET_PREFIX)) { - // User wants to refresh the bean with this name but that isn't the one in the - // cache... - name = SCOPED_TARGET_PREFIX + name; - } - // Ensure lifecycle is finished if bean was disposable - if (super.destroy(name)) { - this.context.publishEvent(new RefreshScopeRefreshedEvent(name)); - return true; - } - return false; - } - - @ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.") - public void refreshAll() { - super.destroy(); - this.context.publishEvent(new RefreshScopeRefreshedEvent()); - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.context = context; - } -} +/* + * 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. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package org.springframework.cloud.context.scope.refresh; + +import java.io.Serializable; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.cloud.context.scope.GenericScope; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.Ordered; +import org.springframework.jmx.export.annotation.ManagedOperation; +import org.springframework.jmx.export.annotation.ManagedResource; + +/** + *

+ * A Scope implementation that allows for beans to be refreshed dynamically at runtime + * (see {@link #refresh(String)} and {@link #refreshAll()}). If a bean is refreshed then + * the next time the bean is accessed (i.e. a method is executed) a new instance is + * created. All lifecycle methods are applied to the bean instances, so any destruction + * callbacks that were registered in the bean factory are called when it is refreshed, and + * then the initialization callbacks are invoked as normal when the new instance is + * created. A new bean instance is created from the original bean definition, so any + * externalized content (property placeholders or expressions in string literals) is + * re-evaluated when it is created. + *

+ * + *

+ * Note that all beans in this scope are only initialized when first accessed, so + * the scope forces lazy initialization semantics. The implementation involves creating a + * proxy for every bean in the scope, so there is a flag + * {@link #setProxyTargetClass(boolean) proxyTargetClass} which controls the proxy + * creation, defaulting to JDK dynamic proxies and therefore only exposing the interfaces + * implemented by a bean. If callers need access to other methods then the flag needs to + * be set (and CGLib present on the classpath). Because this scope automatically proxies + * all its beans, there is no need to add <aop:auto-proxy/> to any bean + * definitions. + *

+ * + *

+ * The scoped proxy approach adopted here has a side benefit that bean instances are + * automatically {@link Serializable}, and can be sent across the wire as long as the + * receiver has an identical application context on the other side. To ensure that the two + * contexts agree that they are identical they have to have the same serialization id. One + * will be generated automatically by default from the bean names, so two contexts with + * the same bean names are by default able to exchange beans by name. If you need to + * override the default id then provide an explicit {@link #setId(String) id} when the + * Scope is declared. + *

+ * + * @author Dave Syer + * + * @since 3.1 + * + */ +@ManagedResource +public class RefreshScope extends GenericScope + implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor, Ordered { + + private ApplicationContext context; + private BeanDefinitionRegistry registry; + private boolean eager = true; + private int order = Ordered.LOWEST_PRECEDENCE - 100; + + /** + * Create a scope instance and give it the default name: "refresh". + */ + public RefreshScope() { + super.setName("refresh"); + } + + @Override + public int getOrder() { + return this.order; + } + + public void setOrder(int order) { + this.order = order; + } + + /** + * Flag to determine whether all beans in refresh scope should be instantiated eagerly + * on startup. Default true. + * + * @param eager the flag to set + */ + public void setEager(boolean eager) { + this.eager = eager; + } + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) + throws BeansException { + this.registry = registry; + } + + @EventListener + public void start(ContextRefreshedEvent event) { + if (event.getApplicationContext() == this.context) { + if (this.eager && this.registry != null) { + for (String name : this.context.getBeanDefinitionNames()) { + BeanDefinition definition = this.registry.getBeanDefinition(name); + if (this.getName().equals(definition.getScope()) + && !definition.isLazyInit()) { + this.context.getBean(name); + } + } + } + } + } + + @ManagedOperation(description = "Dispose of the current instance of bean name provided and force a refresh on next method execution.") + public boolean refresh(String name) { + if (!name.startsWith(SCOPED_TARGET_PREFIX)) { + // User wants to refresh the bean with this name but that isn't the one in the + // cache... + name = SCOPED_TARGET_PREFIX + name; + } + // Ensure lifecycle is finished if bean was disposable + if (super.destroy(name)) { + this.context.publishEvent(new RefreshScopeRefreshedEvent(name)); + return true; + } + return false; + } + + @ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.") + public void refreshAll() { + super.destroy(); + this.context.publishEvent(new RefreshScopeRefreshedEvent()); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } +} diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/thread/ThreadScope.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/thread/ThreadScope.java index 8637cd2b..4b0070be 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/thread/ThreadScope.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/thread/ThreadScope.java @@ -1,36 +1,36 @@ -/* - * 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. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package org.springframework.cloud.context.scope.thread; - -import org.springframework.cloud.context.scope.GenericScope; - -/** - * - * @author Dave Syer - * - * @since 3.1 - * - */ -public class ThreadScope extends GenericScope { - - /** - * Create a scope instance and give it the default name: "thread". - */ - public ThreadScope() { - super(); - super.setName("thread"); - super.setScopeCache(new ThreadLocalScopeCache()); - } - -} +/* + * 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. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package org.springframework.cloud.context.scope.thread; + +import org.springframework.cloud.context.scope.GenericScope; + +/** + * + * @author Dave Syer + * + * @since 3.1 + * + */ +public class ThreadScope extends GenericScope { + + /** + * Create a scope instance and give it the default name: "thread". + */ + public ThreadScope() { + super(); + super.setName("thread"); + super.setScopeCache(new ThreadLocalScopeCache()); + } + +}