, Object>();
private final AspectJAdvisorFactory aspectFactory = new ReflectiveAspectJAdvisorFactory();
@@ -144,7 +144,7 @@ private AspectMetadata createAspectMetadata(Class> aspectClass, String aspectN
private MetadataAwareAspectInstanceFactory createAspectInstanceFactory(
AspectMetadata am, Class> aspectClass, String aspectName) {
- MetadataAwareAspectInstanceFactory instanceFactory = null;
+ MetadataAwareAspectInstanceFactory instanceFactory;
if (am.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// Create a shared aspect instance.
Object instance = getSingletonAspectInstance(aspectClass);
@@ -162,23 +162,29 @@ private MetadataAwareAspectInstanceFactory createAspectInstanceFactory(
* is created if one cannot be found in the instance cache.
*/
private Object getSingletonAspectInstance(Class> aspectClass) {
- synchronized (aspectCache) {
- Object instance = aspectCache.get(aspectClass);
- if (instance != null) {
- return instance;
- }
- try {
- instance = aspectClass.newInstance();
- aspectCache.put(aspectClass, instance);
- return instance;
- }
- catch (InstantiationException ex) {
- throw new AopConfigException("Unable to instantiate aspect class [" + aspectClass.getName() + "]", ex);
- }
- catch (IllegalAccessException ex) {
- throw new AopConfigException("Cannot access aspect class [" + aspectClass.getName() + "]", ex);
+ // Quick check without a lock...
+ Object instance = aspectCache.get(aspectClass);
+ if (instance == null) {
+ synchronized (aspectCache) {
+ // To be safe, check within full lock now...
+ instance = aspectCache.get(aspectClass);
+ if (instance == null) {
+ try {
+ instance = aspectClass.newInstance();
+ aspectCache.put(aspectClass, instance);
+ }
+ catch (InstantiationException ex) {
+ throw new AopConfigException(
+ "Unable to instantiate aspect class: " + aspectClass.getName(), ex);
+ }
+ catch (IllegalAccessException ex) {
+ throw new AopConfigException(
+ "Could not access aspect constructor: " + aspectClass.getName(), ex);
+ }
+ }
}
}
+ return instance;
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java
index dd8823ec1c..937690c7be 100644
--- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java
+++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -35,9 +35,8 @@
* Metadata for an AspectJ aspect class, with an additional Spring AOP pointcut
* for the per clause.
*
- * Uses AspectJ 5 AJType reflection API, so is only supported on Java 5.
- * Enables us to work with different AspectJ instantiation models such as
- * "singleton", "pertarget" and "perthis".
+ *
Uses AspectJ 5 AJType reflection API, enabling us to work with different
+ * AspectJ instantiation models such as "singleton", "pertarget" and "perthis".
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -102,20 +101,22 @@ public AspectMetadata(Class> aspectClass, String aspectName) {
this.ajType = ajType;
switch (this.ajType.getPerClause().getKind()) {
- case SINGLETON :
+ case SINGLETON:
this.perClausePointcut = Pointcut.TRUE;
return;
- case PERTARGET : case PERTHIS :
+ case PERTARGET:
+ case PERTHIS:
AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
- ajexp.setLocation("@Aspect annotation on " + aspectClass.getName());
+ ajexp.setLocation(aspectClass.getName());
ajexp.setExpression(findPerClause(aspectClass));
+ ajexp.setPointcutDeclarationScope(aspectClass);
this.perClausePointcut = ajexp;
return;
- case PERTYPEWITHIN :
+ case PERTYPEWITHIN:
// Works with a type pattern
this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass)));
return;
- default :
+ default:
throw new AopConfigException(
"PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
}
@@ -125,10 +126,8 @@ public AspectMetadata(Class> aspectClass, String aspectName) {
* Extract contents from String of form {@code pertarget(contents)}.
*/
private String findPerClause(Class> aspectClass) {
- // TODO when AspectJ provides this, we can remove this hack. Hence we don't
- // bother to make it elegant. Or efficient. Or robust :-)
String str = aspectClass.getAnnotation(Aspect.class).value();
- str = str.substring(str.indexOf("(") + 1);
+ str = str.substring(str.indexOf('(') + 1);
str = str.substring(0, str.length() - 1);
return str;
}
@@ -149,7 +148,7 @@ public Class> getAspectClass() {
}
/**
- * Return the aspect class.
+ * Return the aspect name.
*/
public String getAspectName() {
return this.aspectName;
diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java
index f68fea5627..afb8ceb332 100644
--- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java
+++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java
@@ -97,8 +97,19 @@ public AspectMetadata getAspectMetadata() {
@Override
public Object getAspectCreationMutex() {
- return (this.beanFactory instanceof ConfigurableBeanFactory ?
- ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex() : this);
+ if (this.beanFactory != null) {
+ if (this.beanFactory.isSingleton(name)) {
+ // Rely on singleton semantics provided by the factory -> no local lock.
+ return null;
+ }
+ else if (this.beanFactory instanceof ConfigurableBeanFactory) {
+ // No singleton guarantees from the factory -> let's lock locally but
+ // reuse the factory's singleton lock, just in case a lazy dependency
+ // of our advice bean happens to trigger the singleton lock implicitly...
+ return ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex();
+ }
+ }
+ return this;
}
/**
diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java
index 5c9a7e7448..310ebca459 100644
--- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java
+++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2016 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,10 +17,10 @@
package org.springframework.aop.aspectj.annotation;
import java.util.Collections;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.aspectj.lang.reflect.PerClauseKind;
@@ -43,12 +43,12 @@ public class BeanFactoryAspectJAdvisorsBuilder {
private final AspectJAdvisorFactory advisorFactory;
- private List aspectBeanNames;
+ private volatile List aspectBeanNames;
- private final Map> advisorsCache = new HashMap>();
+ private final Map> advisorsCache = new ConcurrentHashMap>();
private final Map aspectFactoryCache =
- new HashMap();
+ new ConcurrentHashMap();
/**
@@ -56,7 +56,7 @@ public class BeanFactoryAspectJAdvisorsBuilder {
* @param beanFactory the ListableBeanFactory to scan
*/
public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory) {
- this(beanFactory, new ReflectiveAspectJAdvisorFactory());
+ this(beanFactory, new ReflectiveAspectJAdvisorFactory(beanFactory));
}
/**
@@ -80,56 +80,57 @@ public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, Aspect
* @see #isEligibleBean
*/
public List buildAspectJAdvisors() {
- List aspectNames = null;
-
- synchronized (this) {
- aspectNames = this.aspectBeanNames;
- if (aspectNames == null) {
- List advisors = new LinkedList();
- aspectNames = new LinkedList();
- String[] beanNames =
- BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
- for (String beanName : beanNames) {
- if (!isEligibleBean(beanName)) {
- continue;
- }
- // We must be careful not to instantiate beans eagerly as in this
- // case they would be cached by the Spring container but would not
- // have been weaved
- Class> beanType = this.beanFactory.getType(beanName);
- if (beanType == null) {
- continue;
- }
- if (this.advisorFactory.isAspect(beanType)) {
- aspectNames.add(beanName);
- AspectMetadata amd = new AspectMetadata(beanType, beanName);
- if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
- MetadataAwareAspectInstanceFactory factory =
- new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
- List classAdvisors = this.advisorFactory.getAdvisors(factory);
- if (this.beanFactory.isSingleton(beanName)) {
- this.advisorsCache.put(beanName, classAdvisors);
+ List aspectNames = this.aspectBeanNames;
+
+ if (aspectNames == null) {
+ synchronized (this) {
+ aspectNames = this.aspectBeanNames;
+ if (aspectNames == null) {
+ List advisors = new LinkedList();
+ aspectNames = new LinkedList();
+ String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
+ this.beanFactory, Object.class, true, false);
+ for (String beanName : beanNames) {
+ if (!isEligibleBean(beanName)) {
+ continue;
+ }
+ // We must be careful not to instantiate beans eagerly as in this case they
+ // would be cached by the Spring container but would not have been weaved.
+ Class> beanType = this.beanFactory.getType(beanName);
+ if (beanType == null) {
+ continue;
+ }
+ if (this.advisorFactory.isAspect(beanType)) {
+ aspectNames.add(beanName);
+ AspectMetadata amd = new AspectMetadata(beanType, beanName);
+ if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
+ MetadataAwareAspectInstanceFactory factory =
+ new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
+ List classAdvisors = this.advisorFactory.getAdvisors(factory);
+ if (this.beanFactory.isSingleton(beanName)) {
+ this.advisorsCache.put(beanName, classAdvisors);
+ }
+ else {
+ this.aspectFactoryCache.put(beanName, factory);
+ }
+ advisors.addAll(classAdvisors);
}
else {
+ // Per target or per this.
+ if (this.beanFactory.isSingleton(beanName)) {
+ throw new IllegalArgumentException("Bean with name '" + beanName +
+ "' is a singleton, but aspect instantiation model is not singleton");
+ }
+ MetadataAwareAspectInstanceFactory factory =
+ new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
+ advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
- advisors.addAll(classAdvisors);
- }
- else {
- // Per target or per this.
- if (this.beanFactory.isSingleton(beanName)) {
- throw new IllegalArgumentException("Bean with name '" + beanName +
- "' is a singleton, but aspect instantiation model is not singleton");
- }
- MetadataAwareAspectInstanceFactory factory =
- new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
- this.aspectFactoryCache.put(beanName, factory);
- advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
+ this.aspectBeanNames = aspectNames;
+ return advisors;
}
- this.aspectBeanNames = aspectNames;
- return advisors;
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java
index 27d3a9ba43..ec36fbe9cd 100644
--- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java
+++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -109,29 +109,22 @@ public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut decl
/**
- * The pointcut for Spring AOP to use. Actual behaviour of the pointcut will change
- * depending on the state of the advice.
+ * The pointcut for Spring AOP to use.
+ * Actual behaviour of the pointcut will change depending on the state of the advice.
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
- /**
- * This is only of interest for Spring AOP: AspectJ instantiation semantics
- * are much richer. In AspectJ terminology, all a return of {@code true}
- * means here is that the aspect is not a SINGLETON.
- */
@Override
- public boolean isPerInstance() {
- return (getAspectMetadata().getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON);
+ public boolean isLazy() {
+ return this.lazy;
}
- /**
- * Return the AspectJ AspectMetadata for this advisor.
- */
- public AspectMetadata getAspectMetadata() {
- return this.aspectInstanceFactory.getAspectMetadata();
+ @Override
+ public synchronized boolean isAdviceInstantiated() {
+ return (this.instantiatedAdvice != null);
}
/**
@@ -145,20 +138,26 @@ public synchronized Advice getAdvice() {
return this.instantiatedAdvice;
}
- @Override
- public boolean isLazy() {
- return this.lazy;
+ private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
+ return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
+ this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
}
+ /**
+ * This is only of interest for Spring AOP: AspectJ instantiation semantics
+ * are much richer. In AspectJ terminology, all a return of {@code true}
+ * means here is that the aspect is not a SINGLETON.
+ */
@Override
- public synchronized boolean isAdviceInstantiated() {
- return (this.instantiatedAdvice != null);
+ public boolean isPerInstance() {
+ return (getAspectMetadata().getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON);
}
-
- private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
- return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,
- this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
+ /**
+ * Return the AspectJ AspectMetadata for this advisor.
+ */
+ public AspectMetadata getAspectMetadata() {
+ return this.aspectInstanceFactory.getAspectMetadata();
}
public MetadataAwareAspectInstanceFactory getAspectInstanceFactory() {
@@ -213,33 +212,26 @@ private void determineAdviceType() {
}
else {
switch (aspectJAnnotation.getAnnotationType()) {
- case AtAfter:
- case AtAfterReturning:
- case AtAfterThrowing:
- this.isAfterAdvice = true;
- this.isBeforeAdvice = false;
- break;
- case AtAround:
case AtPointcut:
- this.isAfterAdvice = false;
+ case AtAround:
this.isBeforeAdvice = false;
+ this.isAfterAdvice = false;
break;
case AtBefore:
- this.isAfterAdvice = false;
this.isBeforeAdvice = true;
+ this.isAfterAdvice = false;
+ break;
+ case AtAfter:
+ case AtAfterReturning:
+ case AtAfterThrowing:
+ this.isBeforeAdvice = false;
+ this.isAfterAdvice = true;
+ break;
}
}
}
- @Override
- public String toString() {
- return "InstantiationModelAwarePointcutAdvisor: expression [" + getDeclaredPointcut().getExpression() +
- "]; advice method [" + this.aspectJAdviceMethod + "]; perClauseKind=" +
- this.aspectInstanceFactory.getAspectMetadata().getAjType().getPerClause().getKind();
-
- }
-
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
inputStream.defaultReadObject();
try {
@@ -250,11 +242,18 @@ private void readObject(ObjectInputStream inputStream) throws IOException, Class
}
}
+ @Override
+ public String toString() {
+ return "InstantiationModelAwarePointcutAdvisor: expression [" + getDeclaredPointcut().getExpression() +
+ "]; advice method [" + this.aspectJAdviceMethod + "]; perClauseKind=" +
+ this.aspectInstanceFactory.getAspectMetadata().getAjType().getPerClause().getKind();
+ }
+
/**
* Pointcut implementation that changes its behaviour when the advice is instantiated.
- * Note that this is a dynamic pointcut. Otherwise it might
- * be optimized out if it does not at first match statically.
+ * Note that this is a dynamic pointcut; otherwise it might be optimized out
+ * if it does not at first match statically.
*/
private class PerTargetInstantiationModelPointcut extends DynamicMethodMatcherPointcut {
@@ -264,8 +263,9 @@ private class PerTargetInstantiationModelPointcut extends DynamicMethodMatcherPo
private LazySingletonAspectInstanceFactoryDecorator aspectInstanceFactory;
- private PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPointcut,
+ public PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPointcut,
Pointcut preInstantiationPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
+
this.declaredPointcut = declaredPointcut;
this.preInstantiationPointcut = preInstantiationPointcut;
if (aspectInstanceFactory instanceof LazySingletonAspectInstanceFactoryDecorator) {
@@ -275,7 +275,8 @@ private PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPo
@Override
public boolean matches(Method method, Class> targetClass) {
- // We're either instantiated and matching on declared pointcut, or uninstantiated matching on either pointcut
+ // We're either instantiated and matching on declared pointcut,
+ // or uninstantiated matching on either pointcut...
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass)) ||
this.preInstantiationPointcut.getMethodMatcher().matches(method, targetClass);
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java
index 3e76fb47a1..18bf9f327b 100644
--- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java
+++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java
@@ -48,9 +48,15 @@ public LazySingletonAspectInstanceFactoryDecorator(MetadataAwareAspectInstanceFa
@Override
public Object getAspectInstance() {
if (this.materialized == null) {
- synchronized (this.maaif.getAspectCreationMutex()) {
- if (this.materialized == null) {
- this.materialized = this.maaif.getAspectInstance();
+ Object mutex = this.maaif.getAspectCreationMutex();
+ if (mutex == null) {
+ this.materialized = this.maaif.getAspectInstance();
+ }
+ else {
+ synchronized (mutex) {
+ if (this.materialized == null) {
+ this.materialized = this.maaif.getAspectInstance();
+ }
}
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/MetadataAwareAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/MetadataAwareAspectInstanceFactory.java
index a001efbc33..5f89f007c1 100644
--- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/MetadataAwareAspectInstanceFactory.java
+++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/MetadataAwareAspectInstanceFactory.java
@@ -41,7 +41,7 @@ public interface MetadataAwareAspectInstanceFactory extends AspectInstanceFactor
/**
* Return the best possible creation mutex for this factory.
- * @return the mutex object (never {@code null})
+ * @return the mutex object (may be {@code null} for no mutex to use)
* @since 4.3
*/
Object getAspectCreationMutex();
diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java
index ce139f710d..c8504c834c 100644
--- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java
+++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -20,9 +20,9 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.LinkedList;
import java.util.List;
import org.aopalliance.aop.Advice;
@@ -46,6 +46,7 @@
import org.springframework.aop.aspectj.DeclareParentsAdvisor;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.aop.support.DefaultPointcutAdvisor;
+import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConvertingComparator;
@@ -95,6 +96,30 @@ public String convert(Method method) {
}
+ private final BeanFactory beanFactory;
+
+
+ /**
+ * Create a new {@code ReflectiveAspectJAdvisorFactory}.
+ */
+ public ReflectiveAspectJAdvisorFactory() {
+ this(null);
+ }
+
+ /**
+ * Create a new {@code ReflectiveAspectJAdvisorFactory}, propagating the given
+ * {@link BeanFactory} to the created {@link AspectJExpressionPointcut} instances,
+ * for bean pointcut handling as well as consistent {@link ClassLoader} resolution.
+ * @param beanFactory the BeanFactory to propagate (may be {@code null}}
+ * @since 4.3.6
+ * @see AspectJExpressionPointcut#setBeanFactory
+ * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getBeanClassLoader()
+ */
+ public ReflectiveAspectJAdvisorFactory(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+
@Override
public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
@@ -106,7 +131,7 @@ public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstan
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
- List advisors = new LinkedList();
+ List advisors = new ArrayList();
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
@@ -132,7 +157,7 @@ public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstan
}
private List getAdvisorMethods(Class> aspectClass) {
- final List methods = new LinkedList();
+ final List methods = new ArrayList();
ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException {
@@ -151,7 +176,7 @@ public void doWith(Method method) throws IllegalArgumentException {
* for the given introduction field.
* Resulting Advisors will need to be evaluated for targets.
* @param introductionField the field to introspect
- * @return {@code null} if not an Advisor
+ * @return the Advisor instance, or {@code null} if not an Advisor
*/
private Advisor getDeclareParentsAdvisor(Field introductionField) {
DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
@@ -161,9 +186,7 @@ private Advisor getDeclareParentsAdvisor(Field introductionField) {
}
if (DeclareParents.class == declareParents.defaultImpl()) {
- // This is what comes back if it wasn't set. This seems bizarre...
- // TODO this restriction possibly should be relaxed
- throw new IllegalStateException("defaultImpl must be set on DeclareParents");
+ throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");
}
return new DeclareParentsAdvisor(
@@ -197,6 +220,7 @@ private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Clas
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
+ ajexp.setBeanFactory(this.beanFactory);
return ajexp;
}
@@ -229,6 +253,15 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
+ case AtPointcut:
+ if (logger.isDebugEnabled()) {
+ logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
+ }
+ return null;
+ case AtAround:
+ springAdvice = new AspectJAroundAdvice(
+ candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
+ break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
@@ -253,15 +286,6 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
- case AtAround:
- springAdvice = new AspectJAroundAdvice(
- candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
- break;
- case AtPointcut:
- if (logger.isDebugEnabled()) {
- logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
- }
- return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
@@ -275,6 +299,7 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
+
return springAdvice;
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AbstractInterceptorDrivenBeanDefinitionDecorator.java b/spring-aop/src/main/java/org/springframework/aop/config/AbstractInterceptorDrivenBeanDefinitionDecorator.java
index 65169d1a8d..6495f25af9 100644
--- a/spring-aop/src/main/java/org/springframework/aop/config/AbstractInterceptorDrivenBeanDefinitionDecorator.java
+++ b/spring-aop/src/main/java/org/springframework/aop/config/AbstractInterceptorDrivenBeanDefinitionDecorator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -71,7 +71,7 @@ public final BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder defin
BeanDefinition interceptorDefinition = createInterceptorDefinition(node);
// generate name and register the interceptor
- String interceptorName = existingBeanName + "." + getInterceptorNameSuffix(interceptorDefinition);
+ String interceptorName = existingBeanName + '.' + getInterceptorNameSuffix(interceptorDefinition);
BeanDefinitionReaderUtils.registerBeanDefinition(
new BeanDefinitionHolder(interceptorDefinition, interceptorName), registry);
diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AopConfigUtils.java b/spring-aop/src/main/java/org/springframework/aop/config/AopConfigUtils.java
index c227150060..72b5c62653 100644
--- a/spring-aop/src/main/java/org/springframework/aop/config/AopConfigUtils.java
+++ b/spring-aop/src/main/java/org/springframework/aop/config/AopConfigUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -31,11 +31,10 @@
/**
* Utility class for handling registration of AOP auto-proxy creators.
*
- *
Only a single auto-proxy creator can be registered yet multiple concrete
- * implementations are available. Therefore this class wraps a simple escalation
- * protocol, allowing classes to request a particular auto-proxy creator and know
- * that class, {@code or a subclass thereof}, will eventually be resident
- * in the application context.
+ *
Only a single auto-proxy creator should be registered yet multiple concrete
+ * implementations are available. This class provides a simple escalation protocol,
+ * allowing a caller to request a particular auto-proxy creator and know that creator,
+ * or a more capable variant thereof , will be registered as a post-processor.
*
* @author Rob Harrop
* @author Juergen Hoeller
@@ -54,12 +53,10 @@ public abstract class AopConfigUtils {
/**
* Stores the auto proxy creator classes in escalation order.
*/
- private static final List> APC_PRIORITY_LIST = new ArrayList>();
+ private static final List> APC_PRIORITY_LIST = new ArrayList>(3);
- /**
- * Setup the escalation list.
- */
static {
+ // Set up the escalation list...
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
@@ -107,6 +104,7 @@ public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry reg
private static BeanDefinition registerOrEscalateApcAsRequired(Class> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
+
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
@@ -118,6 +116,7 @@ private static BeanDefinition registerOrEscalateApcAsRequired(Class> cls, Bean
}
return null;
}
+
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
diff --git a/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceUtils.java b/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceUtils.java
index 8298f9e099..5c8e7ce94e 100644
--- a/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceUtils.java
+++ b/spring-aop/src/main/java/org/springframework/aop/config/AopNamespaceUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -27,11 +27,11 @@
* Utility class for handling registration of auto-proxy creators used internally
* by the '{@code aop}' namespace tags.
*
- * Only a single auto-proxy creator can be registered and multiple tags may wish
- * to register different concrete implementations. As such this class delegates to
- * {@link AopConfigUtils} which wraps a simple escalation protocol. Therefore classes
- * may request a particular auto-proxy creator and know that class, or a subclass
- * thereof , will eventually be resident in the application context.
+ *
Only a single auto-proxy creator should be registered and multiple configuration
+ * elements may wish to register different concrete implementations. As such this class
+ * delegates to {@link AopConfigUtils} which provides a simple escalation protocol.
+ * Callers may request a particular auto-proxy creator and know that creator,
+ * or a more capable variant thereof , will be registered as a post-processor.
*
* @author Rob Harrop
* @author Juergen Hoeller
@@ -81,11 +81,11 @@ public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
if (sourceElement != null) {
- boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
+ boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
- boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
+ boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
@@ -94,9 +94,8 @@ private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry,
private static void registerComponentIfNecessary(BeanDefinition beanDefinition, ParserContext parserContext) {
if (beanDefinition != null) {
- BeanComponentDefinition componentDefinition =
- new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
- parserContext.registerComponent(componentDefinition);
+ parserContext.registerComponent(
+ new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/config/SimpleBeanFactoryAwareAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/config/SimpleBeanFactoryAwareAspectInstanceFactory.java
index 64cc673bab..22881292f6 100644
--- a/spring-aop/src/main/java/org/springframework/aop/config/SimpleBeanFactoryAwareAspectInstanceFactory.java
+++ b/spring-aop/src/main/java/org/springframework/aop/config/SimpleBeanFactoryAwareAspectInstanceFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -21,8 +21,8 @@
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.Ordered;
+import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
-import org.springframework.util.StringUtils;
/**
* Implementation of {@link AspectInstanceFactory} that locates the aspect from the
@@ -50,9 +50,7 @@ public void setAspectBeanName(String aspectBeanName) {
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
- if (!StringUtils.hasText(this.aspectBeanName)) {
- throw new IllegalArgumentException("'aspectBeanName' is required");
- }
+ Assert.notNull(this.aspectBeanName, "'aspectBeanName' is required");
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java b/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java
index 7f366d5602..47181986bf 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -90,7 +90,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
return proxyFactory.getProxy(getProxyClassLoader());
}
- // No async proxy needed.
+ // No proxy needed.
return bean;
}
@@ -155,7 +155,7 @@ protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
* Subclasses may choose to implement this: for example,
* to change the interfaces exposed.
*
The default implementation is empty.
- * @param proxyFactory ProxyFactory that is already configured with
+ * @param proxyFactory the ProxyFactory that is already configured with
* target, advisor and interfaces and will be used to create the proxy
* immediately after this method returns
* @since 4.2.3
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java
index 40940374d8..6a5a3cf700 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -22,7 +22,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -93,7 +92,7 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
* List of Advisors. If an Advice is added, it will be wrapped
* in an Advisor before being added to this List.
*/
- private List advisors = new LinkedList();
+ private List advisors = new ArrayList();
/**
* Array updated on changes to the advisors list, which is easier
@@ -234,7 +233,7 @@ public boolean removeInterface(Class> intf) {
@Override
public Class>[] getProxiedInterfaces() {
- return this.interfaces.toArray(new Class>[this.interfaces.size()]);
+ return ClassUtils.toClassArray(this.interfaces);
}
@Override
@@ -480,7 +479,7 @@ public int countAdvicesOfType(Class> adviceClass) {
* for the given method, based on this configuration.
* @param method the proxied method
* @param targetClass the target class
- * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
+ * @return a List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
*/
public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
@@ -534,7 +533,7 @@ protected void copyConfigurationFrom(AdvisedSupport other, TargetSource targetSo
/**
* Build a configuration-only copy of this AdvisedSupport,
- * replacing the TargetSource
+ * replacing the TargetSource.
*/
AdvisedSupport getConfigurationOnlyCopy() {
AdvisedSupport copy = new AdvisedSupport();
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AopProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/framework/AopProxyUtils.java
index 15535a5e27..7c611bad31 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/AopProxyUtils.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/AopProxyUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -43,6 +43,25 @@
*/
public abstract class AopProxyUtils {
+ /**
+ * Obtain the singleton target object behind the given proxy, if any.
+ * @param candidate the (potential) proxy to check
+ * @return the singleton target object managed in a {@link SingletonTargetSource},
+ * or {@code null} in any other case (not a proxy, not an existing singleton target)
+ * @since 4.3.8
+ * @see Advised#getTargetSource()
+ * @see SingletonTargetSource#getTarget()
+ */
+ public static Object getSingletonTarget(Object candidate) {
+ if (candidate instanceof Advised) {
+ TargetSource targetSource = ((Advised) candidate).getTargetSource();
+ if (targetSource instanceof SingletonTargetSource) {
+ return ((SingletonTargetSource) targetSource).getTarget();
+ }
+ }
+ return null;
+ }
+
/**
* Determine the ultimate target class of the given bean instance, traversing
* not only a top-level proxy but any number of nested proxies as well —
@@ -59,14 +78,7 @@ public static Class> ultimateTargetClass(Object candidate) {
Class> result = null;
while (current instanceof TargetClassAware) {
result = ((TargetClassAware) current).getTargetClass();
- Object nested = null;
- if (current instanceof Advised) {
- TargetSource targetSource = ((Advised) current).getTargetSource();
- if (targetSource instanceof SingletonTargetSource) {
- nested = ((SingletonTargetSource) targetSource).getTarget();
- }
- }
- current = nested;
+ current = getSingletonTarget(current);
}
if (result == null) {
result = (AopUtils.isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass());
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java
index 7e60723e03..2bb00f6526 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -23,6 +23,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.WeakHashMap;
import org.aopalliance.aop.Advice;
@@ -56,9 +57,6 @@
/**
* CGLIB-based {@link AopProxy} implementation for the Spring AOP framework.
*
- * Formerly named {@code Cglib2AopProxy}, as of Spring 3.2, this class depends on
- * Spring's own internally repackaged version of CGLIB 3..
- *
*
Objects of this type should be obtained through proxy factories,
* configured by an {@link AdvisedSupport} object. This class is internal
* to Spring's AOP framework and need not be used directly by client code.
@@ -203,18 +201,16 @@ public Object getProxy(ClassLoader classLoader) {
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException ex) {
- throw new AopConfigException("Could not generate CGLIB subclass of class [" +
- this.advised.getTargetClass() + "]: " +
- "Common causes of this problem include using a final class or a non-visible class",
+ throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
+ ": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (IllegalArgumentException ex) {
- throw new AopConfigException("Could not generate CGLIB subclass of class [" +
- this.advised.getTargetClass() + "]: " +
- "Common causes of this problem include using a final class or a non-visible class",
+ throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
+ ": Common causes of this problem include using a final class or a non-visible class",
ex);
}
- catch (Exception ex) {
+ catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
@@ -241,10 +237,11 @@ protected Enhancer createEnhancer() {
* validates it if not.
*/
private void validateClassIfNecessary(Class> proxySuperClass, ClassLoader proxyClassLoader) {
- if (logger.isInfoEnabled()) {
+ if (logger.isWarnEnabled()) {
synchronized (validatedClasses) {
if (!validatedClasses.containsKey(proxySuperClass)) {
- doValidateClass(proxySuperClass, proxyClassLoader);
+ doValidateClass(proxySuperClass, proxyClassLoader,
+ ClassUtils.getAllInterfacesForClassAsSet(proxySuperClass));
validatedClasses.put(proxySuperClass, Boolean.TRUE);
}
}
@@ -255,30 +252,35 @@ private void validateClassIfNecessary(Class> proxySuperClass, ClassLoader prox
* Checks for final methods on the given {@code Class}, as well as package-visible
* methods across ClassLoaders, and writes warnings to the log for each one found.
*/
- private void doValidateClass(Class> proxySuperClass, ClassLoader proxyClassLoader) {
- if (Object.class != proxySuperClass) {
+ private void doValidateClass(Class> proxySuperClass, ClassLoader proxyClassLoader, Set> ifcs) {
+ if (proxySuperClass != Object.class) {
Method[] methods = proxySuperClass.getDeclaredMethods();
for (Method method : methods) {
int mod = method.getModifiers();
- if (!Modifier.isStatic(mod)) {
+ if (!Modifier.isStatic(mod) && !Modifier.isPrivate(mod)) {
if (Modifier.isFinal(mod)) {
- logger.info("Unable to proxy method [" + method + "] because it is final: " +
- "All calls to this method via a proxy will NOT be routed to the target instance.");
+ if (implementsInterface(method, ifcs)) {
+ logger.warn("Unable to proxy interface-implementing method [" + method + "] because " +
+ "it is marked as final: Consider using interface-based JDK proxies instead!");
+ }
+ logger.info("Final method [" + method + "] cannot get proxied via CGLIB: " +
+ "Calls to this method will NOT be routed to the target instance and " +
+ "might lead to NPEs against uninitialized fields in the proxy instance.");
}
- else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) && !Modifier.isPrivate(mod) &&
+ else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) &&
proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) {
- logger.info("Unable to proxy method [" + method + "] because it is package-visible " +
- "across different ClassLoaders: All calls to this method via a proxy will " +
- "NOT be routed to the target instance.");
+ logger.info("Method [" + method + "] is package-visible across different ClassLoaders " +
+ "and cannot get proxied via CGLIB: Declare this method as public or protected " +
+ "if you need to support invocations through the proxy.");
}
}
}
- doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader);
+ doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader, ifcs);
}
}
private Callback[] getCallbacks(Class> rootClass) throws Exception {
- // Parameters used for optimisation choices...
+ // Parameters used for optimization choices...
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
@@ -290,20 +292,20 @@ private Callback[] getCallbacks(Class> rootClass) throws Exception {
// unadvised but can return this). May be required to expose the proxy.
Callback targetInterceptor;
if (exposeProxy) {
- targetInterceptor = isStatic ?
+ targetInterceptor = (isStatic ?
new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
- new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
+ new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
}
else {
- targetInterceptor = isStatic ?
+ targetInterceptor = (isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
- new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
+ new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
}
// Choose a "direct to target" dispatcher (used for
// unadvised calls to static targets that cannot return this).
- Callback targetDispatcher = isStatic ?
- new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
+ Callback targetDispatcher = (isStatic ?
+ new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
Callback[] mainCallbacks = new Callback[] {
aopInterceptor, // for normal advice
@@ -317,14 +319,14 @@ private Callback[] getCallbacks(Class> rootClass) throws Exception {
Callback[] callbacks;
// If the target is a static one and the advice chain is frozen,
- // then we can make some optimisations by sending the AOP calls
+ // then we can make some optimizations by sending the AOP calls
// direct to the target using the fixed chain for that method.
if (isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap(methods.length);
- // TODO: small memory optimisation here (can skip creation for methods with no advice)
+ // TODO: small memory optimization here (can skip creation for methods with no advice)
for (int x = 0; x < methods.length; x++) {
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
@@ -345,13 +347,39 @@ private Callback[] getCallbacks(Class> rootClass) throws Exception {
return callbacks;
}
+
+ @Override
+ public boolean equals(Object other) {
+ return (this == other || (other instanceof CglibAopProxy &&
+ AopProxyUtils.equalsInProxy(this.advised, ((CglibAopProxy) other).advised)));
+ }
+
+ @Override
+ public int hashCode() {
+ return CglibAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
+ }
+
+
+ /**
+ * Check whether the given method is declared on any of the given interfaces.
+ */
+ private static boolean implementsInterface(Method method, Set> ifcs) {
+ for (Class> ifc : ifcs) {
+ if (ClassUtils.hasMethod(ifc, method.getName(), method.getParameterTypes())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Process a return value. Wraps a return of {@code this} if necessary to be the
* {@code proxy} and also verifies that {@code null} is not returned as a primitive.
*/
private static Object processReturnType(Object proxy, Object target, Method method, Object retVal) {
// Massage return value if necessary
- if (retVal != null && retVal == target && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
+ if (retVal != null && retVal == target &&
+ !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this". Note that we can't help
// if the target sets a reference to itself in another returned object.
retVal = proxy;
@@ -365,18 +393,6 @@ private static Object processReturnType(Object proxy, Object target, Method meth
}
- @Override
- public boolean equals(Object other) {
- return (this == other || (other instanceof CglibAopProxy &&
- AopProxyUtils.equalsInProxy(this.advised, ((CglibAopProxy) other).advised)));
- }
-
- @Override
- public int hashCode() {
- return CglibAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
- }
-
-
/**
* Serializable replacement for CGLIB's NoOp interface.
* Public to allow use elsewhere in the framework.
@@ -794,12 +810,16 @@ public int accept(Method method) {
}
// We must always proxy equals, to direct calls to this.
if (AopUtils.isEqualsMethod(method)) {
- logger.debug("Found 'equals' method: " + method);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Found 'equals' method: " + method);
+ }
return INVOKE_EQUALS;
}
// We must always calculate hashCode based on the proxy.
if (AopUtils.isHashCodeMethod(method)) {
- logger.debug("Found 'hashCode' method: " + method);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Found 'hashCode' method: " + method);
+ }
return INVOKE_HASHCODE;
}
Class> targetClass = this.advised.getTargetClass();
@@ -822,51 +842,42 @@ public int accept(Method method) {
// Else use the AOP_PROXY.
if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
if (logger.isDebugEnabled()) {
- logger.debug("Method has advice and optimisations are enabled: " + method);
+ logger.debug("Method has advice and optimizations are enabled: " + method);
}
- // We know that we are optimising so we can use the FixedStaticChainInterceptors.
+ // We know that we are optimizing so we can use the FixedStaticChainInterceptors.
int index = this.fixedInterceptorMap.get(key);
return (index + this.fixedInterceptorOffset);
}
else {
if (logger.isDebugEnabled()) {
- logger.debug("Unable to apply any optimisations to advised method: " + method);
+ logger.debug("Unable to apply any optimizations to advised method: " + method);
}
return AOP_PROXY;
}
}
else {
- // See if the return type of the method is outside the class hierarchy
- // of the target type. If so we know it never needs to have return type
- // massage and can use a dispatcher.
- // If the proxy is being exposed, then must use the interceptor the
- // correct one is already configured. If the target is not static, then
- // cannot use a dispatcher because the target cannot be released.
+ // See if the return type of the method is outside the class hierarchy of the target type.
+ // If so we know it never needs to have return type massage and can use a dispatcher.
+ // If the proxy is being exposed, then must use the interceptor the correct one is already
+ // configured. If the target is not static, then we cannot use a dispatcher because the
+ // target needs to be explicitly released after the invocation.
if (exposeProxy || !isStatic) {
return INVOKE_TARGET;
}
Class> returnType = method.getReturnType();
- if (targetClass == returnType) {
+ if (returnType.isAssignableFrom(targetClass)) {
if (logger.isDebugEnabled()) {
- logger.debug("Method " + method +
- "has return type same as target type (may return this) - using INVOKE_TARGET");
+ logger.debug("Method return type is assignable from target type and " +
+ "may therefore return 'this' - using INVOKE_TARGET: " + method);
}
return INVOKE_TARGET;
}
- else if (returnType.isPrimitive() || !returnType.isAssignableFrom(targetClass)) {
- if (logger.isDebugEnabled()) {
- logger.debug("Method " + method +
- " has return type that ensures this cannot be returned- using DISPATCH_TARGET");
- }
- return DISPATCH_TARGET;
- }
else {
if (logger.isDebugEnabled()) {
- logger.debug("Method " + method +
- "has return type that is assignable from the target type (may return this) - " +
- "using INVOKE_TARGET");
+ logger.debug("Method return type ensures 'this' cannot be returned - " +
+ "using DISPATCH_TARGET: " + method);
}
- return INVOKE_TARGET;
+ return DISPATCH_TARGET;
}
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java
index 740a7bb8ff..e1e0cdfb88 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/DefaultAdvisorChainFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -62,9 +62,9 @@ public List getInterceptorsAndDynamicInterceptionAdvice(
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
- MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
+ MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
@@ -98,8 +98,7 @@ else if (advisor instanceof IntroductionAdvisor) {
* Determine whether the Advisors contain matching introductions.
*/
private static boolean hasMatchingIntroductions(Advised config, Class> actualClass) {
- for (int i = 0; i < config.getAdvisors().length; i++) {
- Advisor advisor = config.getAdvisors()[i];
+ for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (ia.getClassFilter().matches(actualClass)) {
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java
index 1f90b0b573..c280830a14 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java
@@ -215,7 +215,8 @@ else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
// Massage return value if necessary.
Class> returnType = method.getReturnType();
- if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
+ if (retVal != null && retVal == target &&
+ returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyCreatorSupport.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyCreatorSupport.java
index e1867efd7e..7df9a24b80 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyCreatorSupport.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyCreatorSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -34,7 +34,7 @@ public class ProxyCreatorSupport extends AdvisedSupport {
private AopProxyFactory aopProxyFactory;
- private List listeners = new LinkedList();
+ private final List listeners = new LinkedList();
/** Set to true when the first AOP proxy has been created */
private boolean active = false;
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java
index d24c525909..a56b5dcd2d 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -640,7 +640,7 @@ public PrototypePlaceholderAdvisor(String beanName) {
}
public String getBeanName() {
- return beanName;
+ return this.beanName;
}
@Override
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java
index af1cf60398..74f3073b93 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -16,6 +16,8 @@
package org.springframework.aop.framework;
+import java.io.Closeable;
+
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
@@ -48,9 +50,9 @@ public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanC
/**
- * Set the ordering which will apply to this class's implementation
- * of Ordered, used when applying multiple processors.
- * Default value is {@code Integer.MAX_VALUE}, meaning that it's non-ordered.
+ * Set the ordering which will apply to this processor's implementation
+ * of {@link Ordered}, used when applying multiple processors.
+ *
The default value is {@code Ordered.LOWEST_PRECEDENCE}, meaning non-ordered.
* @param order the ordering value
*/
public void setOrder(int order) {
@@ -127,6 +129,7 @@ protected void evaluateProxyInterfaces(Class> beanClass, ProxyFactory proxyFac
*/
protected boolean isConfigurationCallbackInterface(Class> ifc) {
return (InitializingBean.class == ifc || DisposableBean.class == ifc ||
+ Closeable.class == ifc || "java.lang.AutoCloseable".equals(ifc.getName()) ||
ObjectUtils.containsElement(ifc.getInterfaces(), Aware.class));
}
@@ -139,7 +142,9 @@ protected boolean isConfigurationCallbackInterface(Class> ifc) {
* @return whether the given interface is an internal language interface
*/
protected boolean isInternalLanguageInterface(Class> ifc) {
- return ifc.getName().equals("groovy.lang.GroovyObject");
+ return (ifc.getName().equals("groovy.lang.GroovyObject") ||
+ ifc.getName().endsWith(".cglib.proxy.Factory") ||
+ ifc.getName().endsWith(".bytebuddy.MockAccess"));
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AdvisorAdapterRegistry.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AdvisorAdapterRegistry.java
index 9eacf479ba..97ff108d83 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AdvisorAdapterRegistry.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AdvisorAdapterRegistry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -31,15 +31,15 @@
public interface AdvisorAdapterRegistry {
/**
- * Return an Advisor wrapping the given advice.
+ * Return an {@link Advisor} wrapping the given advice.
*
Should by default at least support
* {@link org.aopalliance.intercept.MethodInterceptor},
* {@link org.springframework.aop.MethodBeforeAdvice},
* {@link org.springframework.aop.AfterReturningAdvice},
* {@link org.springframework.aop.ThrowsAdvice}.
* @param advice object that should be an advice
- * @return an Advisor wrapping the given advice. Never returns {@code null}.
- * If the advice parameter is an Advisor, return it.
+ * @return an Advisor wrapping the given advice (never {@code null};
+ * if the advice parameter is an Advisor, it is to be returned as-is)
* @throws UnknownAdviceTypeException if no registered advisor adapter
* can wrap the supposed advice
*/
@@ -48,21 +48,20 @@ public interface AdvisorAdapterRegistry {
/**
* Return an array of AOP Alliance MethodInterceptors to allow use of the
* given Advisor in an interception-based framework.
- *
Don't worry about the pointcut associated with the Advisor,
- * if it's a PointcutAdvisor: just return an interceptor.
+ *
Don't worry about the pointcut associated with the {@link Advisor}, if it is
+ * a {@link org.springframework.aop.PointcutAdvisor}: just return an interceptor.
* @param advisor Advisor to find an interceptor for
* @return an array of MethodInterceptors to expose this Advisor's behavior
* @throws UnknownAdviceTypeException if the Advisor type is
- * not understood by any registered AdvisorAdapter.
+ * not understood by any registered AdvisorAdapter
*/
MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException;
/**
- * Register the given AdvisorAdapter. Note that it is not necessary to register
+ * Register the given {@link AdvisorAdapter}. Note that it is not necessary to register
* adapters for an AOP Alliance Interceptors or Spring Advices: these must be
- * automatically recognized by an AdvisorAdapterRegistry implementation.
- * @param adapter AdvisorAdapter that understands a particular Advisor
- * or Advice types
+ * automatically recognized by an {@code AdvisorAdapterRegistry} implementation.
+ * @param adapter AdvisorAdapter that understands particular Advisor or Advice types
*/
void registerAdvisorAdapter(AdvisorAdapter adapter);
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java
index 5e0bd1d23d..82e5856250 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/AfterReturningAdviceInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -26,11 +26,13 @@
import org.springframework.util.Assert;
/**
- * Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}.
+ * Interceptor to wrap an {@link org.springframework.aop.AfterReturningAdvice}.
* Used internally by the AOP framework; application developers should not need
* to use this class directly.
*
* @author Rod Johnson
+ * @see MethodBeforeAdviceInterceptor
+ * @see ThrowsAdviceInterceptor
*/
@SuppressWarnings("serial")
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
@@ -47,6 +49,7 @@ public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
this.advice = advice;
}
+
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java
index 8b3fd0ce20..a58e27cdc3 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/MethodBeforeAdviceInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -21,6 +21,7 @@
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.util.Assert;
@@ -30,11 +31,13 @@
* to use this class directly.
*
* @author Rod Johnson
+ * @see AfterReturningAdviceInterceptor
+ * @see ThrowsAdviceInterceptor
*/
@SuppressWarnings("serial")
-public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
+public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
- private MethodBeforeAdvice advice;
+ private final MethodBeforeAdvice advice;
/**
@@ -46,9 +49,10 @@ public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
this.advice = advice;
}
+
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
- this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
+ this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java
index 5b2ec8f00e..e91a8cb857 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -50,6 +50,8 @@
*
* @author Rod Johnson
* @author Juergen Hoeller
+ * @see MethodBeforeAdviceInterceptor
+ * @see AfterReturningAdviceInterceptor
*/
public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
@@ -66,9 +68,8 @@ public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
/**
* Create a new ThrowsAdviceInterceptor for the given ThrowsAdvice.
- * @param throwsAdvice the advice object that defines the exception
- * handler methods (usually a {@link org.springframework.aop.ThrowsAdvice}
- * implementation)
+ * @param throwsAdvice the advice object that defines the exception handler methods
+ * (usually a {@link org.springframework.aop.ThrowsAdvice} implementation)
*/
public ThrowsAdviceInterceptor(Object throwsAdvice) {
Assert.notNull(throwsAdvice, "Advice must not be null");
@@ -76,14 +77,17 @@ public ThrowsAdviceInterceptor(Object throwsAdvice) {
Method[] methods = throwsAdvice.getClass().getMethods();
for (Method method : methods) {
- if (method.getName().equals(AFTER_THROWING) &&
- (method.getParameterTypes().length == 1 || method.getParameterTypes().length == 4) &&
- Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1])
- ) {
- // Have an exception handler
- this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method);
- if (logger.isDebugEnabled()) {
- logger.debug("Found exception handler method: " + method);
+ if (method.getName().equals(AFTER_THROWING)) {
+ Class>[] paramTypes = method.getParameterTypes();
+ if (paramTypes.length == 1 || paramTypes.length == 4) {
+ Class> throwableParam = paramTypes[paramTypes.length - 1];
+ if (Throwable.class.isAssignableFrom(throwableParam)) {
+ // An exception handler to register...
+ this.exceptionHandlerMap.put(throwableParam, method);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Found exception handler method on throws advice: " + method);
+ }
+ }
}
}
}
@@ -94,14 +98,33 @@ public ThrowsAdviceInterceptor(Object throwsAdvice) {
}
}
+
+ /**
+ * Return the number of handler methods in this advice.
+ */
public int getHandlerMethodCount() {
return this.exceptionHandlerMap.size();
}
+
+ @Override
+ public Object invoke(MethodInvocation mi) throws Throwable {
+ try {
+ return mi.proceed();
+ }
+ catch (Throwable ex) {
+ Method handlerMethod = getExceptionHandler(ex);
+ if (handlerMethod != null) {
+ invokeHandlerMethod(mi, ex, handlerMethod);
+ }
+ throw ex;
+ }
+ }
+
/**
- * Determine the exception handle method. Can return null if not found.
+ * Determine the exception handle method for the given exception.
* @param exception the exception thrown
- * @return a handler for the given exception type
+ * @return a handler for the given exception type, or {@code null} if none found
*/
private Method getExceptionHandler(Throwable exception) {
Class> exceptionClass = exception.getClass();
@@ -119,24 +142,10 @@ private Method getExceptionHandler(Throwable exception) {
return handler;
}
- @Override
- public Object invoke(MethodInvocation mi) throws Throwable {
- try {
- return mi.proceed();
- }
- catch (Throwable ex) {
- Method handlerMethod = getExceptionHandler(ex);
- if (handlerMethod != null) {
- invokeHandlerMethod(mi, ex, handlerMethod);
- }
- throw ex;
- }
- }
-
private void invokeHandlerMethod(MethodInvocation mi, Throwable ex, Method method) throws Throwable {
Object[] handlerArgs;
if (method.getParameterTypes().length == 1) {
- handlerArgs = new Object[] { ex };
+ handlerArgs = new Object[] {ex};
}
else {
handlerArgs = new Object[] {mi.getMethod(), mi.getArguments(), mi.getThis(), ex};
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java
index 92fc174bea..d7a8d1168b 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -54,7 +54,8 @@ public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyC
public void setBeanFactory(BeanFactory beanFactory) {
super.setBeanFactory(beanFactory);
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
- throw new IllegalStateException("Cannot use AdvisorAutoProxyCreator without a ConfigurableListableBeanFactory");
+ throw new IllegalArgumentException(
+ "AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
}
initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java
index 729343083b..904c765053 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -55,26 +55,25 @@
* before invoking the bean itself.
*
*
This class distinguishes between "common" interceptors: shared for all proxies it
- * creates, and "specific" interceptors: unique per bean instance. There need not
- * be any common interceptors. If there are, they are set using the interceptorNames
- * property. As with ProxyFactoryBean, interceptors names in the current factory
- * are used rather than bean references to allow correct handling of prototype
- * advisors and interceptors: for example, to support stateful mixins.
- * Any advice type is supported for "interceptorNames" entries.
+ * creates, and "specific" interceptors: unique per bean instance. There need not be any
+ * common interceptors. If there are, they are set using the interceptorNames property.
+ * As with {@link org.springframework.aop.framework.ProxyFactoryBean}, interceptors names
+ * in the current factory are used rather than bean references to allow correct handling
+ * of prototype advisors and interceptors: for example, to support stateful mixins.
+ * Any advice type is supported for {@link #setInterceptorNames "interceptorNames"} entries.
*
*
Such auto-proxying is particularly useful if there's a large number of beans that
* need to be wrapped with similar proxies, i.e. delegating to the same interceptors.
* Instead of x repetitive proxy definitions for x target beans, you can register
* one single such post processor with the bean factory to achieve the same effect.
*
- *
Subclasses can apply any strategy to decide if a bean is to be proxied,
- * e.g. by type, by name, by definition details, etc. They can also return
- * additional interceptors that should just be applied to the specific bean
- * instance. The default concrete implementation is BeanNameAutoProxyCreator,
- * identifying the beans to be proxied via a list of bean names.
+ *
Subclasses can apply any strategy to decide if a bean is to be proxied, e.g. by type,
+ * by name, by definition details, etc. They can also return additional interceptors that
+ * should just be applied to the specific bean instance. A simple concrete implementation is
+ * {@link BeanNameAutoProxyCreator}, identifying the beans to be proxied via given names.
*
*
Any number of {@link TargetSourceCreator} implementations can be used to create
- * a custom target source - for example, to pool prototype objects. Auto-proxying will
+ * a custom target source: for example, to pool prototype objects. Auto-proxying will
* occur even if there is no advice, as long as a TargetSourceCreator specifies a custom
* {@link org.springframework.aop.TargetSource}. If there are no TargetSourceCreators set,
* or if none matches, a {@link org.springframework.aop.target.SingletonTargetSource}
@@ -156,8 +155,8 @@ public boolean isFrozen() {
}
/**
- * Specify the AdvisorAdapterRegistry to use.
- * Default is the global AdvisorAdapterRegistry.
+ * Specify the {@link AdvisorAdapterRegistry} to use.
+ *
Default is the global {@link AdvisorAdapterRegistry}.
* @see org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry
*/
public void setAdvisorAdapterRegistry(AdvisorAdapterRegistry advisorAdapterRegistry) {
@@ -165,18 +164,18 @@ public void setAdvisorAdapterRegistry(AdvisorAdapterRegistry advisorAdapterRegis
}
/**
- * Set custom TargetSourceCreators to be applied in this order.
- * If the list is empty, or they all return null, a SingletonTargetSource
+ * Set custom {@code TargetSourceCreators} to be applied in this order.
+ * If the list is empty, or they all return null, a {@link SingletonTargetSource}
* will be created for each bean.
*
Note that TargetSourceCreators will kick in even for target beans
- * where no advices or advisors have been found. If a TargetSourceCreator
- * returns a TargetSource for a specific bean, that bean will be proxied
+ * where no advices or advisors have been found. If a {@code TargetSourceCreator}
+ * returns a {@link TargetSource} for a specific bean, that bean will be proxied
* in any case.
- *
TargetSourceCreators can only be invoked if this post processor is used
- * in a BeanFactory, and its BeanFactoryAware callback is used.
- * @param targetSourceCreators list of TargetSourceCreator.
- * Ordering is significant: The TargetSource returned from the first matching
- * TargetSourceCreator (that is, the first that returns non-null) will be used.
+ *
{@code TargetSourceCreators} can only be invoked if this post processor is used
+ * in a {@link BeanFactory} and its {@link BeanFactoryAware} callback is triggered.
+ * @param targetSourceCreators the list of {@code TargetSourceCreators}.
+ * Ordering is significant: The {@code TargetSource} returned from the first matching
+ * {@code TargetSourceCreator} (that is, the first that returns non-null) will be used.
*/
public void setCustomTargetSourceCreators(TargetSourceCreator... targetSourceCreators) {
this.customTargetSourceCreators = targetSourceCreators;
@@ -207,8 +206,8 @@ public void setBeanFactory(BeanFactory beanFactory) {
}
/**
- * Return the owning BeanFactory.
- * May be {@code null}, as this object doesn't need to belong to a bean factory.
+ * Return the owning {@link BeanFactory}.
+ * May be {@code null}, as this post-processor doesn't need to belong to a bean factory.
*/
protected BeanFactory getBeanFactory() {
return this.beanFactory;
@@ -413,7 +412,7 @@ protected TargetSource getCustomTargetSource(Class> beanClass, String beanName
// Found a matching TargetSource.
if (logger.isDebugEnabled()) {
logger.debug("TargetSourceCreator [" + tsc +
- " found custom TargetSource for bean with name '" + beanName + "'");
+ "] found custom TargetSource for bean with name '" + beanName + "'");
}
return ts;
}
@@ -455,10 +454,7 @@ protected Object createProxy(
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
- for (Advisor advisor : advisors) {
- proxyFactory.addAdvisor(advisor);
- }
-
+ proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
@@ -557,7 +553,7 @@ private Advisor[] resolveInterceptorNames() {
* Subclasses may choose to implement this: for example,
* to change the interfaces exposed.
*
The default implementation is empty.
- * @param proxyFactory ProxyFactory that is already configured with
+ * @param proxyFactory a ProxyFactory that is already configured with
* TargetSource and interfaces and will be used to create the proxy
* immediately after this method returns
*/
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java
index c484520b89..59a2170efd 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanFactoryAdvisorRetrievalHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -16,7 +16,7 @@
package org.springframework.aop.framework.autoproxy;
-import java.util.LinkedList;
+import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
@@ -43,7 +43,7 @@ public class BeanFactoryAdvisorRetrievalHelper {
private final ConfigurableListableBeanFactory beanFactory;
- private String[] cachedAdvisorBeanNames;
+ private volatile String[] cachedAdvisorBeanNames;
/**
@@ -64,22 +64,19 @@ public BeanFactoryAdvisorRetrievalHelper(ConfigurableListableBeanFactory beanFac
*/
public List findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
- String[] advisorNames = null;
- synchronized (this) {
- advisorNames = this.cachedAdvisorBeanNames;
- if (advisorNames == null) {
- // Do not initialize FactoryBeans here: We need to leave all regular beans
- // uninitialized to let the auto-proxy creator apply to them!
- advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
- this.beanFactory, Advisor.class, true, false);
- this.cachedAdvisorBeanNames = advisorNames;
- }
+ String[] advisorNames = this.cachedAdvisorBeanNames;
+ if (advisorNames == null) {
+ // Do not initialize FactoryBeans here: We need to leave all regular beans
+ // uninitialized to let the auto-proxy creator apply to them!
+ advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
+ this.beanFactory, Advisor.class, true, false);
+ this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
- return new LinkedList();
+ return new ArrayList();
}
- List advisors = new LinkedList();
+ List advisors = new ArrayList();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java
index 044c7bbc11..dccf975db9 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/DefaultAdvisorAutoProxyCreator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -19,15 +19,16 @@
import org.springframework.beans.factory.BeanNameAware;
/**
- * BeanPostProcessor implementation that creates AOP proxies based on all candidate
- * Advisors in the current BeanFactory. This class is completely generic; it contains
- * no special code to handle any particular aspects, such as pooling aspects.
+ * {@code BeanPostProcessor} implementation that creates AOP proxies based on all
+ * candidate {@code Advisor}s in the current {@code BeanFactory}. This class is
+ * completely generic; it contains no special code to handle any particular aspects,
+ * such as pooling aspects.
*
* It's possible to filter out advisors - for example, to use multiple post processors
- * of this type in the same factory - by setting the {@code usePrefix} property
- * to true, in which case only advisors beginning with the DefaultAdvisorAutoProxyCreator's
- * bean name followed by a dot (like "aapc.") will be used. This default prefix can be
- * changed from the bean name by setting the {@code advisorBeanNamePrefix} property.
+ * of this type in the same factory - by setting the {@code usePrefix} property to true,
+ * in which case only advisors beginning with the DefaultAdvisorAutoProxyCreator's bean
+ * name followed by a dot (like "aapc.") will be used. This default prefix can be changed
+ * from the bean name by setting the {@code advisorBeanNamePrefix} property.
* The separator (.) will also be used in this case.
*
* @author Rod Johnson
@@ -40,22 +41,22 @@ public class DefaultAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCrea
public final static String SEPARATOR = ".";
- private boolean usePrefix;
+ private boolean usePrefix = false;
private String advisorBeanNamePrefix;
/**
- * Set whether to exclude advisors with a certain prefix
- * in the bean name.
+ * Set whether to only include advisors with a certain prefix in the bean name.
+ *
Default is {@code false}, including all beans of type {@code Advisor}.
+ * @see #setAdvisorBeanNamePrefix
*/
public void setUsePrefix(boolean usePrefix) {
this.usePrefix = usePrefix;
}
/**
- * Return whether to exclude advisors with a certain prefix
- * in the bean name.
+ * Return whether to only include advisors with a certain prefix in the bean name.
*/
public boolean isUsePrefix() {
return this.usePrefix;
@@ -89,7 +90,7 @@ public void setBeanName(String name) {
/**
- * Consider Advisor beans with the specified prefix as eligible, if activated.
+ * Consider {@code Advisor} beans with the specified prefix as eligible, if activated.
* @see #setUsePrefix
* @see #setAdvisorBeanNamePrefix
*/
diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/AbstractBeanFactoryBasedTargetSourceCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/AbstractBeanFactoryBasedTargetSourceCreator.java
index 3735ba06e7..083b7aa9ae 100644
--- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/AbstractBeanFactoryBasedTargetSourceCreator.java
+++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/target/AbstractBeanFactoryBasedTargetSourceCreator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -150,7 +150,7 @@ protected DefaultListableBeanFactory buildInternalBeanFactory(ConfigurableBeanFa
// since those are only meant to apply to beans defined in the original factory.
for (Iterator it = internalBeanFactory.getBeanPostProcessors().iterator(); it.hasNext();) {
if (it.next() instanceof AopInfrastructureBean) {
- it.remove();
+ it.remove(); // effectively deprecated: use List.removeIf on Java 8+
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractMonitoringInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractMonitoringInterceptor.java
index 1bd6a6d5e1..a742be2cb6 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractMonitoringInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractMonitoringInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -22,12 +22,12 @@
/**
* Base class for monitoring interceptors, such as performance monitors.
- * Provides {@code prefix} and {@code suffix} properties
- * that help to classify/group performance monitoring results.
+ * Provides configurable "prefix and "suffix" properties that help to
+ * classify/group performance monitoring results.
*
- * Subclasses should call the {@code createInvocationTraceName(MethodInvocation)}
- * method to create a name for the given trace that includes information about the
- * method invocation under trace along with the prefix and suffix added as appropriate.
+ *
In their {@link #invokeUnderTrace} implementation, subclasses should call the
+ * {@link #createInvocationTraceName} method to create a name for the given trace,
+ * including information about the method invocation along with a prefix/suffix.
*
* @author Rob Harrop
* @author Juergen Hoeller
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractTraceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractTraceInterceptor.java
index 00ae0a2d81..7450df1438 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractTraceInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AbstractTraceInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -58,6 +58,12 @@ public abstract class AbstractTraceInterceptor implements MethodInterceptor, Ser
*/
private boolean hideProxyClassNames = false;
+ /**
+ * Indicates whether to pass an exception to the logger.
+ * @see #writeToLog(Log, String, Throwable)
+ */
+ private boolean logExceptionStackTrace = true;
+
/**
* Set whether to use a dynamic logger or a static logger.
@@ -98,6 +104,17 @@ public void setHideProxyClassNames(boolean hideProxyClassNames) {
this.hideProxyClassNames = hideProxyClassNames;
}
+ /**
+ * Set whether to pass an exception to the logger, suggesting inclusion
+ * of its stack trace into the log. Default is "true"; set this to "false"
+ * in order to reduce the log output to just the trace message (which may
+ * include the exception class name and exception message, if applicable).
+ * @since 4.3.10
+ */
+ public void setLogExceptionStackTrace(boolean logExceptionStackTrace) {
+ this.logExceptionStackTrace = logExceptionStackTrace;
+ }
+
/**
* Determines whether or not logging is enabled for the particular {@code MethodInvocation}.
@@ -171,6 +188,40 @@ protected boolean isLogEnabled(Log logger) {
return logger.isTraceEnabled();
}
+ /**
+ * Write the supplied trace message to the supplied {@code Log} instance.
+ *
To be called by {@link #invokeUnderTrace} for enter/exit messages.
+ *
Delegates to {@link #writeToLog(Log, String, Throwable)} as the
+ * ultimate delegate that controls the underlying logger invocation.
+ * @since 4.3.10
+ * @see #writeToLog(Log, String, Throwable)
+ */
+ protected void writeToLog(Log logger, String message) {
+ writeToLog(logger, message, null);
+ }
+
+ /**
+ * Write the supplied trace message and {@link Throwable} to the
+ * supplied {@code Log} instance.
+ *
To be called by {@link #invokeUnderTrace} for enter/exit outcomes,
+ * potentially including an exception. Note that an exception's stack trace
+ * won't get logged when {@link #setLogExceptionStackTrace} is "false".
+ *
By default messages are written at {@code TRACE} level. Subclasses
+ * can override this method to control which level the message is written
+ * at, typically also overriding {@link #isLogEnabled} accordingly.
+ * @since 4.3.10
+ * @see #setLogExceptionStackTrace
+ * @see #isLogEnabled
+ */
+ protected void writeToLog(Log logger, String message, Throwable ex) {
+ if (ex != null && this.logExceptionStackTrace) {
+ logger.trace(message, ex);
+ }
+ else {
+ logger.trace(message);
+ }
+ }
+
/**
* Subclasses must override this method to perform any tracing around the
@@ -180,13 +231,15 @@ protected boolean isLogEnabled(Log logger) {
*
By default, the passed-in {@code Log} instance will have log level
* "trace" enabled. Subclasses do not have to check for this again, unless
* they overwrite the {@code isInterceptorEnabled} method to modify
- * the default behavior.
+ * the default behavior, and may delegate to {@code writeToLog} for actual
+ * messages to be written.
* @param logger the {@code Log} to write trace messages to
* @return the result of the call to {@code MethodInvocation.proceed()}
* @throws Throwable if the call to {@code MethodInvocation.proceed()}
* encountered any errors
- * @see #isInterceptorEnabled
* @see #isLogEnabled
+ * @see #writeToLog(Log, String)
+ * @see #writeToLog(Log, String, Throwable)
*/
protected abstract Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable;
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java
index 28917436f0..7c25f69632 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -228,6 +228,7 @@ protected Executor getDefaultExecutor(BeanFactory beanFactory) {
return beanFactory.getBean(TaskExecutor.class);
}
catch (NoUniqueBeanDefinitionException ex) {
+ logger.debug("Could not find unique TaskExecutor bean", ex);
try {
return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
}
@@ -241,8 +242,14 @@ protected Executor getDefaultExecutor(BeanFactory beanFactory) {
}
catch (NoSuchBeanDefinitionException ex) {
logger.debug("Could not find default TaskExecutor bean", ex);
+ try {
+ return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
+ }
+ catch (NoSuchBeanDefinitionException ex2) {
+ logger.info("No task executor bean found for async processing: " +
+ "no bean of type TaskExecutor and no bean named 'taskExecutor' either");
+ }
// Giving up -> either using local default executor or none at all...
- logger.info("No TaskExecutor bean found for async processing");
}
}
return null;
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java
index f8b19cb71f..7598f7ad91 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/CustomizableTraceInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -128,20 +128,20 @@ public class CustomizableTraceInterceptor extends AbstractTraceInterceptor {
/**
* The default message used for writing method entry messages.
*/
- private static final String DEFAULT_ENTER_MESSAGE =
- "Entering method '" + PLACEHOLDER_METHOD_NAME + "' of class [" + PLACEHOLDER_TARGET_CLASS_NAME + "]";
+ private static final String DEFAULT_ENTER_MESSAGE = "Entering method '" +
+ PLACEHOLDER_METHOD_NAME + "' of class [" + PLACEHOLDER_TARGET_CLASS_NAME + "]";
/**
* The default message used for writing method exit messages.
*/
- private static final String DEFAULT_EXIT_MESSAGE =
- "Exiting method '" + PLACEHOLDER_METHOD_NAME + "' of class [" + PLACEHOLDER_TARGET_CLASS_NAME + "]";
+ private static final String DEFAULT_EXIT_MESSAGE = "Exiting method '" +
+ PLACEHOLDER_METHOD_NAME + "' of class [" + PLACEHOLDER_TARGET_CLASS_NAME + "]";
/**
* The default message used for writing exception messages.
*/
- private static final String DEFAULT_EXCEPTION_MESSAGE =
- "Exception thrown in method '" + PLACEHOLDER_METHOD_NAME + "' of class [" + PLACEHOLDER_TARGET_CLASS_NAME + "]";
+ private static final String DEFAULT_EXCEPTION_MESSAGE = "Exception thrown in method '" +
+ PLACEHOLDER_METHOD_NAME + "' of class [" + PLACEHOLDER_TARGET_CLASS_NAME + "]";
/**
* The {@code Pattern} used to match placeholders.
@@ -182,14 +182,14 @@ public class CustomizableTraceInterceptor extends AbstractTraceInterceptor {
*
*/
public void setEnterMessage(String enterMessage) throws IllegalArgumentException {
- Assert.hasText(enterMessage, "'enterMessage' must not be empty");
+ Assert.hasText(enterMessage, "enterMessage must not be empty");
checkForInvalidPlaceholders(enterMessage);
Assert.doesNotContain(enterMessage, PLACEHOLDER_RETURN_VALUE,
- "enterMessage cannot contain placeholder [" + PLACEHOLDER_RETURN_VALUE + "]");
+ "enterMessage cannot contain placeholder " + PLACEHOLDER_RETURN_VALUE);
Assert.doesNotContain(enterMessage, PLACEHOLDER_EXCEPTION,
- "enterMessage cannot contain placeholder [" + PLACEHOLDER_EXCEPTION + "]");
+ "enterMessage cannot contain placeholder " + PLACEHOLDER_EXCEPTION);
Assert.doesNotContain(enterMessage, PLACEHOLDER_INVOCATION_TIME,
- "enterMessage cannot contain placeholder [" + PLACEHOLDER_INVOCATION_TIME + "]");
+ "enterMessage cannot contain placeholder " + PLACEHOLDER_INVOCATION_TIME);
this.enterMessage = enterMessage;
}
@@ -206,10 +206,10 @@ public void setEnterMessage(String enterMessage) throws IllegalArgumentException
*
*/
public void setExitMessage(String exitMessage) {
- Assert.hasText(exitMessage, "'exitMessage' must not be empty");
+ Assert.hasText(exitMessage, "exitMessage must not be empty");
checkForInvalidPlaceholders(exitMessage);
Assert.doesNotContain(exitMessage, PLACEHOLDER_EXCEPTION,
- "exitMessage cannot contain placeholder [" + PLACEHOLDER_EXCEPTION + "]");
+ "exitMessage cannot contain placeholder" + PLACEHOLDER_EXCEPTION);
this.exitMessage = exitMessage;
}
@@ -225,12 +225,10 @@ public void setExitMessage(String exitMessage) {
*
*/
public void setExceptionMessage(String exceptionMessage) {
- Assert.hasText(exceptionMessage, "'exceptionMessage' must not be empty");
+ Assert.hasText(exceptionMessage, "exceptionMessage must not be empty");
checkForInvalidPlaceholders(exceptionMessage);
Assert.doesNotContain(exceptionMessage, PLACEHOLDER_RETURN_VALUE,
- "exceptionMessage cannot contain placeholder [" + PLACEHOLDER_RETURN_VALUE + "]");
- Assert.doesNotContain(exceptionMessage, PLACEHOLDER_INVOCATION_TIME,
- "exceptionMessage cannot contain placeholder [" + PLACEHOLDER_INVOCATION_TIME + "]");
+ "exceptionMessage cannot contain placeholder " + PLACEHOLDER_RETURN_VALUE);
this.exceptionMessage = exceptionMessage;
}
@@ -246,7 +244,7 @@ public void setExceptionMessage(String exceptionMessage) {
*/
@Override
protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable {
- String name = invocation.getMethod().getDeclaringClass().getName() + "." + invocation.getMethod().getName();
+ String name = ClassUtils.getQualifiedMethodName(invocation.getMethod());
StopWatch stopWatch = new StopWatch(name);
Object returnValue = null;
boolean exitThroughException = false;
@@ -262,8 +260,8 @@ protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throw
stopWatch.stop();
}
exitThroughException = true;
- writeToLog(logger,
- replacePlaceholders(this.exceptionMessage, invocation, null, ex, stopWatch.getTotalTimeMillis()), ex);
+ writeToLog(logger, replacePlaceholders(
+ this.exceptionMessage, invocation, null, ex, stopWatch.getTotalTimeMillis()), ex);
throw ex;
}
finally {
@@ -271,35 +269,12 @@ protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throw
if (stopWatch.isRunning()) {
stopWatch.stop();
}
- writeToLog(logger,
- replacePlaceholders(this.exitMessage, invocation, returnValue, null, stopWatch.getTotalTimeMillis()));
+ writeToLog(logger, replacePlaceholders(
+ this.exitMessage, invocation, returnValue, null, stopWatch.getTotalTimeMillis()));
}
}
}
- /**
- * Writes the supplied message to the supplied {@code Log} instance.
- * @see #writeToLog(org.apache.commons.logging.Log, String, Throwable)
- */
- protected void writeToLog(Log logger, String message) {
- writeToLog(logger, message, null);
- }
-
- /**
- * Writes the supplied message and {@link Throwable} to the
- * supplied {@code Log} instance. By default messages are written
- * at {@code TRACE} level. Sub-classes can override this method
- * to control which level the message is written at.
- */
- protected void writeToLog(Log logger, String message, Throwable ex) {
- if (ex != null) {
- logger.trace(message, ex);
- }
- else {
- logger.trace(message);
- }
- }
-
/**
* Replace the placeholders in the given message with the supplied values,
* or values derived from those supplied.
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/JamonPerformanceMonitorInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/JamonPerformanceMonitorInterceptor.java
index a1955bc291..2b71b3cbbd 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/JamonPerformanceMonitorInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/JamonPerformanceMonitorInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -121,7 +121,7 @@ protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throw
finally {
monitor.stop();
if (!this.trackAllInvocations || isLogEnabled(logger)) {
- logger.trace("JAMon performance statistics for method [" + name + "]:\n" + monitor);
+ writeToLog(logger, "JAMon performance statistics for method [" + name + "]:\n" + monitor);
}
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/PerformanceMonitorInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/PerformanceMonitorInterceptor.java
index 4362c5de10..dd72c00f35 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/PerformanceMonitorInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/PerformanceMonitorInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -63,7 +63,7 @@ protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throw
}
finally {
stopWatch.stop();
- logger.trace(stopWatch.shortSummary());
+ writeToLog(logger, stopWatch.shortSummary());
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java
index dffba79e80..e77f84b435 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleAsyncUncaughtExceptionHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -29,13 +29,13 @@
*/
public class SimpleAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
- private final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class);
+ private static final Log logger = LogFactory.getLog(SimpleAsyncUncaughtExceptionHandler.class);
+
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
if (logger.isErrorEnabled()) {
- logger.error(String.format("Unexpected error occurred invoking async " +
- "method '%s'.", method), ex);
+ logger.error("Unexpected error occurred invoking async method: " + method, ex);
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleTraceInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleTraceInterceptor.java
index e70f502fd1..8ca8baa8ae 100644
--- a/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleTraceInterceptor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/SimpleTraceInterceptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -55,14 +55,14 @@ public SimpleTraceInterceptor(boolean useDynamicLogger) {
@Override
protected Object invokeUnderTrace(MethodInvocation invocation, Log logger) throws Throwable {
String invocationDescription = getInvocationDescription(invocation);
- logger.trace("Entering " + invocationDescription);
+ writeToLog(logger, "Entering " + invocationDescription);
try {
Object rval = invocation.proceed();
- logger.trace("Exiting " + invocationDescription);
+ writeToLog(logger, "Exiting " + invocationDescription);
return rval;
}
catch (Throwable ex) {
- logger.trace("Exception thrown in " + invocationDescription, ex);
+ writeToLog(logger, "Exception thrown in " + invocationDescription, ex);
throw ex;
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyFactoryBean.java b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyFactoryBean.java
index c93d3dfeb3..6c983d2a66 100644
--- a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyFactoryBean.java
+++ b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -50,7 +50,8 @@
* @see #setProxyTargetClass
*/
@SuppressWarnings("serial")
-public class ScopedProxyFactoryBean extends ProxyConfig implements FactoryBean, BeanFactoryAware {
+public class ScopedProxyFactoryBean extends ProxyConfig
+ implements FactoryBean, BeanFactoryAware, AopInfrastructureBean {
/** The TargetSource that manages scoping */
private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();
diff --git a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java
index f6213a7d3c..9db73d062d 100644
--- a/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java
+++ b/spring-aop/src/main/java/org/springframework/aop/scope/ScopedProxyUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -89,7 +89,7 @@ public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder defini
}
/**
- * Generates the bean name that is used within the scoped proxy to reference the target bean.
+ * Generate the bean name that is used within the scoped proxy to reference the target bean.
* @param originalBeanName the original name of bean
* @return the generated bean to be used to reference the target bean
*/
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java
index 2c2eff5feb..5401bb96a7 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java
@@ -46,7 +46,7 @@ public abstract class AbstractBeanFactoryPointcutAdvisor extends AbstractPointcu
private BeanFactory beanFactory;
- private transient Advice advice;
+ private transient volatile Advice advice;
private transient volatile Object adviceMonitor = new Object();
@@ -98,12 +98,28 @@ public void setAdvice(Advice advice) {
@Override
public Advice getAdvice() {
- synchronized (this.adviceMonitor) {
- if (this.advice == null && this.adviceBeanName != null) {
- Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");
- this.advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class);
+ Advice advice = this.advice;
+ if (advice != null || this.adviceBeanName == null) {
+ return advice;
+ }
+
+ Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");
+ if (this.beanFactory.isSingleton(this.adviceBeanName)) {
+ // Rely on singleton semantics provided by the factory.
+ advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class);
+ this.advice = advice;
+ return advice;
+ }
+ else {
+ // No singleton guarantees from the factory -> let's lock locally but
+ // reuse the factory's singleton lock, just in case a lazy dependency
+ // of our advice bean happens to trigger the singleton lock implicitly...
+ synchronized (this.adviceMonitor) {
+ if (this.advice == null) {
+ this.advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class);
+ }
+ return this.advice;
}
- return this.advice;
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java
index e610410153..aa666cba09 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/AbstractRegexpMethodPointcut.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -21,6 +21,7 @@
import java.util.Arrays;
import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -129,8 +130,9 @@ public String[] getExcludedPatterns() {
*/
@Override
public boolean matches(Method method, Class> targetClass) {
- return ((targetClass != null && matchesPattern(targetClass.getName() + "." + method.getName())) ||
- matchesPattern(method.getDeclaringClass().getName() + "." + method.getName()));
+ return ((targetClass != null && targetClass != method.getDeclaringClass() &&
+ matchesPattern(ClassUtils.getQualifiedMethodName(method, targetClass))) ||
+ matchesPattern(ClassUtils.getQualifiedMethodName(method, method.getDeclaringClass())));
}
/**
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/ComposablePointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/ComposablePointcut.java
index fd78bfd604..50a67d2ae2 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/ComposablePointcut.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/ComposablePointcut.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -22,7 +22,6 @@
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
/**
* Convenient class for building up pointcuts. All methods return
@@ -188,21 +187,14 @@ public boolean equals(Object other) {
if (!(other instanceof ComposablePointcut)) {
return false;
}
- ComposablePointcut that = (ComposablePointcut) other;
- return ObjectUtils.nullSafeEquals(that.classFilter, this.classFilter) &&
- ObjectUtils.nullSafeEquals(that.methodMatcher, this.methodMatcher);
+ ComposablePointcut otherPointcut = (ComposablePointcut) other;
+ return (this.classFilter.equals(otherPointcut.classFilter) &&
+ this.methodMatcher.equals(otherPointcut.methodMatcher));
}
@Override
public int hashCode() {
- int code = 17;
- if (this.classFilter != null) {
- code = 37 * code + this.classFilter.hashCode();
- }
- if (this.methodMatcher != null) {
- code = 37 * code + this.methodMatcher.hashCode();
- }
- return code;
+ return this.classFilter.hashCode() * 37 + this.methodMatcher.hashCode();
}
@Override
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java
index 1989a6dae4..47d201cfcd 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -22,8 +22,6 @@
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
-import org.springframework.core.ControlFlow;
-import org.springframework.core.ControlFlowFactory;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -34,7 +32,7 @@
*
* @author Rod Johnson
* @author Rob Harrop
- * @see org.springframework.core.ControlFlow
+ * @author Juergen Hoeller
*/
@SuppressWarnings("serial")
public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
@@ -43,7 +41,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
private String methodName;
- private int evaluations;
+ private volatile int evaluations;
/**
@@ -55,11 +53,11 @@ public ControlFlowPointcut(Class> clazz) {
}
/**
- * Construct a new pointcut that matches all calls below the
- * given method in the given class. If the method name is null,
- * matches all control flows below that class.
+ * Construct a new pointcut that matches all calls below the given method
+ * in the given class. If no method name is given, matches all control flows
+ * below the given class.
* @param clazz the clazz
- * @param methodName the name of the method
+ * @param methodName the name of the method (may be {@code null})
*/
public ControlFlowPointcut(Class> clazz, String methodName) {
Assert.notNull(clazz, "Class must not be null");
@@ -93,8 +91,14 @@ public boolean isRuntime() {
@Override
public boolean matches(Method method, Class> targetClass, Object... args) {
this.evaluations++;
- ControlFlow cflow = ControlFlowFactory.createControlFlow();
- return (this.methodName != null ? cflow.under(this.clazz, this.methodName) : cflow.under(this.clazz));
+
+ for (StackTraceElement element : new Throwable().getStackTrace()) {
+ if (element.getClassName().equals(this.clazz.getName()) &&
+ (this.methodName == null || element.getMethodName().equals(this.methodName))) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -130,8 +134,7 @@ public boolean equals(Object other) {
@Override
public int hashCode() {
- int code = 17;
- code = 37 * code + this.clazz.hashCode();
+ int code = this.clazz.hashCode();
if (this.methodName != null) {
code = 37 * code + this.methodName.hashCode();
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java
index 32af73dc7b..17b167010f 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -45,7 +45,7 @@ public class DefaultIntroductionAdvisor implements IntroductionAdvisor, ClassFil
private final Set> interfaces = new LinkedHashSet>();
- private int order = Integer.MAX_VALUE;
+ private int order = Ordered.LOWEST_PRECEDENCE;
/**
@@ -104,7 +104,7 @@ public void addInterface(Class> intf) {
@Override
public Class>[] getInterfaces() {
- return this.interfaces.toArray(new Class>[this.interfaces.size()]);
+ return ClassUtils.toClassArray(this.interfaces);
}
@Override
@@ -118,7 +118,6 @@ public void validateInterfaces() throws IllegalArgumentException {
}
}
-
public void setOrder(int order) {
this.order = order;
}
@@ -128,7 +127,6 @@ public int getOrder() {
return this.order;
}
-
@Override
public Advice getAdvice() {
return this.advice;
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java
index 78b230d8f0..1f7dc97484 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -23,6 +23,8 @@
/**
* Convenient abstract superclass for dynamic method matchers,
* which do care about arguments at runtime.
+ *
+ * @author Rod Johnson
*/
public abstract class DynamicMethodMatcher implements MethodMatcher {
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcherPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcherPointcut.java
index df3963dc85..56e29cb0bd 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcherPointcut.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/DynamicMethodMatcherPointcut.java
@@ -24,7 +24,7 @@
* Convenient superclass when we want to force subclasses to
* implement MethodMatcher interface, but subclasses
* will want to be pointcuts. The getClassFilter() method can
- * be overriden to customize ClassFilter behaviour as well.
+ * be overridden to customize ClassFilter behaviour as well.
*
* @author Rod Johnson
*/
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/IntroductionInfoSupport.java b/spring-aop/src/main/java/org/springframework/aop/support/IntroductionInfoSupport.java
index b91c897502..b0153f91bb 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/IntroductionInfoSupport.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/IntroductionInfoSupport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -53,15 +53,15 @@ public class IntroductionInfoSupport implements IntroductionInfo, Serializable {
* due to the delegate implementing it. Call this method to exclude
* internal interfaces from being visible at the proxy level.
* Does nothing if the interface is not implemented by the delegate.
- * @param intf the interface to suppress
+ * @param ifc the interface to suppress
*/
- public void suppressInterface(Class> intf) {
- this.publishedInterfaces.remove(intf);
+ public void suppressInterface(Class> ifc) {
+ this.publishedInterfaces.remove(ifc);
}
@Override
public Class>[] getInterfaces() {
- return this.publishedInterfaces.toArray(new Class>[this.publishedInterfaces.size()]);
+ return ClassUtils.toClassArray(this.publishedInterfaces);
}
/**
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcut.java
index 7ef41f0a3f..5a59d87f4f 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcut.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/NameMatchMethodPointcut.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -27,7 +27,7 @@
/**
* Pointcut bean for simple method name matches, as alternative to regexp patterns.
- * Does not handle overloaded methods: all methods *with a given name will be eligible.
+ * Does not handle overloaded methods: all methods with a given name will be eligible.
*
* @author Juergen Hoeller
* @author Rod Johnson
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java b/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java
index b17977ba48..8a4f15b075 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java
@@ -94,7 +94,7 @@ public static boolean matches(Pointcut pointcut, Method method, Class> targetC
@SuppressWarnings("serial")
private static class SetterPointcut extends StaticMethodMatcherPointcut implements Serializable {
- public static SetterPointcut INSTANCE = new SetterPointcut();
+ public static final SetterPointcut INSTANCE = new SetterPointcut();
@Override
public boolean matches(Method method, Class> targetClass) {
@@ -115,7 +115,7 @@ private Object readResolve() {
@SuppressWarnings("serial")
private static class GetterPointcut extends StaticMethodMatcherPointcut implements Serializable {
- public static GetterPointcut INSTANCE = new GetterPointcut();
+ public static final GetterPointcut INSTANCE = new GetterPointcut();
@Override
public boolean matches(Method method, Class> targetClass) {
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java b/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java
index 364ff6282e..6b8073d69a 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/RootClassFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -21,7 +21,8 @@
import org.springframework.aop.ClassFilter;
/**
- * Simple ClassFilter implementation that passes classes (and optionally subclasses)
+ * Simple ClassFilter implementation that passes classes (and optionally subclasses).
+ *
* @author Rod Johnson
*/
@SuppressWarnings("serial")
@@ -37,7 +38,7 @@ public RootClassFilter(Class> clazz) {
@Override
public boolean matches(Class> candidate) {
- return clazz.isAssignableFrom(candidate);
+ return this.clazz.isAssignableFrom(candidate);
}
}
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java
index 0627248ee9..923daaf94d 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -23,6 +23,8 @@
/**
* Convenient abstract superclass for static method matchers, which don't care
* about arguments at runtime.
+ *
+ * @author Rod Johnson
*/
public abstract class StaticMethodMatcher implements MethodMatcher {
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcherPointcutAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcherPointcutAdvisor.java
index 9dbe10dbf4..3ba109a1ac 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcherPointcutAdvisor.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcherPointcutAdvisor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -36,10 +36,10 @@
public abstract class StaticMethodMatcherPointcutAdvisor extends StaticMethodMatcherPointcut
implements PointcutAdvisor, Ordered, Serializable {
- private int order = Integer.MAX_VALUE;
-
private Advice advice;
+ private int order = Integer.MAX_VALUE;
+
/**
* Create a new StaticMethodMatcherPointcutAdvisor,
diff --git a/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMatchingPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMatchingPointcut.java
index 59619830d9..ef2790d6f7 100644
--- a/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMatchingPointcut.java
+++ b/spring-aop/src/main/java/org/springframework/aop/support/annotation/AnnotationMatchingPointcut.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -22,7 +22,6 @@
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
/**
* Simple Pointcut that looks for a specific Java 5 annotation
@@ -46,16 +45,15 @@ public class AnnotationMatchingPointcut implements Pointcut {
* @param classAnnotationType the annotation type to look for at the class level
*/
public AnnotationMatchingPointcut(Class extends Annotation> classAnnotationType) {
- this.classFilter = new AnnotationClassFilter(classAnnotationType);
- this.methodMatcher = MethodMatcher.TRUE;
+ this(classAnnotationType, false);
}
/**
* Create a new AnnotationMatchingPointcut for the given annotation type.
* @param classAnnotationType the annotation type to look for at the class level
- * @param checkInherited whether to explicitly check the superclasses and
- * interfaces for the annotation type as well (even if the annotation type
- * is not marked as inherited itself)
+ * @param checkInherited whether to also check the superclasses and interfaces
+ * as well as meta-annotations for the annotation type
+ * @see AnnotationClassFilter#AnnotationClassFilter(Class, boolean)
*/
public AnnotationMatchingPointcut(Class extends Annotation> classAnnotationType, boolean checkInherited) {
this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited);
@@ -109,21 +107,14 @@ public boolean equals(Object other) {
if (!(other instanceof AnnotationMatchingPointcut)) {
return false;
}
- AnnotationMatchingPointcut that = (AnnotationMatchingPointcut) other;
- return ObjectUtils.nullSafeEquals(that.classFilter, this.classFilter) &&
- ObjectUtils.nullSafeEquals(that.methodMatcher, this.methodMatcher);
+ AnnotationMatchingPointcut otherPointcut = (AnnotationMatchingPointcut) other;
+ return (this.classFilter.equals(otherPointcut.classFilter) &&
+ this.methodMatcher.equals(otherPointcut.methodMatcher));
}
@Override
public int hashCode() {
- int code = 17;
- if (this.classFilter != null) {
- code = 37 * code + this.classFilter.hashCode();
- }
- if (this.methodMatcher != null) {
- code = 37 * code + this.methodMatcher.hashCode();
- }
- return code;
+ return this.classFilter.hashCode() * 37 + this.methodMatcher.hashCode();
}
@Override
diff --git a/spring-aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java
index 3d0e9ae4f5..b070b628ea 100644
--- a/spring-aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java
+++ b/spring-aop/src/main/java/org/springframework/aop/target/AbstractBeanFactoryBasedTargetSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -60,7 +60,7 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
private String targetBeanName;
/** Class of the target */
- private Class> targetClass;
+ private volatile Class> targetClass;
/**
* BeanFactory that owns this TargetSource. We need to hold onto this
@@ -120,21 +120,28 @@ public BeanFactory getBeanFactory() {
@Override
- public synchronized Class> getTargetClass() {
- if (this.targetClass == null && this.beanFactory != null) {
- // Determine type of the target bean.
- this.targetClass = this.beanFactory.getType(this.targetBeanName);
- if (this.targetClass == null) {
- if (logger.isTraceEnabled()) {
- logger.trace("Getting bean with name '" + this.targetBeanName + "' in order to determine type");
- }
- Object beanInstance = this.beanFactory.getBean(this.targetBeanName);
- if (beanInstance != null) {
- this.targetClass = beanInstance.getClass();
+ public Class> getTargetClass() {
+ Class> targetClass = this.targetClass;
+ if (targetClass != null) {
+ return targetClass;
+ }
+ synchronized (this) {
+ // Full check within synchronization, entering the BeanFactory interaction algorithm only once...
+ targetClass = this.targetClass;
+ if (targetClass == null && this.beanFactory != null) {
+ // Determine type of the target bean.
+ targetClass = this.beanFactory.getType(this.targetBeanName);
+ if (targetClass == null) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Getting bean with name '" + this.targetBeanName + "' for type determination");
+ }
+ Object beanInstance = this.beanFactory.getBean(this.targetBeanName);
+ targetClass = beanInstance.getClass();
}
+ this.targetClass = targetClass;
}
+ return targetClass;
}
- return this.targetClass;
}
@Override
diff --git a/spring-aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java
index 415d0e57e1..450f431abe 100644
--- a/spring-aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java
+++ b/spring-aop/src/main/java/org/springframework/aop/target/AbstractPrototypeBasedTargetSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -74,8 +74,8 @@ protected Object newPrototypeInstance() throws BeansException {
* @param target the bean instance to destroy
*/
protected void destroyPrototypeInstance(Object target) {
- if (this.logger.isDebugEnabled()) {
- this.logger.debug("Destroying instance of bean '" + getTargetBeanName() + "'");
+ if (logger.isDebugEnabled()) {
+ logger.debug("Destroying instance of bean '" + getTargetBeanName() + "'");
}
if (getBeanFactory() instanceof ConfigurableBeanFactory) {
((ConfigurableBeanFactory) getBeanFactory()).destroyBean(getTargetBeanName(), target);
diff --git a/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java
index c96c21e193..7bab7e0771 100644
--- a/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java
+++ b/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -39,7 +39,7 @@
public abstract class AbstractRefreshableTargetSource implements TargetSource, Refreshable {
/** Logger available to subclasses */
- protected Log logger = LogFactory.getLog(getClass());
+ protected final Log logger = LogFactory.getLog(getClass());
protected Object targetObject;
diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPointTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPointTests.java
index f8c39e7b44..37f8094b5d 100644
--- a/spring-aop/src/test/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPointTests.java
+++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPointTests.java
@@ -44,7 +44,7 @@
* @author Ramnivas Laddad
* @since 2.0
*/
-public final class MethodInvocationProceedingJoinPointTests {
+public class MethodInvocationProceedingJoinPointTests {
@Test
public void testingBindingWithJoinPoint() {
@@ -217,7 +217,7 @@ public void before(Method method, Object[] args, Object target) throws Throwable
itb.unreliableFileOperation();
}
catch (IOException ex) {
- // we don't realy care...
+ // we don't really care...
}
}
diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java
index 424505004d..08740b5ade 100644
--- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java
+++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/ArgumentBindingTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -37,26 +37,26 @@
* @author Juergen Hoeller
* @author Chris Beams
*/
-public final class ArgumentBindingTests {
+public class ArgumentBindingTests {
- @Test(expected=IllegalArgumentException.class)
+ @Test(expected = IllegalArgumentException.class)
public void testBindingInPointcutUsedByAdvice() {
TestBean tb = new TestBean();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
proxyFactory.addAspect(NamedPointcutWithArgs.class);
- ITestBean proxiedTestBean = (ITestBean) proxyFactory.getProxy();
- proxiedTestBean.setName("Supercalifragalisticexpialidocious"); // should throw
+ ITestBean proxiedTestBean = proxyFactory.getProxy();
+ proxiedTestBean.setName("Supercalifragalisticexpialidocious");
}
- @Test(expected=IllegalStateException.class)
+ @Test(expected = IllegalStateException.class)
public void testAnnotationArgumentNameBinding() {
TransactionalBean tb = new TransactionalBean();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
- ITransactionalBean proxiedTestBean = (ITransactionalBean) proxyFactory.getProxy();
- proxiedTestBean.doInTransaction(); // should throw
+ ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
+ proxiedTestBean.doInTransaction();
}
@Test
@@ -71,6 +71,7 @@ public void testParameterNameDiscoverWithReferencePointcut() throws Exception {
assertEquals("formal", pnames[0]);
}
+
public void methodWithOneParam(String aParam) {
}
@@ -100,9 +101,6 @@ public void doInTransaction() {
}
-/**
- * @author Juergen Hoeller
- */
@Aspect
class PointcutWithAnnotationArgument {
@@ -115,9 +113,6 @@ public Object around(ProceedingJoinPoint pjp, Transactional transaction) throws
}
-/**
- * @author Adrian Colyer
- */
@Aspect
class NamedPointcutWithArgs {
diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptorTests.java
index 8d4c43a6e7..96b2d18430 100644
--- a/spring-aop/src/test/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptorTests.java
+++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/ConcurrencyThrottleInterceptorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -47,7 +47,7 @@ public final class ConcurrencyThrottleInterceptorTests {
public void testSerializable() throws Exception {
DerivedTestBean tb = new DerivedTestBean();
ProxyFactory proxyFactory = new ProxyFactory();
- proxyFactory.setInterfaces(new Class[] {ITestBean.class});
+ proxyFactory.setInterfaces(ITestBean.class);
ConcurrencyThrottleInterceptor cti = new ConcurrencyThrottleInterceptor();
proxyFactory.addAdvice(cti);
proxyFactory.setTarget(tb);
@@ -75,7 +75,7 @@ public void testMultipleThreadsWithLimit10() {
private void testMultipleThreads(int concurrencyLimit) {
TestBean tb = new TestBean();
ProxyFactory proxyFactory = new ProxyFactory();
- proxyFactory.setInterfaces(new Class[] {ITestBean.class});
+ proxyFactory.setInterfaces(ITestBean.class);
ConcurrencyThrottleInterceptor cti = new ConcurrencyThrottleInterceptor();
cti.setConcurrencyLimit(concurrencyLimit);
proxyFactory.addAdvice(cti);
@@ -95,7 +95,7 @@ private void testMultipleThreads(int concurrencyLimit) {
ex.printStackTrace();
}
threads[i] = new ConcurrencyThread(proxy,
- i % 2 == 0 ? (Throwable) new OutOfMemoryError() : (Throwable) new IllegalStateException());
+ i % 2 == 0 ? new OutOfMemoryError() : new IllegalStateException());
threads[i].start();
}
for (int i = 0; i < NR_OF_THREADS; i++) {
diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/CustomizableTraceInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/CustomizableTraceInterceptorTests.java
index 6146571fa6..7d17d49e17 100644
--- a/spring-aop/src/test/java/org/springframework/aop/interceptor/CustomizableTraceInterceptorTests.java
+++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/CustomizableTraceInterceptorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -83,7 +83,7 @@ public void testSetExceptionMethodWithReturnValuePlaceholder() {
public void testSunnyDayPathLogsCorrectly() throws Throwable {
MethodInvocation methodInvocation = mock(MethodInvocation.class);
- given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString", new Class[]{}));
+ given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString"));
given(methodInvocation.getThis()).willReturn(this);
Log log = mock(Log.class);
@@ -101,7 +101,7 @@ public void testExceptionPathLogsCorrectly() throws Throwable {
MethodInvocation methodInvocation = mock(MethodInvocation.class);
IllegalArgumentException exception = new IllegalArgumentException();
- given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString", new Class[]{}));
+ given(methodInvocation.getMethod()).willReturn(String.class.getMethod("toString"));
given(methodInvocation.getThis()).willReturn(this);
given(methodInvocation.proceed()).willThrow(exception);
diff --git a/spring-aop/src/test/java/org/springframework/aop/interceptor/SimpleTraceInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/interceptor/SimpleTraceInterceptorTests.java
index 3b811b2443..9c9d0c2ece 100644
--- a/spring-aop/src/test/java/org/springframework/aop/interceptor/SimpleTraceInterceptorTests.java
+++ b/spring-aop/src/test/java/org/springframework/aop/interceptor/SimpleTraceInterceptorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -34,7 +34,7 @@ public final class SimpleTraceInterceptorTests {
@Test
public void testSunnyDayPathLogsCorrectly() throws Throwable {
MethodInvocation mi = mock(MethodInvocation.class);
- given(mi.getMethod()).willReturn(String.class.getMethod("toString", new Class[]{}));
+ given(mi.getMethod()).willReturn(String.class.getMethod("toString"));
given(mi.getThis()).willReturn(this);
Log log = mock(Log.class);
@@ -48,7 +48,7 @@ public void testSunnyDayPathLogsCorrectly() throws Throwable {
@Test
public void testExceptionPathStillLogsCorrectly() throws Throwable {
MethodInvocation mi = mock(MethodInvocation.class);
- given(mi.getMethod()).willReturn(String.class.getMethod("toString", new Class[]{}));
+ given(mi.getMethod()).willReturn(String.class.getMethod("toString"));
given(mi.getThis()).willReturn(this);
IllegalArgumentException exception = new IllegalArgumentException();
given(mi.proceed()).willThrow(exception);
diff --git a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyAutowireTests.java b/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyAutowireTests.java
index 9886a0a145..4b5634f2b0 100644
--- a/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyAutowireTests.java
+++ b/spring-aop/src/test/java/org/springframework/aop/scope/ScopedProxyAutowireTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -16,6 +16,8 @@
package org.springframework.aop.scope;
+import java.util.Arrays;
+
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@@ -27,19 +29,24 @@
/**
* @author Mark Fisher
+ * @author Juergen Hoeller
* @author Chris Beams
*/
-public final class ScopedProxyAutowireTests {
+public class ScopedProxyAutowireTests {
- private static final Class> CLASS = ScopedProxyAutowireTests.class;
+ private static final Resource SCOPED_AUTOWIRE_FALSE_CONTEXT =
+ qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireFalse.xml");
+ private static final Resource SCOPED_AUTOWIRE_TRUE_CONTEXT =
+ qualifiedResource(ScopedProxyAutowireTests.class, "scopedAutowireTrue.xml");
- private static final Resource SCOPED_AUTOWIRE_TRUE_CONTEXT = qualifiedResource(CLASS, "scopedAutowireTrue.xml");
- private static final Resource SCOPED_AUTOWIRE_FALSE_CONTEXT = qualifiedResource(CLASS, "scopedAutowireFalse.xml");
@Test
public void testScopedProxyInheritsAutowireCandidateFalse() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(SCOPED_AUTOWIRE_FALSE_CONTEXT);
+ assertTrue(Arrays.asList(bf.getBeanNamesForType(TestBean.class, false, false)).contains("scoped"));
+ assertTrue(Arrays.asList(bf.getBeanNamesForType(TestBean.class, true, false)).contains("scoped"));
+ assertFalse(bf.containsSingleton("scoped"));
TestBean autowired = (TestBean) bf.getBean("autowired");
TestBean unscoped = (TestBean) bf.getBean("unscoped");
assertSame(unscoped, autowired.getChild());
@@ -49,6 +56,9 @@ public void testScopedProxyInheritsAutowireCandidateFalse() {
public void testScopedProxyReplacesAutowireCandidateTrue() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(SCOPED_AUTOWIRE_TRUE_CONTEXT);
+ assertTrue(Arrays.asList(bf.getBeanNamesForType(TestBean.class, true, false)).contains("scoped"));
+ assertTrue(Arrays.asList(bf.getBeanNamesForType(TestBean.class, false, false)).contains("scoped"));
+ assertFalse(bf.containsSingleton("scoped"));
TestBean autowired = (TestBean) bf.getBean("autowired");
TestBean scoped = (TestBean) bf.getBean("scoped");
assertSame(scoped, autowired.getChild());
diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java b/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java
index 95dff09cc0..de49c8af7f 100644
--- a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java
+++ b/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java
@@ -1,6 +1,5 @@
-
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -29,28 +28,22 @@ public class NopInterceptor implements MethodInterceptor {
private int count;
- /**
- * @see org.aopalliance.intercept.MethodInterceptor#invoke(MethodInvocation)
- */
+
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
increment();
return invocation.proceed();
}
- public int getCount() {
- return this.count;
- }
-
protected void increment() {
- ++count;
+ this.count++;
}
- @Override
- public int hashCode() {
- return 0;
+ public int getCount() {
+ return this.count;
}
+
@Override
public boolean equals(Object other) {
if (!(other instanceof NopInterceptor)) {
@@ -62,5 +55,9 @@ public boolean equals(Object other) {
return this.count == ((NopInterceptor) other).count;
}
+ @Override
+ public int hashCode() {
+ return NopInterceptor.class.hashCode();
+ }
}
diff --git a/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java b/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java
index 805dabd41a..bfa856144a 100644
--- a/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java
+++ b/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -28,31 +28,29 @@
@SuppressWarnings("serial")
public class SerializablePerson implements Person, Serializable {
- private static final long serialVersionUID = 1L;
-
-
private String name;
private int age;
+
@Override
- public int getAge() {
- return age;
+ public String getName() {
+ return name;
}
@Override
- public void setAge(int age) {
- this.age = age;
+ public void setName(String name) {
+ this.name = name;
}
@Override
- public String getName() {
- return name;
+ public int getAge() {
+ return age;
}
@Override
- public void setName(String name) {
- this.name = name;
+ public void setAge(int age) {
+ this.age = age;
}
@Override
@@ -63,10 +61,6 @@ public Object echo(Object o) throws Throwable {
return o;
}
- @Override
- public int hashCode() {
- return 0;
- }
@Override
public boolean equals(Object other) {
@@ -77,4 +71,9 @@ public boolean equals(Object other) {
return p.age == age && ObjectUtils.nullSafeEquals(name, p.name);
}
+ @Override
+ public int hashCode() {
+ return SerializablePerson.class.hashCode();
+ }
+
}
diff --git a/spring-aop/src/test/resources/org/springframework/aop/config/TopLevelAopTagTests-context.xml b/spring-aop/src/test/resources/org/springframework/aop/config/TopLevelAopTagTests-context.xml
index 6c9e44b560..f1fff08fa4 100644
--- a/spring-aop/src/test/resources/org/springframework/aop/config/TopLevelAopTagTests-context.xml
+++ b/spring-aop/src/test/resources/org/springframework/aop/config/TopLevelAopTagTests-context.xml
@@ -1,6 +1,6 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
@@ -9,4 +9,4 @@
-
\ No newline at end of file
+
diff --git a/spring-aop/src/test/resources/org/springframework/aop/scope/ScopedProxyAutowireTests-scopedAutowireFalse.xml b/spring-aop/src/test/resources/org/springframework/aop/scope/ScopedProxyAutowireTests-scopedAutowireFalse.xml
index feecab3e9c..5cb859c8c0 100644
--- a/spring-aop/src/test/resources/org/springframework/aop/scope/ScopedProxyAutowireTests-scopedAutowireFalse.xml
+++ b/spring-aop/src/test/resources/org/springframework/aop/scope/ScopedProxyAutowireTests-scopedAutowireFalse.xml
@@ -1,16 +1,16 @@
+ xmlns:aop="http://www.springframework.org/schema/aop"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
-
-
-
+
+
+
-
+
-
+
diff --git a/spring-aop/src/test/resources/org/springframework/aop/scope/ScopedProxyAutowireTests-scopedAutowireTrue.xml b/spring-aop/src/test/resources/org/springframework/aop/scope/ScopedProxyAutowireTests-scopedAutowireTrue.xml
index 445b50d064..22c62644dd 100644
--- a/spring-aop/src/test/resources/org/springframework/aop/scope/ScopedProxyAutowireTests-scopedAutowireTrue.xml
+++ b/spring-aop/src/test/resources/org/springframework/aop/scope/ScopedProxyAutowireTests-scopedAutowireTrue.xml
@@ -1,16 +1,16 @@
+ xmlns:aop="http://www.springframework.org/schema/aop"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
-
-
-
+
+
+
-
+
-
+
diff --git a/spring-aspects/aspects.gradle b/spring-aspects/aspects.gradle
index 2c68dff4da..78491cc0bd 100644
--- a/spring-aspects/aspects.gradle
+++ b/spring-aspects/aspects.gradle
@@ -1,5 +1,4 @@
-// redefine the compileJava and compileTestJava tasks in order to
-// compile sources with ajc instead of javac
+// Redefine the compileJava and compileTestJava tasks in order to compile sources with ajc instead of javac
configurations {
rt
@@ -8,22 +7,14 @@ configurations {
ajInpath
}
-// exclude spring-aspects as a module within IDEA until IDEA-64446 is resolved
-tasks.getByName("idea").onlyIf { false }
-tasks.getByName("ideaModule").onlyIf { false }
-
compileJava {
actions = []
dependsOn configurations.ajc.getTaskDependencyFromProjectDependency(true, "compileJava")
def outputDir = project.sourceSets.main.output.classesDir
-
inputs.files(project.sourceSets.main.allSource + project.sourceSets.main.compileClasspath)
outputs.dir outputDir
- ext.sourceCompatibility = project(":spring-core").compileJava.sourceCompatibility
- ext.targetCompatibility = project(":spring-core").compileJava.targetCompatibility
-
doLast{
// Assemble runtime classpath from folders and JARs that actually exist
def runtimeClasspath = project.files(sourceSets.main.runtimeClasspath.files.findAll({ it.exists() }))
@@ -53,13 +44,9 @@ compileTestJava {
dependsOn jar
def outputDir = project.sourceSets.test.output.classesDir
-
inputs.files(project.sourceSets.test.allSource + project.sourceSets.test.compileClasspath)
outputs.dir outputDir
- ext.sourceCompatibility = project(":spring-core").compileTestJava.sourceCompatibility
- ext.targetCompatibility = project(":spring-core").compileTestJava.targetCompatibility
-
doLast{
// Assemble runtime classpath from folders and JARs that actually exist
def runtimeClasspath = project.files(sourceSets.test.runtimeClasspath.files.findAll({ it.exists() }))
diff --git a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java
index 9932c79d12..32e5b77a15 100644
--- a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java
+++ b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -34,6 +34,7 @@
* @see org.springframework.cache.annotation.CachingConfigurationSelector
*/
@Configuration
+@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class AspectJCachingConfiguration extends AbstractCachingConfiguration {
@Bean(name = CacheManagementConfigUtils.CACHE_ASPECT_BEAN_NAME)
diff --git a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java
index 63076f476b..73bf36f068 100644
--- a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java
+++ b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJJCacheConfiguration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -34,6 +34,7 @@
* @see org.springframework.cache.annotation.CachingConfigurationSelector
*/
@Configuration
+@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class AspectJJCacheConfiguration extends AbstractJCacheConfiguration {
@Bean(name = CacheManagementConfigUtils.JCACHE_ASPECT_BEAN_NAME)
diff --git a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj
index fe378642a7..f77838e37b 100644
--- a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj
+++ b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj
@@ -36,7 +36,9 @@ import org.springframework.util.ObjectUtils;
* @author Rod Johnson
* @author Ramnivas Laddad
* @author Sam Brannen
+ * @deprecated as of Spring 4.3, in favor of a custom aspect for such purposes
*/
+@Deprecated
public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMethod()) {
private final Expectations expectations = new Expectations();
diff --git a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj
index b2979303bb..d67744df3a 100644
--- a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj
+++ b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj
@@ -59,7 +59,9 @@ import org.aspectj.lang.annotation.SuppressAjWarnings;
* @author Ramnivas Laddad
* @author Sam Brannen
* @see MockStaticEntityMethods
+ * @deprecated as of Spring 4.3, in favor of a custom aspect for such purposes
*/
+@Deprecated
@RequiredTypes("javax.persistence.Entity")
public aspect AnnotationDrivenStaticEntityMockingControl extends AbstractMethodMockingControl {
diff --git a/spring-aspects/src/main/java/org/springframework/mock/staticmock/MockStaticEntityMethods.java b/spring-aspects/src/main/java/org/springframework/mock/staticmock/MockStaticEntityMethods.java
index f68b80640b..3b2c128c57 100644
--- a/spring-aspects/src/main/java/org/springframework/mock/staticmock/MockStaticEntityMethods.java
+++ b/spring-aspects/src/main/java/org/springframework/mock/staticmock/MockStaticEntityMethods.java
@@ -29,7 +29,9 @@
*
* @author Rod Johnson
* @author Sam Brannen
+ * @deprecated as of Spring 4.3, in favor of a custom aspect for such purposes
*/
+@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MockStaticEntityMethods {
diff --git a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/JtaAnnotationTransactionAspect.aj b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/JtaAnnotationTransactionAspect.aj
index 6b02e1de3a..22a3bb134b 100644
--- a/spring-aspects/src/main/java/org/springframework/transaction/aspectj/JtaAnnotationTransactionAspect.aj
+++ b/spring-aspects/src/main/java/org/springframework/transaction/aspectj/JtaAnnotationTransactionAspect.aj
@@ -45,7 +45,7 @@ import org.springframework.transaction.annotation.AnnotationTransactionAttribute
* @see javax.transaction.Transactional
* @see AnnotationTransactionAspect
*/
-@RequiredTypes({"javax.transaction.Transactional"})
+@RequiredTypes("javax.transaction.Transactional")
public aspect JtaAnnotationTransactionAspect extends AbstractTransactionAspect {
public JtaAnnotationTransactionAspect() {
diff --git a/spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java b/spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java
index 5971b0ae00..c12bf12049 100644
--- a/spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java
+++ b/spring-aspects/src/test/java/org/springframework/transaction/aspectj/TransactionAspectTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -18,55 +18,45 @@
import java.lang.reflect.Method;
+import org.junit.Before;
+import org.junit.Test;
+
import org.springframework.tests.transaction.CallCountingTransactionManager;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
-import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.interceptor.TransactionAttribute;
+import static org.junit.Assert.*;
+
/**
* @author Rod Johnson
* @author Ramnivas Laddad
* @author Juergen Hoeller
* @author Sam Brannen
*/
-@SuppressWarnings("deprecation")
-public class TransactionAspectTests extends org.springframework.test.AbstractDependencyInjectionSpringContextTests {
+public class TransactionAspectTests {
- private CallCountingTransactionManager txManager;
+ private final CallCountingTransactionManager txManager = new CallCountingTransactionManager();
- private TransactionalAnnotationOnlyOnClassWithNoInterface annotationOnlyOnClassWithNoInterface;
+ private final TransactionalAnnotationOnlyOnClassWithNoInterface annotationOnlyOnClassWithNoInterface =
+ new TransactionalAnnotationOnlyOnClassWithNoInterface();
- private ClassWithProtectedAnnotatedMember beanWithAnnotatedProtectedMethod;
+ private final ClassWithProtectedAnnotatedMember beanWithAnnotatedProtectedMethod =
+ new ClassWithProtectedAnnotatedMember();
- private ClassWithPrivateAnnotatedMember beanWithAnnotatedPrivateMethod;
+ private final ClassWithPrivateAnnotatedMember beanWithAnnotatedPrivateMethod =
+ new ClassWithPrivateAnnotatedMember();
- private MethodAnnotationOnClassWithNoInterface methodAnnotationOnly = new MethodAnnotationOnClassWithNoInterface();
+ private final MethodAnnotationOnClassWithNoInterface methodAnnotationOnly =
+ new MethodAnnotationOnClassWithNoInterface();
- public void setAnnotationOnlyOnClassWithNoInterface(
- TransactionalAnnotationOnlyOnClassWithNoInterface annotationOnlyOnClassWithNoInterface) {
- this.annotationOnlyOnClassWithNoInterface = annotationOnlyOnClassWithNoInterface;
- }
-
- public void setClassWithAnnotatedProtectedMethod(ClassWithProtectedAnnotatedMember aBean) {
- this.beanWithAnnotatedProtectedMethod = aBean;
- }
-
- public void setClassWithAnnotatedPrivateMethod(ClassWithPrivateAnnotatedMember aBean) {
- this.beanWithAnnotatedPrivateMethod = aBean;
- }
-
- public void setTransactionAspect(TransactionAspectSupport transactionAspect) {
- this.txManager = (CallCountingTransactionManager) transactionAspect.getTransactionManager();
- }
-
-
- @Override
- protected String[] getConfigPaths() {
- return new String[] { "TransactionAspectTests-context.xml" };
+ @Before
+ public void initContext() {
+ AnnotationTransactionAspect.aspectOf().setTransactionManager(txManager);
}
+ @Test
public void testCommitOnAnnotatedClass() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
@@ -74,6 +64,7 @@ public void testCommitOnAnnotatedClass() throws Throwable {
assertEquals(1, txManager.commits);
}
+ @Test
public void testCommitOnAnnotatedProtectedMethod() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
@@ -81,6 +72,7 @@ public void testCommitOnAnnotatedProtectedMethod() throws Throwable {
assertEquals(1, txManager.commits);
}
+ @Test
public void testCommitOnAnnotatedPrivateMethod() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
@@ -88,6 +80,7 @@ public void testCommitOnAnnotatedPrivateMethod() throws Throwable {
assertEquals(1, txManager.commits);
}
+ @Test
public void testNoCommitOnNonAnnotatedNonPublicMethodInTransactionalType() throws Throwable {
txManager.clear();
assertEquals(0,txManager.begun);
@@ -95,6 +88,7 @@ public void testNoCommitOnNonAnnotatedNonPublicMethodInTransactionalType() throw
assertEquals(0,txManager.begun);
}
+ @Test
public void testCommitOnAnnotatedMethod() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
@@ -102,6 +96,7 @@ public void testCommitOnAnnotatedMethod() throws Throwable {
assertEquals(1, txManager.commits);
}
+ @Test
public void testNotTransactional() throws Throwable {
txManager.clear();
assertEquals(0, txManager.begun);
@@ -109,6 +104,7 @@ public void testNotTransactional() throws Throwable {
assertEquals(0, txManager.begun);
}
+ @Test
public void testDefaultCommitOnAnnotatedClass() throws Throwable {
final Exception ex = new Exception();
try {
@@ -125,6 +121,7 @@ public Object performTransactionalOperation() throws Throwable {
}
}
+ @Test
public void testDefaultRollbackOnAnnotatedClass() throws Throwable {
final RuntimeException ex = new RuntimeException();
try {
@@ -141,10 +138,11 @@ public Object performTransactionalOperation() throws Throwable {
}
}
+ @Test
public void testDefaultCommitOnSubclassOfAnnotatedClass() throws Throwable {
final Exception ex = new Exception();
try {
- testRollback(new TransactionOperationCallback() {
+ testRollback(new TransactionOperationCallback() {
@Override
public Object performTransactionalOperation() throws Throwable {
return new SubclassOfClassWithTransactionalAnnotation().echo(ex);
@@ -157,6 +155,7 @@ public Object performTransactionalOperation() throws Throwable {
}
}
+ @Test
public void testDefaultCommitOnSubclassOfClassWithTransactionalMethodAnnotated() throws Throwable {
final Exception ex = new Exception();
try {
@@ -173,6 +172,7 @@ public Object performTransactionalOperation() throws Throwable {
}
}
+ @Test
public void testDefaultCommitOnImplementationOfAnnotatedInterface() throws Throwable {
final Exception ex = new Exception();
testNotTransactional(new TransactionOperationCallback() {
@@ -185,16 +185,19 @@ public Object performTransactionalOperation() throws Throwable {
/**
* Note: resolution does not occur. Thus we can't make a class transactional if
- * it implements a transactionally annotated interface. This behaviour could only
+ * it implements a transactionally annotated interface. This behavior could only
* be changed in AbstractFallbackTransactionAttributeSource in Spring proper.
+ * See SPR-14322.
*/
+ @Test
public void testDoesNotResolveTxAnnotationOnMethodFromClassImplementingAnnotatedInterface() throws Exception {
AnnotationTransactionAttributeSource atas = new AnnotationTransactionAttributeSource();
- Method m = ImplementsAnnotatedInterface.class.getMethod("echo", Throwable.class);
- TransactionAttribute ta = atas.getTransactionAttribute(m, ImplementsAnnotatedInterface.class);
+ Method method = ImplementsAnnotatedInterface.class.getMethod("echo", Throwable.class);
+ TransactionAttribute ta = atas.getTransactionAttribute(method, ImplementsAnnotatedInterface.class);
assertNull(ta);
}
+ @Test
public void testDefaultRollbackOnImplementationOfAnnotatedInterface() throws Throwable {
final Exception rollbackProvokingException = new RuntimeException();
testNotTransactional(new TransactionOperationCallback() {
@@ -237,15 +240,19 @@ protected void testNotTransactional(TransactionOperationCallback toc, Throwable
private interface TransactionOperationCallback {
+
Object performTransactionalOperation() throws Throwable;
}
+
public static class SubclassOfClassWithTransactionalAnnotation extends TransactionalAnnotationOnlyOnClassWithNoInterface {
}
+
public static class SubclassOfClassWithTransactionalMethodAnnotation extends MethodAnnotationOnClassWithNoInterface {
}
+
public static class ImplementsAnnotatedInterface implements ITransactional {
@Override
@@ -257,6 +264,7 @@ public Object echo(Throwable t) throws Throwable {
}
}
+
public static class NotTransactional {
public void noop() {
diff --git a/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java b/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java
index 43d6823331..dbc949dd72 100644
--- a/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java
+++ b/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -237,6 +237,7 @@ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefin
}
Closure beans = new Closure(this) {
+ @Override
public Object call(Object[] args) {
invokeBeanDefiningClosure((Closure) args[0]);
return null;
@@ -303,7 +304,7 @@ public AbstractBeanDefinition bean(Class> type, Object...args) {
Collection constructorArgs = null;
if (!ObjectUtils.isEmpty(args)) {
int index = args.length;
- Object lastArg = args[index-1];
+ Object lastArg = args[index - 1];
if (lastArg instanceof Closure) {
callable = (Closure) lastArg;
index--;
@@ -380,10 +381,8 @@ else if ("ref".equals(name)) {
refName = args[0].toString();
}
boolean parentRef = false;
- if (args.length > 1) {
- if (args[1] instanceof Boolean) {
- parentRef = (Boolean) args[1];
- }
+ if (args.length > 1 && args[1] instanceof Boolean) {
+ parentRef = (Boolean) args[1];
}
return new RuntimeBeanReference(refName, parentRef);
}
@@ -410,12 +409,7 @@ else if (args.length > 1 && args[args.length -1] instanceof Closure) {
}
private boolean addDeferredProperty(String property, Object newValue) {
- if (newValue instanceof List) {
- this.deferredProperties.put(this.currentBeanDefinition.getBeanName() + '.' + property,
- new DeferredProperty(this.currentBeanDefinition, property, newValue));
- return true;
- }
- else if (newValue instanceof Map) {
+ if (newValue instanceof List || newValue instanceof Map) {
this.deferredProperties.put(this.currentBeanDefinition.getBeanName() + '.' + property,
new DeferredProperty(this.currentBeanDefinition, property, newValue));
return true;
@@ -456,14 +450,14 @@ protected GroovyBeanDefinitionReader invokeBeanDefiningClosure(Closure callable)
* @return the bean definition wrapper
*/
private GroovyBeanDefinitionWrapper invokeBeanDefiningMethod(String beanName, Object[] args) {
- boolean hasClosureArgument = args[args.length - 1] instanceof Closure;
+ boolean hasClosureArgument = (args[args.length - 1] instanceof Closure);
if (args[0] instanceof Class) {
- Class> beanClass = (args[0] instanceof Class ? (Class) args[0] : args[0].getClass());
+ Class> beanClass = (Class>) args[0];
if (args.length >= 1) {
if (hasClosureArgument) {
- if (args.length-1 != 1) {
+ if (args.length - 1 != 1) {
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(
- beanName, beanClass, resolveConstructorArguments(args,1,args.length-1));
+ beanName, beanClass, resolveConstructorArguments(args, 1, args.length - 1));
}
else {
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, beanClass);
@@ -471,7 +465,7 @@ private GroovyBeanDefinitionWrapper invokeBeanDefiningMethod(String beanName, Ob
}
else {
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(
- beanName, beanClass, resolveConstructorArguments(args,1,args.length));
+ beanName, beanClass, resolveConstructorArguments(args, 1, args.length));
}
}
@@ -483,7 +477,7 @@ else if (args[0] instanceof RuntimeBeanReference) {
else if (args[0] instanceof Map) {
// named constructor arguments
if (args.length > 1 && args[1] instanceof Class) {
- List constructorArgs = resolveConstructorArguments(args, 2, hasClosureArgument ? args.length-1 : args.length);
+ List constructorArgs = resolveConstructorArguments(args, 2, hasClosureArgument ? args.length - 1 : args.length);
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class)args[1], constructorArgs);
Map namedArgs = (Map)args[0];
for (Object o : namedArgs.keySet()) {
@@ -519,18 +513,18 @@ else if (args[0] instanceof Closure) {
this.currentBeanDefinition.getBeanDefinition().setAbstract(true);
}
else {
- List constructorArgs = resolveConstructorArguments(args, 0, hasClosureArgument ? args.length-1 : args.length);
+ List constructorArgs = resolveConstructorArguments(args, 0, hasClosureArgument ? args.length - 1 : args.length);
currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs);
}
if (hasClosureArgument) {
- Closure callable = (Closure)args[args.length-1];
+ Closure callable = (Closure) args[args.length - 1];
callable.setDelegate(this);
callable.setResolveStrategy(Closure.DELEGATE_FIRST);
- callable.call(new Object[]{currentBeanDefinition});
+ callable.call(this.currentBeanDefinition);
}
- GroovyBeanDefinitionWrapper beanDefinition = currentBeanDefinition;
+ GroovyBeanDefinitionWrapper beanDefinition = this.currentBeanDefinition;
this.currentBeanDefinition = null;
beanDefinition.getBeanDefinition().setAttribute(GroovyBeanDefinitionWrapper.class.getName(), beanDefinition);
getRegistry().registerBeanDefinition(beanName, beanDefinition.getBeanDefinition());
@@ -818,14 +812,17 @@ public boolean addAll(Collection values) {
return retVal;
}
+ @Override
public Object invokeMethod(String name, Object args) {
return InvokerHelper.invokeMethod(this.propertyValue, name, args);
}
+ @Override
public Object getProperty(String name) {
return InvokerHelper.getProperty(this.propertyValue, name);
}
+ @Override
public void setProperty(String name, Object value) {
InvokerHelper.setProperty(this.propertyValue, name, value);
}
diff --git a/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java b/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java
index 4a70a42c6d..1a93c9dc55 100644
--- a/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java
+++ b/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -162,6 +162,7 @@ public GroovyBeanDefinitionWrapper addProperty(String propertyName, Object prope
}
+ @Override
public Object getProperty(String property) {
if (this.definitionWrapper.isReadableProperty(property)) {
return this.definitionWrapper.getPropertyValue(property);
@@ -172,6 +173,7 @@ else if (dynamicProperties.contains(property)) {
return super.getProperty(property);
}
+ @Override
public void setProperty(String property, Object newValue) {
if (PARENT.equals(property)) {
setParent(newValue);
diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java
index 08e4ae39fe..6496e3d6f3 100644
--- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -55,10 +55,10 @@
* as String arrays are converted in such a format if the array itself is not
* assignable.
*
- * @author Rod Johnson
* @author Juergen Hoeller
- * @author Rob Harrop
* @author Stephane Nicoll
+ * @author Rod Johnson
+ * @author Rob Harrop
* @since 4.2
* @see #registerCustomEditor
* @see #setPropertyValues
@@ -94,11 +94,9 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
private String nestedPath = "";
- private Object rootObject;
+ Object rootObject;
- /**
- * Map with cached nested Accessors: nested path -> Accessor instance.
- */
+ /** Map with cached nested Accessors: nested path -> Accessor instance */
private Map nestedPropertyAccessors;
@@ -284,198 +282,212 @@ public void setPropertyValue(PropertyValue pv) throws BeansException {
}
}
- @SuppressWarnings("unchecked")
protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
- String propertyName = tokens.canonicalName;
- String actualName = tokens.actualName;
-
if (tokens.keys != null) {
- // Apply indexes and map keys: fetch value for all keys but the last one.
- PropertyTokenHolder getterTokens = new PropertyTokenHolder();
- getterTokens.canonicalName = tokens.canonicalName;
- getterTokens.actualName = tokens.actualName;
- getterTokens.keys = new String[tokens.keys.length - 1];
- System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
- Object propValue;
+ processKeyedProperty(tokens, pv);
+ }
+ else {
+ processLocalProperty(tokens, pv);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void processKeyedProperty(PropertyTokenHolder tokens, PropertyValue pv) {
+ Object propValue = getPropertyHoldingValue(tokens);
+ String lastKey = tokens.keys[tokens.keys.length - 1];
+
+ if (propValue.getClass().isArray()) {
+ PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
+ Class> requiredType = propValue.getClass().getComponentType();
+ int arrayIndex = Integer.parseInt(lastKey);
+ Object oldValue = null;
try {
- propValue = getPropertyValue(getterTokens);
- }
- catch (NotReadablePropertyException ex) {
- throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
- "Cannot access indexed value in property referenced " +
- "in indexed property path '" + propertyName + "'", ex);
- }
- // Set value for last key.
- String key = tokens.keys[tokens.keys.length - 1];
- if (propValue == null) {
- // null map value case
- if (isAutoGrowNestedPaths()) {
- // TODO: cleanup, this is pretty hacky
- int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
- getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex);
- propValue = setDefaultValue(getterTokens);
+ if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
+ oldValue = Array.get(propValue, arrayIndex);
}
- else {
- throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
- "Cannot access indexed value in property referenced " +
- "in indexed property path '" + propertyName + "': returned null");
+ Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
+ requiredType, ph.nested(tokens.keys.length));
+ int length = Array.getLength(propValue);
+ if (arrayIndex >= length && arrayIndex < this.autoGrowCollectionLimit) {
+ Class> componentType = propValue.getClass().getComponentType();
+ Object newArray = Array.newInstance(componentType, arrayIndex + 1);
+ System.arraycopy(propValue, 0, newArray, 0, length);
+ setPropertyValue(tokens.actualName, newArray);
+ propValue = getPropertyValue(tokens.actualName);
}
+ Array.set(propValue, arrayIndex, convertedValue);
}
- if (propValue.getClass().isArray()) {
- PropertyHandler ph = getLocalPropertyHandler(actualName);
- Class> requiredType = propValue.getClass().getComponentType();
- int arrayIndex = Integer.parseInt(key);
- Object oldValue = null;
- try {
- if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
- oldValue = Array.get(propValue, arrayIndex);
+ catch (IndexOutOfBoundsException ex) {
+ throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
+ "Invalid array index in property path '" + tokens.canonicalName + "'", ex);
+ }
+ }
+
+ else if (propValue instanceof List) {
+ PropertyHandler ph = getPropertyHandler(tokens.actualName);
+ Class> requiredType = ph.getCollectionType(tokens.keys.length);
+ List list = (List) propValue;
+ int index = Integer.parseInt(lastKey);
+ Object oldValue = null;
+ if (isExtractOldValueForEditor() && index < list.size()) {
+ oldValue = list.get(index);
+ }
+ Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
+ requiredType, ph.nested(tokens.keys.length));
+ int size = list.size();
+ if (index >= size && index < this.autoGrowCollectionLimit) {
+ for (int i = size; i < index; i++) {
+ try {
+ list.add(null);
}
- Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
- requiredType, ph.nested(tokens.keys.length));
- int length = Array.getLength(propValue);
- if (arrayIndex >= length && arrayIndex < this.autoGrowCollectionLimit) {
- Class> componentType = propValue.getClass().getComponentType();
- Object newArray = Array.newInstance(componentType, arrayIndex + 1);
- System.arraycopy(propValue, 0, newArray, 0, length);
- setPropertyValue(actualName, newArray);
- propValue = getPropertyValue(actualName);
+ catch (NullPointerException ex) {
+ throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
+ "Cannot set element with index " + index + " in List of size " +
+ size + ", accessed using property path '" + tokens.canonicalName +
+ "': List does not support filling up gaps with null elements");
}
- Array.set(propValue, arrayIndex, convertedValue);
- }
- catch (IndexOutOfBoundsException ex) {
- throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
- "Invalid array index in property path '" + propertyName + "'", ex);
}
+ list.add(convertedValue);
}
- else if (propValue instanceof List) {
- PropertyHandler ph = getPropertyHandler(actualName);
- Class> requiredType = ph.getCollectionType(tokens.keys.length);
- List list = (List) propValue;
- int index = Integer.parseInt(key);
- Object oldValue = null;
- if (isExtractOldValueForEditor() && index < list.size()) {
- oldValue = list.get(index);
- }
- Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
- requiredType, ph.nested(tokens.keys.length));
- int size = list.size();
- if (index >= size && index < this.autoGrowCollectionLimit) {
- for (int i = size; i < index; i++) {
- try {
- list.add(null);
- }
- catch (NullPointerException ex) {
- throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
- "Cannot set element with index " + index + " in List of size " +
- size + ", accessed using property path '" + propertyName +
- "': List does not support filling up gaps with null elements");
- }
- }
- list.add(convertedValue);
+ else {
+ try {
+ list.set(index, convertedValue);
}
- else {
- try {
- list.set(index, convertedValue);
- }
- catch (IndexOutOfBoundsException ex) {
- throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
- "Invalid list index in property path '" + propertyName + "'", ex);
- }
+ catch (IndexOutOfBoundsException ex) {
+ throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
+ "Invalid list index in property path '" + tokens.canonicalName + "'", ex);
}
}
- else if (propValue instanceof Map) {
- PropertyHandler ph = getLocalPropertyHandler(actualName);
- Class> mapKeyType = ph.getMapKeyType(tokens.keys.length);
- Class> mapValueType = ph.getMapValueType(tokens.keys.length);
- Map map = (Map) propValue;
- // IMPORTANT: Do not pass full property name in here - property editors
- // must not kick in for map keys but rather only for map values.
- TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType);
- Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor);
- Object oldValue = null;
- if (isExtractOldValueForEditor()) {
- oldValue = map.get(convertedMapKey);
+ }
+
+ else if (propValue instanceof Map) {
+ PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
+ Class> mapKeyType = ph.getMapKeyType(tokens.keys.length);
+ Class> mapValueType = ph.getMapValueType(tokens.keys.length);
+ Map map = (Map) propValue;
+ // IMPORTANT: Do not pass full property name in here - property editors
+ // must not kick in for map keys but rather only for map values.
+ TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType);
+ Object convertedMapKey = convertIfNecessary(null, null, lastKey, mapKeyType, typeDescriptor);
+ Object oldValue = null;
+ if (isExtractOldValueForEditor()) {
+ oldValue = map.get(convertedMapKey);
+ }
+ // Pass full property name and old value in here, since we want full
+ // conversion ability for map values.
+ Object convertedMapValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
+ mapValueType, ph.nested(tokens.keys.length));
+ map.put(convertedMapKey, convertedMapValue);
+ }
+
+ else {
+ throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
+ "Property referenced in indexed property path '" + tokens.canonicalName +
+ "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]");
+ }
+ }
+
+ private Object getPropertyHoldingValue(PropertyTokenHolder tokens) {
+ // Apply indexes and map keys: fetch value for all keys but the last one.
+ PropertyTokenHolder getterTokens = new PropertyTokenHolder();
+ getterTokens.canonicalName = tokens.canonicalName;
+ getterTokens.actualName = tokens.actualName;
+ getterTokens.keys = new String[tokens.keys.length - 1];
+ System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
+
+ Object propValue;
+ try {
+ propValue = getPropertyValue(getterTokens);
+ }
+ catch (NotReadablePropertyException ex) {
+ throw new NotWritablePropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
+ "Cannot access indexed value in property referenced " +
+ "in indexed property path '" + tokens.canonicalName + "'", ex);
+ }
+
+ if (propValue == null) {
+ // null map value case
+ if (isAutoGrowNestedPaths()) {
+ int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
+ getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex);
+ propValue = setDefaultValue(getterTokens);
+ }
+ else {
+ throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + tokens.canonicalName,
+ "Cannot access indexed value in property referenced " +
+ "in indexed property path '" + tokens.canonicalName + "': returned null");
+ }
+ }
+ return propValue;
+ }
+
+ private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
+ PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
+ if (ph == null || !ph.isWritable()) {
+ if (pv.isOptional()) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Ignoring optional value for property '" + tokens.actualName +
+ "' - property not found on bean class [" + getRootClass().getName() + "]");
}
- // Pass full property name and old value in here, since we want full
- // conversion ability for map values.
- Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
- mapValueType, ph.nested(tokens.keys.length));
- map.put(convertedMapKey, convertedMapValue);
+ return;
}
else {
- throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
- "Property referenced in indexed property path '" + propertyName +
- "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]");
+ throw createNotWritablePropertyException(tokens.canonicalName);
}
}
- else {
- PropertyHandler ph = getLocalPropertyHandler(actualName);
- if (ph == null || !ph.isWritable()) {
- if (pv.isOptional()) {
- if (logger.isDebugEnabled()) {
- logger.debug("Ignoring optional value for property '" + actualName +
- "' - property not found on bean class [" + getRootClass().getName() + "]");
- }
- return;
+ Object oldValue = null;
+ try {
+ Object originalValue = pv.getValue();
+ Object valueToApply = originalValue;
+ if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
+ if (pv.isConverted()) {
+ valueToApply = pv.getConvertedValue();
}
else {
- throw createNotWritablePropertyException(propertyName);
- }
- }
- Object oldValue = null;
- try {
- Object originalValue = pv.getValue();
- Object valueToApply = originalValue;
- if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
- if (pv.isConverted()) {
- valueToApply = pv.getConvertedValue();
- }
- else {
- if (isExtractOldValueForEditor() && ph.isReadable()) {
- try {
- oldValue = ph.getValue();
+ if (isExtractOldValueForEditor() && ph.isReadable()) {
+ try {
+ oldValue = ph.getValue();
+ }
+ catch (Exception ex) {
+ if (ex instanceof PrivilegedActionException) {
+ ex = ((PrivilegedActionException) ex).getException();
}
- catch (Exception ex) {
- if (ex instanceof PrivilegedActionException) {
- ex = ((PrivilegedActionException) ex).getException();
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Could not read previous value of property '" +
- this.nestedPath + propertyName + "'", ex);
- }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Could not read previous value of property '" +
+ this.nestedPath + tokens.canonicalName + "'", ex);
}
}
- valueToApply = convertForProperty(
- propertyName, oldValue, originalValue, ph.toTypeDescriptor());
}
- pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
+ valueToApply = convertForProperty(
+ tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
}
- ph.setValue(this.wrappedObject, valueToApply);
+ pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
}
- catch (TypeMismatchException ex) {
- throw ex;
+ ph.setValue(this.wrappedObject, valueToApply);
+ }
+ catch (TypeMismatchException ex) {
+ throw ex;
+ }
+ catch (InvocationTargetException ex) {
+ PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
+ this.rootObject, this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
+ if (ex.getTargetException() instanceof ClassCastException) {
+ throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
}
- catch (InvocationTargetException ex) {
- PropertyChangeEvent propertyChangeEvent =
- new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
- if (ex.getTargetException() instanceof ClassCastException) {
- throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
- }
- else {
- Throwable cause = ex.getTargetException();
- if (cause instanceof UndeclaredThrowableException) {
- // May happen e.g. with Groovy-generated methods
- cause = cause.getCause();
- }
- throw new MethodInvocationException(propertyChangeEvent, cause);
+ else {
+ Throwable cause = ex.getTargetException();
+ if (cause instanceof UndeclaredThrowableException) {
+ // May happen e.g. with Groovy-generated methods
+ cause = cause.getCause();
}
+ throw new MethodInvocationException(propertyChangeEvent, cause);
}
- catch (Exception ex) {
- PropertyChangeEvent pce =
- new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
- throw new MethodInvocationException(pce, ex);
- }
+ }
+ catch (Exception ex) {
+ PropertyChangeEvent pce = new PropertyChangeEvent(
+ this.rootObject, this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
+ throw new MethodInvocationException(pce, ex);
}
}
@@ -590,7 +602,7 @@ private Object convertIfNecessary(String propertyName, Object oldValue, Object n
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, requiredType, ex);
}
- catch (Throwable ex) {
+ catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, requiredType, ex);
@@ -914,11 +926,9 @@ else if (Map.class.isAssignableFrom(type)) {
return BeanUtils.instantiate(type);
}
}
- catch (Exception ex) {
- // TODO: Root cause exception context is lost here; just exception message preserved.
- // Should we throw another exception type that preserves context instead?
+ catch (Throwable ex) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name,
- "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path: " + ex);
+ "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path", ex);
}
}
@@ -975,9 +985,6 @@ public String toString() {
}
- /**
- * Handle a given property.
- */
protected abstract static class PropertyHandler {
private final Class> propertyType;
diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java
index 078712179a..216e5a4aab 100644
--- a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java
@@ -148,7 +148,7 @@ public Class> getPropertyType(String propertyPath) {
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyAccessException if the property was valid but the
- * accessor method failed or a type mismatch occured
+ * accessor method failed or a type mismatch occurred
*/
@Override
public abstract void setPropertyValue(String propertyName, Object value) throws BeansException;
diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
index 3956ca9357..0cdf050a2a 100644
--- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -63,11 +63,10 @@ public abstract class BeanUtils {
/**
* Convenience method to instantiate a class using its no-arg constructor.
- * As this method doesn't try to load classes by name, it should avoid
- * class-loading issues.
* @param clazz class to instantiate
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
+ * @see Class#newInstance()
*/
public static T instantiate(Class clazz) throws BeanInstantiationException {
Assert.notNull(clazz, "Class must not be null");
@@ -87,13 +86,12 @@ public static T instantiate(Class clazz) throws BeanInstantiationExceptio
/**
* Instantiate a class using its no-arg constructor.
- * As this method doesn't try to load classes by name, it should avoid
- * class-loading issues.
* Note that this method tries to set the constructor accessible
* if given a non-accessible (that is, non-public) constructor.
* @param clazz class to instantiate
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
+ * @see Constructor#newInstance
*/
public static T instantiateClass(Class clazz) throws BeanInstantiationException {
Assert.notNull(clazz, "Class must not be null");
@@ -111,17 +109,15 @@ public static T instantiateClass(Class clazz) throws BeanInstantiationExc
/**
* Instantiate a class using its no-arg constructor and return the new instance
* as the specified assignable type.
- * Useful in cases where
- * the type of the class to instantiate (clazz) is not available, but the type
- * desired (assignableTo) is known.
- *
As this method doesn't try to load classes by name, it should avoid
- * class-loading issues.
- *
Note that this method tries to set the constructor accessible
- * if given a non-accessible (that is, non-public) constructor.
+ *
Useful in cases where the type of the class to instantiate (clazz) is not
+ * available, but the type desired (assignableTo) is known.
+ *
Note that this method tries to set the constructor accessible if given a
+ * non-accessible (that is, non-public) constructor.
* @param clazz class to instantiate
* @param assignableTo type that clazz must be assignableTo
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
+ * @see Constructor#newInstance
*/
@SuppressWarnings("unchecked")
public static T instantiateClass(Class> clazz, Class assignableTo) throws BeanInstantiationException {
@@ -131,14 +127,13 @@ public static T instantiateClass(Class> clazz, Class assignableTo) thro
/**
* Convenience method to instantiate a class using the given constructor.
- * As this method doesn't try to load classes by name, it should avoid
- * class-loading issues.
- * Note that this method tries to set the constructor accessible
- * if given a non-accessible (that is, non-public) constructor.
+ *
Note that this method tries to set the constructor accessible if given a
+ * non-accessible (that is, non-public) constructor.
* @param ctor the constructor to instantiate
* @param args the constructor arguments to apply
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
+ * @see Constructor#newInstance
*/
public static T instantiateClass(Constructor ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
@@ -316,23 +311,23 @@ else if (!method.isBridge() && targetMethod.getParameterTypes().length == numPar
public static Method resolveSignature(String signature, Class> clazz) {
Assert.hasText(signature, "'signature' must not be empty");
Assert.notNull(clazz, "Class must not be null");
- int firstParen = signature.indexOf("(");
- int lastParen = signature.indexOf(")");
- if (firstParen > -1 && lastParen == -1) {
+ int startParen = signature.indexOf('(');
+ int endParen = signature.indexOf(')');
+ if (startParen > -1 && endParen == -1) {
throw new IllegalArgumentException("Invalid method signature '" + signature +
"': expected closing ')' for args list");
}
- else if (lastParen > -1 && firstParen == -1) {
+ else if (startParen == -1 && endParen > -1) {
throw new IllegalArgumentException("Invalid method signature '" + signature +
"': expected opening '(' for args list");
}
- else if (firstParen == -1 && lastParen == -1) {
+ else if (startParen == -1 && endParen == -1) {
return findMethodWithMinimalParameters(clazz, signature);
}
else {
- String methodName = signature.substring(0, firstParen);
+ String methodName = signature.substring(0, startParen);
String[] parameterTypeNames =
- StringUtils.commaDelimitedListToStringArray(signature.substring(firstParen + 1, lastParen));
+ StringUtils.commaDelimitedListToStringArray(signature.substring(startParen + 1, endParen));
Class>[] parameterTypes = new Class>[parameterTypeNames.length];
for (int i = 0; i < parameterTypeNames.length; i++) {
String parameterTypeName = parameterTypeNames[i].trim();
@@ -511,13 +506,14 @@ public static boolean isSimpleProperty(Class> clazz) {
/**
* Check if the given type represents a "simple" value type:
- * a primitive, a String or other CharSequence, a Number, a Date,
+ * a primitive, an enum, a String or other CharSequence, a Number, a Date,
* a URI, a URL, a Locale or a Class.
* @param clazz the type to check
* @return whether the given type represents a "simple" value type
*/
public static boolean isSimpleValueType(Class> clazz) {
- return (ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() ||
+ return (ClassUtils.isPrimitiveOrWrapper(clazz) ||
+ Enum.class.isAssignableFrom(clazz) ||
CharSequence.class.isAssignableFrom(clazz) ||
Number.class.isAssignableFrom(clazz) ||
Date.class.isAssignableFrom(clazz) ||
diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java
index e297d85fb8..a1686d0e80 100644
--- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java
+++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -141,6 +141,7 @@ private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl parent
*/
public void setBeanInstance(Object object) {
this.wrappedObject = object;
+ this.rootObject = object;
this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject);
setIntrospectionClass(object.getClass());
}
@@ -223,10 +224,7 @@ private Property property(PropertyDescriptor pd) {
@Override
protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
- if (pd != null) {
- return new BeanPropertyHandler(pd);
- }
- return null;
+ return (pd != null ? new BeanPropertyHandler(pd) : null);
}
@Override
@@ -237,8 +235,7 @@ protected BeanWrapperImpl newNestedPropertyAccessor(Object object, String nested
@Override
protected NotWritablePropertyException createNotWritablePropertyException(String propertyName) {
PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass());
- throw new NotWritablePropertyException(
- getRootClass(), getNestedPath() + propertyName,
+ throw new NotWritablePropertyException(getRootClass(), getNestedPath() + propertyName,
matches.buildErrorMessage(), matches.getPossibleMatches());
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
index 2efe9dabeb..a9a01f2bf4 100644
--- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
+++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -339,10 +339,10 @@ Class> getBeanClass() {
PropertyDescriptor getPropertyDescriptor(String name) {
PropertyDescriptor pd = this.propertyDescriptorCache.get(name);
if (pd == null && StringUtils.hasLength(name)) {
- // Same lenient fallback checking as in PropertyTypeDescriptor...
- pd = this.propertyDescriptorCache.get(name.substring(0, 1).toLowerCase() + name.substring(1));
+ // Same lenient fallback checking as in Property...
+ pd = this.propertyDescriptorCache.get(StringUtils.uncapitalize(name));
if (pd == null) {
- pd = this.propertyDescriptorCache.get(name.substring(0, 1).toUpperCase() + name.substring(1));
+ pd = this.propertyDescriptorCache.get(StringUtils.capitalize(name));
}
}
return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd :
diff --git a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java
index 356231132d..b4777d4409 100644
--- a/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/DirectFieldAccessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -76,8 +76,8 @@ protected FieldPropertyHandler getLocalPropertyHandler(String propertyName) {
Field field = ReflectionUtils.findField(getWrappedClass(), propertyName);
if (field != null) {
propertyHandler = new FieldPropertyHandler(field);
+ this.fieldMap.put(propertyName, propertyHandler);
}
- this.fieldMap.put(propertyName, propertyHandler);
}
return propertyHandler;
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/Mergeable.java b/spring-beans/src/main/java/org/springframework/beans/Mergeable.java
index cba64d94f8..d3d127c026 100644
--- a/spring-beans/src/main/java/org/springframework/beans/Mergeable.java
+++ b/spring-beans/src/main/java/org/springframework/beans/Mergeable.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -41,7 +41,7 @@ public interface Mergeable {
* @param parent the object to merge with
* @return the result of the merge operation
* @throws IllegalArgumentException if the supplied parent is {@code null}
- * @exception IllegalStateException if merging is not enabled for this instance
+ * @throws IllegalStateException if merging is not enabled for this instance
* (i.e. {@code mergeEnabled} equals {@code false}).
*/
Object merge(Object parent);
diff --git a/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java b/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java
index efb4ae9bbd..8167613a0f 100644
--- a/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java
+++ b/spring-beans/src/main/java/org/springframework/beans/MutablePropertyValues.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -262,7 +262,7 @@ public PropertyValue getPropertyValue(String propertyName) {
/**
* Get the raw property value, if any.
* @param propertyName the name to search for
- * @return the raw property value, or {@code null}
+ * @return the raw property value, or {@code null} if none found
* @since 4.0
* @see #getPropertyValue(String)
* @see PropertyValue#getValue()
@@ -283,11 +283,7 @@ public PropertyValues changesSince(PropertyValues old) {
for (PropertyValue newPv : this.propertyValueList) {
// if there wasn't an old one, add it
PropertyValue pvOld = old.getPropertyValue(newPv.getName());
- if (pvOld == null) {
- changes.addPropertyValue(newPv);
- }
- else if (!pvOld.equals(newPv)) {
- // it's changed
+ if (pvOld == null || !pvOld.equals(newPv)) {
changes.addPropertyValue(newPv);
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java b/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java
index e01fafbfec..e15885bc8a 100644
--- a/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -24,6 +24,7 @@
* spouse property of the target object has a null value.
*
* @author Rod Johnson
+ * @author Juergen Hoeller
*/
@SuppressWarnings("serial")
public class NullValueInNestedPathException extends InvalidPropertyException {
@@ -47,4 +48,16 @@ public NullValueInNestedPathException(Class> beanClass, String propertyName, S
super(beanClass, propertyName, msg);
}
+ /**
+ * Create a new NullValueInNestedPathException.
+ * @param beanClass the offending bean class
+ * @param propertyName the offending property
+ * @param msg the detail message
+ * @param cause the root cause
+ * @since 4.3.2
+ */
+ public NullValueInNestedPathException(Class> beanClass, String propertyName, String msg, Throwable cause) {
+ super(beanClass, propertyName, msg, cause);
+ }
+
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessException.java b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessException.java
index 68bdde7412..61cb509a86 100644
--- a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -18,8 +18,6 @@
import java.beans.PropertyChangeEvent;
-import org.springframework.core.ErrorCoded;
-
/**
* Superclass for exceptions related to a property access,
* such as type mismatch or invocation target exception.
@@ -27,8 +25,8 @@
* @author Rod Johnson
* @author Juergen Hoeller
*/
-@SuppressWarnings("serial")
-public abstract class PropertyAccessException extends BeansException implements ErrorCoded {
+@SuppressWarnings({"serial", "deprecation"})
+public abstract class PropertyAccessException extends BeansException implements org.springframework.core.ErrorCoded {
private transient PropertyChangeEvent propertyChangeEvent;
@@ -77,4 +75,10 @@ public Object getValue() {
return (this.propertyChangeEvent != null ? this.propertyChangeEvent.getNewValue() : null);
}
+ /**
+ * Return a corresponding error code for this type of exception.
+ */
+ @Override
+ public abstract String getErrorCode();
+
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java
index 00068ea4b6..fe9151805c 100644
--- a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -81,8 +81,6 @@ public interface PropertyAccessor {
* (may be a nested path and/or an indexed/mapped property)
* @return the property type for the particular property,
* or {@code null} if not determinable
- * @throws InvalidPropertyException if there is no such property or
- * if the property isn't readable
* @throws PropertyAccessException if the property was valid but the
* accessor method failed
*/
@@ -95,8 +93,8 @@ public interface PropertyAccessor {
* (may be a nested path and/or an indexed/mapped property)
* @return the property type for the particular property,
* or {@code null} if not determinable
- * @throws InvalidPropertyException if there is no such property or
- * if the property isn't readable
+ * @throws PropertyAccessException if the property was valid but the
+ * accessor method failed
*/
TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
@@ -120,7 +118,7 @@ public interface PropertyAccessor {
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyAccessException if the property was valid but the
- * accessor method failed or a type mismatch occured
+ * accessor method failed or a type mismatch occurred
*/
void setPropertyValue(String propertyName, Object value) throws BeansException;
@@ -130,7 +128,7 @@ public interface PropertyAccessor {
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyAccessException if the property was valid but the
- * accessor method failed or a type mismatch occured
+ * accessor method failed or a type mismatch occurred
*/
void setPropertyValue(PropertyValue pv) throws BeansException;
@@ -144,7 +142,7 @@ public interface PropertyAccessor {
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions
- * occured for specific properties during the batch update. This exception bundles
+ * occurred for specific properties during the batch update. This exception bundles
* all individual PropertyAccessExceptions. All other properties will have been
* successfully updated.
*/
@@ -164,7 +162,7 @@ public interface PropertyAccessor {
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions
- * occured for specific properties during the batch update. This exception bundles
+ * occurred for specific properties during the batch update. This exception bundles
* all individual PropertyAccessExceptions. All other properties will have been
* successfully updated.
* @see #setPropertyValues(PropertyValues, boolean, boolean)
@@ -185,7 +183,7 @@ public interface PropertyAccessor {
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions
- * occured for specific properties during the batch update. This exception bundles
+ * occurred for specific properties during the batch update. This exception bundles
* all individual PropertyAccessExceptions. All other properties will have been
* successfully updated.
* @see #setPropertyValues(PropertyValues, boolean, boolean)
@@ -208,7 +206,7 @@ void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions
- * occured for specific properties during the batch update. This exception bundles
+ * occurred for specific properties during the batch update. This exception bundles
* all individual PropertyAccessExceptions. All other properties will have been
* successfully updated.
*/
diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java b/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java
index 5e766690af..f90d718a05 100644
--- a/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/PropertyDescriptorUtils.java
@@ -75,7 +75,7 @@ public static Class> findPropertyType(Method readMethod, Method writeMethod) t
}
if (writeMethod != null) {
- Class> params[] = writeMethod.getParameterTypes();
+ Class>[] params = writeMethod.getParameterTypes();
if (params.length != 1) {
throw new IntrospectionException("Bad write method arg count: " + writeMethod);
}
@@ -109,7 +109,7 @@ public static Class> findIndexedPropertyType(String name, Class> propertyTyp
Class> indexedPropertyType = null;
if (indexedReadMethod != null) {
- Class> params[] = indexedReadMethod.getParameterTypes();
+ Class>[] params = indexedReadMethod.getParameterTypes();
if (params.length != 1) {
throw new IntrospectionException("Bad indexed read method arg count: " + indexedReadMethod);
}
@@ -123,7 +123,7 @@ public static Class> findIndexedPropertyType(String name, Class> propertyTyp
}
if (indexedWriteMethod != null) {
- Class> params[] = indexedWriteMethod.getParameterTypes();
+ Class>[] params = indexedWriteMethod.getParameterTypes();
if (params.length != 2) {
throw new IntrospectionException("Bad indexed write method arg count: " + indexedWriteMethod);
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java b/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java
index f42d6c332e..71825c38cf 100644
--- a/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java
+++ b/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java
@@ -59,6 +59,7 @@
import org.springframework.beans.propertyeditors.InputSourceEditor;
import org.springframework.beans.propertyeditors.InputStreamEditor;
import org.springframework.beans.propertyeditors.LocaleEditor;
+import org.springframework.beans.propertyeditors.PathEditor;
import org.springframework.beans.propertyeditors.PatternEditor;
import org.springframework.beans.propertyeditors.PropertiesEditor;
import org.springframework.beans.propertyeditors.ReaderEditor;
@@ -87,11 +88,21 @@
*/
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
+ private static Class> pathClass;
+
private static Class> zoneIdClass;
static {
+ ClassLoader cl = PropertyEditorRegistrySupport.class.getClassLoader();
+ try {
+ pathClass = ClassUtils.forName("java.nio.file.Path", cl);
+ }
+ catch (ClassNotFoundException ex) {
+ // Java 7 Path class not available
+ pathClass = null;
+ }
try {
- zoneIdClass = ClassUtils.forName("java.time.ZoneId", PropertyEditorRegistrySupport.class.getClassLoader());
+ zoneIdClass = ClassUtils.forName("java.time.ZoneId", cl);
}
catch (ClassNotFoundException ex) {
// Java 8 ZoneId class not available
@@ -211,6 +222,9 @@ private void createDefaultEditors() {
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
+ if (pathClass != null) {
+ this.defaultEditors.put(pathClass, new PathEditor());
+ }
this.defaultEditors.put(Pattern.class, new PatternEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Reader.class, new ReaderEditor());
diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java b/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java
index 87cea92830..8cfbc4a046 100644
--- a/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java
+++ b/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -30,8 +30,8 @@
* Helper class for calculating property matches, according to a configurable
* distance. Provide the list of potential matches and an easy way to generate
* an error message. Works for both java bean properties and fields.
- *
- * Mainly for use within the framework and in particular the binding facility
+ *
+ *
Mainly for use within the framework and in particular the binding facility.
*
* @author Alef Arendsen
* @author Arjen Poutsma
@@ -43,14 +43,12 @@
*/
public abstract class PropertyMatches {
- //---------------------------------------------------------------------
- // Static section
- //---------------------------------------------------------------------
-
/** Default maximum property distance: 2 */
public static final int DEFAULT_MAX_DISTANCE = 2;
+ // Static factory methods
+
/**
* Create PropertyMatches for the given bean property.
* @param propertyName the name of the property to find possible matches for
@@ -90,9 +88,7 @@ public static PropertyMatches forField(String propertyName, Class> beanClass,
}
- //---------------------------------------------------------------------
- // Instance section
- //---------------------------------------------------------------------
+ // Instance state
private final String propertyName;
@@ -107,18 +103,19 @@ private PropertyMatches(String propertyName, String[] possibleMatches) {
this.possibleMatches = possibleMatches;
}
+
/**
* Return the name of the requested property.
*/
public String getPropertyName() {
- return propertyName;
+ return this.propertyName;
}
/**
* Return the calculated possible matches.
*/
public String[] getPossibleMatches() {
- return possibleMatches;
+ return this.possibleMatches;
}
/**
@@ -127,6 +124,9 @@ public String[] getPossibleMatches() {
*/
public abstract String buildErrorMessage();
+
+ // Implementation support for subclasses
+
protected void appendHintMessage(StringBuilder msg) {
msg.append("Did you mean ");
for (int i = 0; i < this.possibleMatches.length; i++) {
@@ -150,14 +150,14 @@ else if (i == this.possibleMatches.length - 2) {
* @return the distance value
*/
private static int calculateStringDistance(String s1, String s2) {
- if (s1.length() == 0) {
+ if (s1.isEmpty()) {
return s2.length();
}
- if (s2.length() == 0) {
+ if (s2.isEmpty()) {
return s1.length();
}
- int d[][] = new int[s1.length() + 1][s2.length() + 1];
+ int[][] d = new int[s1.length() + 1][s2.length() + 1];
for (int i = 0; i <= s1.length(); i++) {
d[i][0] = i;
}
@@ -166,45 +166,46 @@ private static int calculateStringDistance(String s1, String s2) {
}
for (int i = 1; i <= s1.length(); i++) {
- char s_i = s1.charAt(i - 1);
+ char c1 = s1.charAt(i - 1);
for (int j = 1; j <= s2.length(); j++) {
int cost;
- char t_j = s2.charAt(j - 1);
- if (s_i == t_j) {
+ char c2 = s2.charAt(j - 1);
+ if (c1 == c2) {
cost = 0;
}
else {
cost = 1;
}
- d[i][j] = Math.min(Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1),
- d[i - 1][j - 1] + cost);
+ d[i][j] = Math.min(Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1), d[i - 1][j - 1] + cost);
}
}
return d[s1.length()][s2.length()];
}
+
+ // Concrete subclasses
+
private static class BeanPropertyMatches extends PropertyMatches {
- private BeanPropertyMatches(String propertyName, Class> beanClass, int maxDistance) {
- super(propertyName, calculateMatches(propertyName,
- BeanUtils.getPropertyDescriptors(beanClass), maxDistance));
+ public BeanPropertyMatches(String propertyName, Class> beanClass, int maxDistance) {
+ super(propertyName,
+ calculateMatches(propertyName, BeanUtils.getPropertyDescriptors(beanClass), maxDistance));
}
/**
- * Generate possible property alternatives for the given property and
- * class. Internally uses the {@code getStringDistance} method, which
- * in turn uses the Levenshtein algorithm to determine the distance between
- * two Strings.
- * @param propertyDescriptors the JavaBeans property descriptors to search
+ * Generate possible property alternatives for the given property and class.
+ * Internally uses the {@code getStringDistance} method, which in turn uses
+ * the Levenshtein algorithm to determine the distance between two Strings.
+ * @param descriptors the JavaBeans property descriptors to search
* @param maxDistance the maximum distance to accept
*/
- private static String[] calculateMatches(String propertyName, PropertyDescriptor[] propertyDescriptors, int maxDistance) {
+ private static String[] calculateMatches(String name, PropertyDescriptor[] descriptors, int maxDistance) {
List candidates = new ArrayList();
- for (PropertyDescriptor pd : propertyDescriptors) {
+ for (PropertyDescriptor pd : descriptors) {
if (pd.getWriteMethod() != null) {
String possibleAlternative = pd.getName();
- if (calculateStringDistance(propertyName, possibleAlternative) <= maxDistance) {
+ if (calculateStringDistance(name, possibleAlternative) <= maxDistance) {
candidates.add(possibleAlternative);
}
}
@@ -213,40 +214,35 @@ private static String[] calculateMatches(String propertyName, PropertyDescriptor
return StringUtils.toStringArray(candidates);
}
-
@Override
public String buildErrorMessage() {
- String propertyName = getPropertyName();
- String[] possibleMatches = getPossibleMatches();
- StringBuilder msg = new StringBuilder();
- msg.append("Bean property '");
- msg.append(propertyName);
- msg.append("' is not writable or has an invalid setter method. ");
-
- if (ObjectUtils.isEmpty(possibleMatches)) {
- msg.append("Does the parameter type of the setter match the return type of the getter?");
+ StringBuilder msg = new StringBuilder(160);
+ msg.append("Bean property '").append(getPropertyName()).append(
+ "' is not writable or has an invalid setter method. ");
+ if (!ObjectUtils.isEmpty(getPossibleMatches())) {
+ appendHintMessage(msg);
}
else {
- appendHintMessage(msg);
+ msg.append("Does the parameter type of the setter match the return type of the getter?");
}
return msg.toString();
}
-
}
+
private static class FieldPropertyMatches extends PropertyMatches {
- private FieldPropertyMatches(String propertyName, Class> beanClass, int maxDistance) {
+ public FieldPropertyMatches(String propertyName, Class> beanClass, int maxDistance) {
super(propertyName, calculateMatches(propertyName, beanClass, maxDistance));
}
- private static String[] calculateMatches(final String propertyName, Class> beanClass, final int maxDistance) {
+ private static String[] calculateMatches(final String name, Class> clazz, final int maxDistance) {
final List candidates = new ArrayList();
- ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
+ ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
String possibleAlternative = field.getName();
- if (calculateStringDistance(propertyName, possibleAlternative) <= maxDistance) {
+ if (calculateStringDistance(name, possibleAlternative) <= maxDistance) {
candidates.add(possibleAlternative);
}
}
@@ -255,22 +251,16 @@ public void doWith(Field field) throws IllegalArgumentException, IllegalAccessEx
return StringUtils.toStringArray(candidates);
}
-
@Override
public String buildErrorMessage() {
- String propertyName = getPropertyName();
- String[] possibleMatches = getPossibleMatches();
- StringBuilder msg = new StringBuilder();
- msg.append("Bean property '");
- msg.append(propertyName);
- msg.append("' has no matching field. ");
-
- if (!ObjectUtils.isEmpty(possibleMatches)) {
+ StringBuilder msg = new StringBuilder(80);
+ msg.append("Bean property '").append(getPropertyName()).append("' has no matching field.");
+ if (!ObjectUtils.isEmpty(getPossibleMatches())) {
+ msg.append(' ');
appendHintMessage(msg);
}
return msg.toString();
}
-
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java b/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java
index 4f4f37ca79..3c2f4e6c5b 100644
--- a/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java
+++ b/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -45,8 +45,6 @@ public class PropertyValue extends BeanMetadataAttributeAccessor implements Seri
private final Object value;
- private Object source;
-
private boolean optional = false;
private boolean converted = false;
@@ -78,12 +76,12 @@ public PropertyValue(PropertyValue original) {
Assert.notNull(original, "Original must not be null");
this.name = original.getName();
this.value = original.getValue();
- this.source = original.getSource();
this.optional = original.isOptional();
this.converted = original.converted;
this.convertedValue = original.convertedValue;
this.conversionNecessary = original.conversionNecessary;
this.resolvedTokens = original.resolvedTokens;
+ setSource(original.getSource());
copyAttributesFrom(original);
}
@@ -97,10 +95,10 @@ public PropertyValue(PropertyValue original, Object newValue) {
Assert.notNull(original, "Original must not be null");
this.name = original.getName();
this.value = newValue;
- this.source = original;
this.optional = original.isOptional();
this.conversionNecessary = original.conversionNecessary;
this.resolvedTokens = original.resolvedTokens;
+ setSource(original);
copyAttributesFrom(original);
}
@@ -129,16 +127,28 @@ public Object getValue() {
*/
public PropertyValue getOriginalPropertyValue() {
PropertyValue original = this;
- while (original.source instanceof PropertyValue && original.source != original) {
- original = (PropertyValue) original.source;
+ Object source = getSource();
+ while (source instanceof PropertyValue && source != original) {
+ original = (PropertyValue) source;
+ source = original.getSource();
}
return original;
}
+ /**
+ * Set whether this is an optional value, that is, to be ignored
+ * when no corresponding property exists on the target class.
+ * @since 3.0
+ */
public void setOptional(boolean optional) {
this.optional = optional;
}
+ /**
+ * Return whether this is an optional value, that is, to be ignored
+ * when no corresponding property exists on the target class.
+ * @since 3.0
+ */
public boolean isOptional() {
return this.optional;
}
@@ -180,7 +190,7 @@ public boolean equals(Object other) {
PropertyValue otherPv = (PropertyValue) other;
return (this.name.equals(otherPv.name) &&
ObjectUtils.nullSafeEquals(this.value, otherPv.value) &&
- ObjectUtils.nullSafeEquals(this.source, otherPv.source));
+ ObjectUtils.nullSafeEquals(getSource(), otherPv.getSource()));
}
@Override
diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
index 9928530dbd..ef7d44eb06 100644
--- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
+++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -35,6 +35,7 @@
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ClassUtils;
import org.springframework.util.NumberUtils;
+import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -269,7 +270,7 @@ else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requi
}
else {
// convertedValue == null
- if (javaUtilOptionalEmpty != null && requiredType.equals(javaUtilOptionalEmpty.getClass())) {
+ if (javaUtilOptionalEmpty != null && requiredType == javaUtilOptionalEmpty.getClass()) {
convertedValue = javaUtilOptionalEmpty;
}
}
@@ -290,15 +291,15 @@ else if (conversionService != null) {
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
- msg.append("Cannot convert value of type [").append(ClassUtils.getDescriptiveType(newValue));
- msg.append("] to required type [").append(ClassUtils.getQualifiedName(requiredType)).append("]");
+ msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
+ msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
- "] returned inappropriate value of type [").append(
- ClassUtils.getDescriptiveType(convertedValue)).append("]");
+ "] returned inappropriate value of type '").append(
+ ClassUtils.getDescriptiveType(convertedValue)).append("'");
throw new IllegalArgumentException(msg.toString());
}
else {
@@ -324,7 +325,7 @@ private Object attemptToConvertStringToEnum(Class> requiredType, String trimme
if (Enum.class == requiredType) {
// target type is declared as raw enum, treat the trimmed value as .FIELD_NAME
- int index = trimmedValue.lastIndexOf(".");
+ int index = trimmedValue.lastIndexOf('.');
if (index > - 1) {
String enumType = trimmedValue.substring(0, index);
String fieldName = trimmedValue.substring(index + 1);
@@ -353,6 +354,7 @@ private Object attemptToConvertStringToEnum(Class> requiredType, String trimme
// to be checked, hence we don't return it right away.
try {
Field enumField = requiredType.getField(trimmedValue);
+ ReflectionUtils.makeAccessible(enumField);
convertedValue = enumField.get(null);
}
catch (Throwable ex) {
diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java
index ba9fc09cca..9be206c694 100644
--- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java
+++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java
@@ -73,7 +73,7 @@ private T doConvert(Object value, Class requiredType, MethodParameter met
catch (IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
- catch (Throwable ex) {
+ catch (IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java b/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java
index c2cffe10b5..7bc7a90113 100644
--- a/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/TypeMismatchException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -41,7 +41,7 @@ public class TypeMismatchException extends PropertyAccessException {
/**
- * Create a new TypeMismatchException.
+ * Create a new {@code TypeMismatchException}.
* @param propertyChangeEvent the PropertyChangeEvent that resulted in the problem
* @param requiredType the required target type
*/
@@ -50,17 +50,17 @@ public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class> r
}
/**
- * Create a new TypeMismatchException.
+ * Create a new {@code TypeMismatchException}.
* @param propertyChangeEvent the PropertyChangeEvent that resulted in the problem
* @param requiredType the required target type (or {@code null} if not known)
* @param cause the root cause (may be {@code null})
*/
public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class> requiredType, Throwable cause) {
super(propertyChangeEvent,
- "Failed to convert property value of type [" +
- ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "]" +
+ "Failed to convert property value of type '" +
+ ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "'" +
(requiredType != null ?
- " to required type [" + ClassUtils.getQualifiedName(requiredType) + "]" : "") +
+ " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : "") +
(propertyChangeEvent.getPropertyName() != null ?
" for property '" + propertyChangeEvent.getPropertyName() + "'" : ""),
cause);
@@ -69,7 +69,7 @@ public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class> r
}
/**
- * Create a new TypeMismatchException without PropertyChangeEvent.
+ * Create a new {@code TypeMismatchException} without a {@code PropertyChangeEvent}.
* @param value the offending value that couldn't be converted (may be {@code null})
* @param requiredType the required target type (or {@code null} if not known)
*/
@@ -78,14 +78,14 @@ public TypeMismatchException(Object value, Class> requiredType) {
}
/**
- * Create a new TypeMismatchException without PropertyChangeEvent.
+ * Create a new {@code TypeMismatchException} without a {@code PropertyChangeEvent}.
* @param value the offending value that couldn't be converted (may be {@code null})
* @param requiredType the required target type (or {@code null} if not known)
* @param cause the root cause (may be {@code null})
*/
public TypeMismatchException(Object value, Class> requiredType, Throwable cause) {
- super("Failed to convert value of type [" + ClassUtils.getDescriptiveType(value) + "]" +
- (requiredType != null ? " to required type [" + ClassUtils.getQualifiedName(requiredType) + "]" : ""),
+ super("Failed to convert value of type '" + ClassUtils.getDescriptiveType(value) + "'" +
+ (requiredType != null ? " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : ""),
cause);
this.value = value;
this.requiredType = requiredType;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/Aware.java b/spring-beans/src/main/java/org/springframework/beans/factory/Aware.java
index f993f1f713..8423a458e1 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/Aware.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/Aware.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2018 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,21 +17,19 @@
package org.springframework.beans.factory;
/**
- * Marker superinterface indicating that a bean is eligible to be
- * notified by the Spring container of a particular framework object
- * through a callback-style method. Actual method signature is
- * determined by individual subinterfaces, but should typically
- * consist of just one void-returning method that accepts a single
- * argument.
+ * A marker superinterface indicating that a bean is eligible to be notified by the
+ * Spring container of a particular framework object through a callback-style method.
+ * The actual method signature is determined by individual subinterfaces but should
+ * typically consist of just one void-returning method that accepts a single argument.
*
- * Note that merely implementing {@link Aware} provides no default
- * functionality. Rather, processing must be done explicitly, for example
- * in a {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}.
+ *
Note that merely implementing {@link Aware} provides no default functionality.
+ * Rather, processing must be done explicitly, for example in a
+ * {@link org.springframework.beans.factory.config.BeanPostProcessor}.
* Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
- * and {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory}
- * for examples of processing {@code *Aware} interface callbacks.
+ * for an example of processing specific {@code *Aware} interface callbacks.
*
* @author Chris Beams
+ * @author Juergen Hoeller
* @since 3.1
*/
public interface Aware {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java
index dc60afb462..75a3838692 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java
@@ -123,7 +123,7 @@ public String getResourceDescription() {
/**
* Add a related cause to this bean creation exception,
- * not being a direct cause of the failure but having occured
+ * not being a direct cause of the failure but having occurred
* earlier in the creation of the same bean instance.
* @param ex the related cause to add
*/
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanDefinitionStoreException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanDefinitionStoreException.java
index 47a2ec8c14..1d0fc9a7f4 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanDefinitionStoreException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanDefinitionStoreException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -75,7 +75,7 @@ public BeanDefinitionStoreException(String resourceDescription, String msg, Thro
/**
* Create a new BeanDefinitionStoreException.
* @param resourceDescription description of the resource that the bean definition came from
- * @param beanName the name of the bean requested
+ * @param beanName the name of the bean
* @param msg the detail message (appended to an introductory message that indicates
* the resource and the name of the bean)
*/
@@ -86,28 +86,28 @@ public BeanDefinitionStoreException(String resourceDescription, String beanName,
/**
* Create a new BeanDefinitionStoreException.
* @param resourceDescription description of the resource that the bean definition came from
- * @param beanName the name of the bean requested
+ * @param beanName the name of the bean
* @param msg the detail message (appended to an introductory message that indicates
* the resource and the name of the bean)
* @param cause the root cause (may be {@code null})
*/
public BeanDefinitionStoreException(String resourceDescription, String beanName, String msg, Throwable cause) {
- super("Invalid bean definition with name '" + beanName + "' defined in " + resourceDescription + ": " + msg, cause);
+ super("Invalid bean definition with name '" + beanName + "' defined in " + resourceDescription + ": " + msg,
+ cause);
this.resourceDescription = resourceDescription;
this.beanName = beanName;
}
/**
- * Return the description of the resource that the bean
- * definition came from, if any.
+ * Return the description of the resource that the bean definition came from, if available.
*/
public String getResourceDescription() {
return this.resourceDescription;
}
/**
- * Return the name of the bean requested, if any.
+ * Return the name of the bean, if available.
*/
public String getBeanName() {
return this.beanName;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java
index 58b2fd97bb..83498c333e 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -63,28 +63,35 @@
* are supposed to override beans of the same name in any parent factory.
*
*
Bean factory implementations should support the standard bean lifecycle interfaces
- * as far as possible. The full set of initialization methods and their standard order is:
- * 1. BeanNameAware's {@code setBeanName}
- * 2. BeanClassLoaderAware's {@code setBeanClassLoader}
- * 3. BeanFactoryAware's {@code setBeanFactory}
- * 4. ResourceLoaderAware's {@code setResourceLoader}
- * (only applicable when running in an application context)
- * 5. ApplicationEventPublisherAware's {@code setApplicationEventPublisher}
- * (only applicable when running in an application context)
- * 6. MessageSourceAware's {@code setMessageSource}
- * (only applicable when running in an application context)
- * 7. ApplicationContextAware's {@code setApplicationContext}
- * (only applicable when running in an application context)
- * 8. ServletContextAware's {@code setServletContext}
- * (only applicable when running in a web application context)
- * 9. {@code postProcessBeforeInitialization} methods of BeanPostProcessors
- * 10. InitializingBean's {@code afterPropertiesSet}
- * 11. a custom init-method definition
- * 12. {@code postProcessAfterInitialization} methods of BeanPostProcessors
+ * as far as possible. The full set of initialization methods and their standard order is:
+ *
+ * BeanNameAware's {@code setBeanName}
+ * BeanClassLoaderAware's {@code setBeanClassLoader}
+ * BeanFactoryAware's {@code setBeanFactory}
+ * EnvironmentAware's {@code setEnvironment}
+ * EmbeddedValueResolverAware's {@code setEmbeddedValueResolver}
+ * ResourceLoaderAware's {@code setResourceLoader}
+ * (only applicable when running in an application context)
+ * ApplicationEventPublisherAware's {@code setApplicationEventPublisher}
+ * (only applicable when running in an application context)
+ * MessageSourceAware's {@code setMessageSource}
+ * (only applicable when running in an application context)
+ * ApplicationContextAware's {@code setApplicationContext}
+ * (only applicable when running in an application context)
+ * ServletContextAware's {@code setServletContext}
+ * (only applicable when running in a web application context)
+ * {@code postProcessBeforeInitialization} methods of BeanPostProcessors
+ * InitializingBean's {@code afterPropertiesSet}
+ * a custom init-method definition
+ * {@code postProcessAfterInitialization} methods of BeanPostProcessors
+ *
*
- * On shutdown of a bean factory, the following lifecycle methods apply:
- * 1. DisposableBean's {@code destroy}
- * 2. a custom destroy-method definition
+ *
On shutdown of a bean factory, the following lifecycle methods apply:
+ *
+ * {@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors
+ * DisposableBean's {@code destroy}
+ * a custom destroy-method definition
+ *
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -151,23 +158,6 @@ public interface BeanFactory {
*/
T getBean(String name, Class requiredType) throws BeansException;
- /**
- * Return the bean instance that uniquely matches the given object type, if any.
- * @param requiredType type the bean must match; can be an interface or superclass.
- * {@code null} is disallowed.
- * This method goes into {@link ListableBeanFactory} by-type lookup territory
- * but may also be translated into a conventional by-name lookup based on the name
- * of the given type. For more extensive retrieval operations across sets of beans,
- * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
- * @return an instance of the single bean matching the required type
- * @throws NoSuchBeanDefinitionException if no bean of the given type was found
- * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
- * @throws BeansException if the bean could not be created
- * @since 3.0
- * @see ListableBeanFactory
- */
- T getBean(Class requiredType) throws BeansException;
-
/**
* Return an instance, which may be shared or independent, of the specified bean.
* Allows for specifying explicit constructor arguments / factory method arguments,
@@ -184,6 +174,23 @@ public interface BeanFactory {
*/
Object getBean(String name, Object... args) throws BeansException;
+ /**
+ * Return the bean instance that uniquely matches the given object type, if any.
+ *
This method goes into {@link ListableBeanFactory} by-type lookup territory
+ * but may also be translated into a conventional by-name lookup based on the name
+ * of the given type. For more extensive retrieval operations across sets of beans,
+ * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
+ * @param requiredType type the bean must match; can be an interface or superclass.
+ * {@code null} is disallowed.
+ * @return an instance of the single bean matching the required type
+ * @throws NoSuchBeanDefinitionException if no bean of the given type was found
+ * @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
+ * @throws BeansException if the bean could not be created
+ * @since 3.0
+ * @see ListableBeanFactory
+ */
+ T getBean(Class requiredType) throws BeansException;
+
/**
* Return an instance, which may be shared or independent, of the specified bean.
* Allows for specifying explicit constructor arguments / factory method arguments,
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java
index 42b749e6fa..7a67710ea9 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -147,14 +147,7 @@ public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lb
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
String[] parentResult = beanNamesForTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type);
- List resultList = new ArrayList();
- resultList.addAll(Arrays.asList(result));
- for (String beanName : parentResult) {
- if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
- resultList.add(beanName);
- }
- }
- result = StringUtils.toStringArray(resultList);
+ result = mergeNamesWithParent(result, parentResult, hbf);
}
}
return result;
@@ -180,14 +173,7 @@ public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lb
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
String[] parentResult = beanNamesForTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type);
- List resultList = new ArrayList();
- resultList.addAll(Arrays.asList(result));
- for (String beanName : parentResult) {
- if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
- resultList.add(beanName);
- }
- }
- result = StringUtils.toStringArray(resultList);
+ result = mergeNamesWithParent(result, parentResult, hbf);
}
}
return result;
@@ -223,14 +209,7 @@ public static String[] beanNamesForTypeIncludingAncestors(
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
String[] parentResult = beanNamesForTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
- List resultList = new ArrayList();
- resultList.addAll(Arrays.asList(result));
- for (String beanName : parentResult) {
- if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
- resultList.add(beanName);
- }
- }
- result = StringUtils.toStringArray(resultList);
+ result = mergeNamesWithParent(result, parentResult, hbf);
}
}
return result;
@@ -446,6 +425,29 @@ public static T beanOfType(
return uniqueBean(type, beansOfType);
}
+
+ /**
+ * Merge the given bean names result with the given parent result.
+ * @param result the local bean name result
+ * @param parentResult the parent bean name result (possibly empty)
+ * @param hbf the local bean factory
+ * @return the merged result (possibly the local result as-is)
+ * @since 4.3.15
+ */
+ private static String[] mergeNamesWithParent(String[] result, String[] parentResult, HierarchicalBeanFactory hbf) {
+ if (parentResult.length == 0) {
+ return result;
+ }
+ List merged = new ArrayList(result.length + parentResult.length);
+ merged.addAll(Arrays.asList(result));
+ for (String beanName : parentResult) {
+ if (!merged.contains(beanName) && !hbf.containsLocalBean(beanName)) {
+ merged.add(beanName);
+ }
+ }
+ return StringUtils.toStringArray(merged);
+ }
+
/**
* Extract a unique bean for the given type from the given Map of matching beans.
* @param type type of bean to match
@@ -455,11 +457,11 @@ public static T beanOfType(
* @throws NoUniqueBeanDefinitionException if more than one bean of the given type was found
*/
private static T uniqueBean(Class type, Map matchingBeans) {
- int nrFound = matchingBeans.size();
- if (nrFound == 1) {
+ int count = matchingBeans.size();
+ if (count == 1) {
return matchingBeans.values().iterator().next();
}
- else if (nrFound > 1) {
+ else if (count > 1) {
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
}
else {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java
index bd6d33fbc4..1cb0b75830 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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,6 +17,7 @@
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
+import org.springframework.util.ClassUtils;
/**
* Thrown when a bean doesn't match the expected type.
@@ -45,8 +46,8 @@ public class BeanNotOfRequiredTypeException extends BeansException {
* the expected type
*/
public BeanNotOfRequiredTypeException(String beanName, Class> requiredType, Class> actualType) {
- super("Bean named '" + beanName + "' must be of type [" + requiredType.getName() +
- "], but was actually of type [" + actualType.getName() + "]");
+ super("Bean named '" + beanName + "' is expected to be of type '" + ClassUtils.getQualifiedName(requiredType) +
+ "' but was actually of type '" + ClassUtils.getQualifiedName(actualType) + "'");
this.beanName = beanName;
this.requiredType = requiredType;
this.actualType = actualType;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/CannotLoadBeanClassException.java b/spring-beans/src/main/java/org/springframework/beans/factory/CannotLoadBeanClassException.java
index 772da666e2..17b8f16296 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/CannotLoadBeanClassException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/CannotLoadBeanClassException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -46,8 +46,8 @@ public class CannotLoadBeanClassException extends FatalBeanException {
public CannotLoadBeanClassException(
String resourceDescription, String beanName, String beanClassName, ClassNotFoundException cause) {
- super("Cannot find class [" + beanClassName + "] for bean with name '" + beanName +
- "' defined in " + resourceDescription, cause);
+ super("Cannot find class [" + beanClassName + "] for bean with name '" + beanName + "'" +
+ (resourceDescription != null ? " defined in " + resourceDescription : ""), cause);
this.resourceDescription = resourceDescription;
this.beanName = beanName;
this.beanClassName = beanClassName;
@@ -64,8 +64,9 @@ public CannotLoadBeanClassException(
public CannotLoadBeanClassException(
String resourceDescription, String beanName, String beanClassName, LinkageError cause) {
- super("Error loading class [" + beanClassName + "] for bean with name '" + beanName +
- "' defined in " + resourceDescription + ": problem with class file or dependent class", cause);
+ super("Error loading class [" + beanClassName + "] for bean with name '" + beanName + "'" +
+ (resourceDescription != null ? " defined in " + resourceDescription : "") +
+ ": problem with class file or dependent class", cause);
this.resourceDescription = resourceDescription;
this.beanName = beanName;
this.beanClassName = beanClassName;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/DisposableBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/DisposableBean.java
index 5554afc2b3..d92f7ed6a2 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/DisposableBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/DisposableBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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,27 +17,29 @@
package org.springframework.beans.factory;
/**
- * Interface to be implemented by beans that want to release resources
- * on destruction. A BeanFactory is supposed to invoke the destroy
- * method if it disposes a cached singleton. An application context
- * is supposed to dispose all of its singletons on close.
+ * Interface to be implemented by beans that want to release resources on destruction.
+ * A {@link BeanFactory} will invoke the destroy method on individual destruction of a
+ * scoped bean. An {@link org.springframework.context.ApplicationContext} is supposed
+ * to dispose all of its singletons on shutdown, driven by the application lifecycle.
*
- * An alternative to implementing DisposableBean is specifying a custom
- * destroy-method, for example in an XML bean definition.
- * For a list of all bean lifecycle methods, see the BeanFactory javadocs.
+ *
A Spring-managed bean may also implement Java's {@link AutoCloseable} interface
+ * for the same purpose. An alternative to implementing an interface is specifying a
+ * custom destroy method, for example in an XML bean definition. For a list of all
+ * bean lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}.
*
* @author Juergen Hoeller
* @since 12.08.2003
- * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName
- * @see org.springframework.context.ConfigurableApplicationContext#close
+ * @see InitializingBean
+ * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName()
+ * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons()
+ * @see org.springframework.context.ConfigurableApplicationContext#close()
*/
public interface DisposableBean {
/**
- * Invoked by a BeanFactory on destruction of a singleton.
- * @throws Exception in case of shutdown errors.
- * Exceptions will get logged but not rethrown to allow
- * other beans to release their resources too.
+ * Invoked by the containing {@code BeanFactory} on destruction of a bean.
+ * @throws Exception in case of shutdown errors. Exceptions will get logged
+ * but not rethrown to allow other beans to release their resources as well.
*/
void destroy() throws Exception;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java
index 84763c37f6..ab465931ab 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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,31 +17,34 @@
package org.springframework.beans.factory;
/**
- * Interface to be implemented by objects used within a {@link BeanFactory}
- * which are themselves factories. If a bean implements this interface,
- * it is used as a factory for an object to expose, not directly as a bean
- * instance that will be exposed itself.
+ * Interface to be implemented by objects used within a {@link BeanFactory} which
+ * are themselves factories for individual objects. If a bean implements this
+ * interface, it is used as a factory for an object to expose, not directly as a
+ * bean instance that will be exposed itself.
*
- *
NB: A bean that implements this interface cannot be used as a
- * normal bean. A FactoryBean is defined in a bean style, but the
- * object exposed for bean references ({@link #getObject()} is always
- * the object that it creates.
+ *
NB: A bean that implements this interface cannot be used as a normal bean.
+ * A FactoryBean is defined in a bean style, but the object exposed for bean
+ * references ({@link #getObject()}) is always the object that it creates.
*
- *
FactoryBeans can support singletons and prototypes, and can
- * either create objects lazily on demand or eagerly on startup.
- * The {@link SmartFactoryBean} interface allows for exposing
- * more fine-grained behavioral metadata.
+ *
FactoryBeans can support singletons and prototypes, and can either create
+ * objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
+ * interface allows for exposing more fine-grained behavioral metadata.
*
- *
This interface is heavily used within the framework itself, for
- * example for the AOP {@link org.springframework.aop.framework.ProxyFactoryBean}
- * or the {@link org.springframework.jndi.JndiObjectFactoryBean}.
- * It can be used for application components as well; however,
- * this is not common outside of infrastructure code.
+ *
This interface is heavily used within the framework itself, for example for
+ * the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
+ * {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
+ * custom components as well; however, this is only common for infrastructure code.
*
- *
NOTE: FactoryBean objects participate in the containing
- * BeanFactory's synchronization of bean creation. There is usually no
- * need for internal synchronization other than for purposes of lazy
- * initialization within the FactoryBean itself (or the like).
+ *
{@code FactoryBean} is a programmatic contract. Implementations are not
+ * supposed to rely on annotation-driven injection or other reflective facilities.
+ * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in
+ * the bootstrap process, even ahead of any post-processor setup. If you need access
+ * other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
+ *
+ *
Finally, FactoryBean objects participate in the containing BeanFactory's
+ * synchronization of bean creation. There is usually no need for internal
+ * synchronization other than for purposes of lazy initialization within the
+ * FactoryBean itself (or the like).
*
* @author Rod Johnson
* @author Juergen Hoeller
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/InitializingBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/InitializingBean.java
index 365bf99bf1..b0ca094510 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/InitializingBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/InitializingBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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,31 +17,29 @@
package org.springframework.beans.factory;
/**
- * Interface to be implemented by beans that need to react once all their
- * properties have been set by a BeanFactory: for example, to perform custom
- * initialization, or merely to check that all mandatory properties have been set.
+ * Interface to be implemented by beans that need to react once all their properties
+ * have been set by a {@link BeanFactory}: e.g. to perform custom initialization,
+ * or merely to check that all mandatory properties have been set.
*
- *
An alternative to implementing InitializingBean is specifying a custom
- * init-method, for example in an XML bean definition.
- * For a list of all bean lifecycle methods, see the BeanFactory javadocs.
+ *
An alternative to implementing {@code InitializingBean} is specifying a custom
+ * init method, for example in an XML bean definition. For a list of all bean
+ * lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}.
*
* @author Rod Johnson
- * @see BeanNameAware
- * @see BeanFactoryAware
- * @see BeanFactory
- * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
- * @see org.springframework.context.ApplicationContextAware
+ * @author Juergen Hoeller
+ * @see DisposableBean
+ * @see org.springframework.beans.factory.config.BeanDefinition#getPropertyValues()
+ * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getInitMethodName()
*/
public interface InitializingBean {
/**
- * Invoked by a BeanFactory after it has set all bean properties supplied
- * (and satisfied BeanFactoryAware and ApplicationContextAware).
- *
This method allows the bean instance to perform initialization only
- * possible when all bean properties have been set and to throw an
- * exception in the event of misconfiguration.
- * @throws Exception in the event of misconfiguration (such
- * as failure to set an essential property) or if initialization fails.
+ * Invoked by the containing {@code BeanFactory} after it has set all bean properties
+ * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
+ *
This method allows the bean instance to perform validation of its overall
+ * configuration and final initialization when all bean properties have been set.
+ * @throws Exception in the event of misconfiguration (such as failure to set an
+ * essential property) or if initialization fails for any other reason
*/
void afterPropertiesSet() throws Exception;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java b/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java
index 0a3d55a931..38ace23fd4 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -111,6 +111,17 @@ public Annotation[] getAnnotations() {
}
}
+ /**
+ * Retrieve a field/parameter annotation of the given type, if any.
+ * @param annotationType the annotation type to retrieve
+ * @return the annotation instance, or {@code null} if none found
+ * @since 4.3.9
+ */
+ public A getAnnotation(Class annotationType) {
+ return (this.field != null ? this.field.getAnnotation(annotationType) :
+ this.methodParameter.getParameterAnnotation(annotationType));
+ }
+
/**
* Return the type declared by the underlying field or method/constructor parameter,
* indicating the injection type.
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java
index e1e5ed33f5..9f2453ff65 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/NamedBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2006 the original author or authors.
+ * Copyright 2002-2016 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,10 +17,10 @@
package org.springframework.beans.factory;
/**
- * Counterpart of BeanNameAware. Returns the bean name of an object.
+ * Counterpart of {@link BeanNameAware}. Returns the bean name of an object.
*
- * This interface can be introduced to avoid a brittle dependence
- * on bean name in objects used with Spring IoC and Spring AOP.
+ *
This interface can be introduced to avoid a brittle dependence on
+ * bean name in objects used with Spring IoC and Spring AOP.
*
* @author Rod Johnson
* @since 2.0
@@ -29,7 +29,7 @@
public interface NamedBean {
/**
- * Return the name of this bean in a Spring bean factory.
+ * Return the name of this bean in a Spring bean factory, if known.
*/
String getBeanName();
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java
index 106fa0cafc..c02210581b 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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,6 +17,8 @@
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
+import org.springframework.core.ResolvableType;
+import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
@@ -26,6 +28,7 @@
*
* @author Rod Johnson
* @author Juergen Hoeller
+ * @author Stephane Nicoll
* @see BeanFactory#getBean(String)
* @see BeanFactory#getBean(Class)
* @see NoUniqueBeanDefinitionException
@@ -33,11 +36,9 @@
@SuppressWarnings("serial")
public class NoSuchBeanDefinitionException extends BeansException {
- /** Name of the missing bean */
private String beanName;
- /** Required type of the missing bean */
- private Class> beanType;
+ private ResolvableType resolvableType;
/**
@@ -45,7 +46,7 @@ public class NoSuchBeanDefinitionException extends BeansException {
* @param name the name of the missing bean
*/
public NoSuchBeanDefinitionException(String name) {
- super("No bean named '" + name + "' is defined");
+ super("No bean named '" + name + "' available");
this.beanName = name;
}
@@ -55,7 +56,7 @@ public NoSuchBeanDefinitionException(String name) {
* @param message detailed message describing the problem
*/
public NoSuchBeanDefinitionException(String name, String message) {
- super("No bean named '" + name + "' is defined: " + message);
+ super("No bean named '" + name + "' available: " + message);
this.beanName = name;
}
@@ -64,8 +65,7 @@ public NoSuchBeanDefinitionException(String name, String message) {
* @param type required type of the missing bean
*/
public NoSuchBeanDefinitionException(Class> type) {
- super("No qualifying bean of type [" + type.getName() + "] is defined");
- this.beanType = type;
+ this(ResolvableType.forClass(type));
}
/**
@@ -74,8 +74,28 @@ public NoSuchBeanDefinitionException(Class> type) {
* @param message detailed message describing the problem
*/
public NoSuchBeanDefinitionException(Class> type, String message) {
- super("No qualifying bean of type [" + type.getName() + "] is defined: " + message);
- this.beanType = type;
+ this(ResolvableType.forClass(type), message);
+ }
+
+ /**
+ * Create a new {@code NoSuchBeanDefinitionException}.
+ * @param type full type declaration of the missing bean
+ * @since 4.3.4
+ */
+ public NoSuchBeanDefinitionException(ResolvableType type) {
+ super("No qualifying bean of type '" + type + "' available");
+ this.resolvableType = type;
+ }
+
+ /**
+ * Create a new {@code NoSuchBeanDefinitionException}.
+ * @param type full type declaration of the missing bean
+ * @param message detailed message describing the problem
+ * @since 4.3.4
+ */
+ public NoSuchBeanDefinitionException(ResolvableType type, String message) {
+ super("No qualifying bean of type '" + type + "' available: " + message);
+ this.resolvableType = type;
}
/**
@@ -83,12 +103,15 @@ public NoSuchBeanDefinitionException(Class> type, String message) {
* @param type required type of the missing bean
* @param dependencyDescription a description of the originating dependency
* @param message detailed message describing the problem
+ * @deprecated as of 4.3.4, in favor of {@link #NoSuchBeanDefinitionException(ResolvableType, String)}
*/
+ @Deprecated
public NoSuchBeanDefinitionException(Class> type, String dependencyDescription, String message) {
- super("No qualifying bean of type [" + type.getName() + "] found for dependency" +
+ super("No qualifying bean" + (!StringUtils.hasLength(dependencyDescription) ?
+ " of type '" + ClassUtils.getQualifiedName(type) + "'" : "") + " found for dependency" +
(StringUtils.hasLength(dependencyDescription) ? " [" + dependencyDescription + "]" : "") +
": " + message);
- this.beanType = type;
+ this.resolvableType = ResolvableType.forClass(type);
}
@@ -100,10 +123,20 @@ public String getBeanName() {
}
/**
- * Return the required type of the missing bean, if it was a lookup by type that failed.
+ * Return the required type of the missing bean, if it was a lookup by type
+ * that failed.
*/
public Class> getBeanType() {
- return this.beanType;
+ return (this.resolvableType != null ? this.resolvableType.resolve() : null);
+ }
+
+ /**
+ * Return the required {@link ResolvableType} of the missing bean, if it was a lookup
+ * by type that failed.
+ * @since 4.3.4
+ */
+ public ResolvableType getResolvableType() {
+ return this.resolvableType;
}
/**
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectFactory.java
index 40f807dd54..9b92362485 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -40,7 +40,7 @@ public interface ObjectFactory {
/**
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
- * @return an instance of the bean (should never be {@code null})
+ * @return the resulting instance
* @throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java
index 0403abfc7a..666415dc64 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java
@@ -18,6 +18,7 @@
import org.springframework.beans.BeansException;
import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
/**
* Exception thrown when a bean depends on other beans or simple properties
@@ -46,7 +47,7 @@ public UnsatisfiedDependencyException(
super(resourceDescription, beanName,
"Unsatisfied dependency expressed through bean property '" + propertyName + "'" +
- (msg != null ? ": " + msg : ""));
+ (StringUtils.hasLength(msg) ? ": " + msg : ""));
}
/**
@@ -59,7 +60,7 @@ public UnsatisfiedDependencyException(
public UnsatisfiedDependencyException(
String resourceDescription, String beanName, String propertyName, BeansException ex) {
- this(resourceDescription, beanName, propertyName, (ex != null ? ex.getMessage() : ""));
+ this(resourceDescription, beanName, propertyName, "");
initCause(ex);
}
@@ -74,7 +75,9 @@ public UnsatisfiedDependencyException(
public UnsatisfiedDependencyException(
String resourceDescription, String beanName, InjectionPoint injectionPoint, String msg) {
- super(resourceDescription, beanName, "Unsatisfied dependency expressed through " + injectionPoint + ": " + msg);
+ super(resourceDescription, beanName,
+ "Unsatisfied dependency expressed through " + injectionPoint +
+ (StringUtils.hasLength(msg) ? ": " + msg : ""));
this.injectionPoint = injectionPoint;
}
@@ -89,7 +92,7 @@ public UnsatisfiedDependencyException(
public UnsatisfiedDependencyException(
String resourceDescription, String beanName, InjectionPoint injectionPoint, BeansException ex) {
- this(resourceDescription, beanName, injectionPoint, (ex != null ? ex.getMessage() : ""));
+ this(resourceDescription, beanName, injectionPoint, "");
initCause(ex);
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java
index 73779365e0..7df2fe0f6a 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -23,29 +23,40 @@
import java.lang.annotation.Target;
/**
- * Marks a constructor, field, setter method or config method as to be
- * autowired by Spring's dependency injection facilities.
+ * Marks a constructor, field, setter method or config method as to be autowired by
+ * Spring's dependency injection facilities. This is an alternative to the JSR-330
+ * {@link javax.inject.Inject} annotation, adding required-vs-optional semantics.
*
- * Only one constructor (at max) of any given bean class may carry this
- * annotation, indicating the constructor to autowire when used as a Spring
- * bean. Such a constructor does not have to be public.
+ *
Only one constructor (at max) of any given bean class may declare this annotation
+ * with the 'required' parameter set to {@code true}, indicating the constructor
+ * to autowire when used as a Spring bean. If multiple non-required constructors
+ * declare the annotation, they will be considered as candidates for autowiring.
+ * The constructor with the greatest number of dependencies that can be satisfied by
+ * matching beans in the Spring container will be chosen. If none of the candidates
+ * can be satisfied, then a standard default constructor (if present) will be used.
+ * If a class only declares a single constructor to begin with, it will always be used,
+ * even if not annotated. An annotated constructor does not have to be public.
*
- *
Fields are injected right after construction of a bean, before any
- * config methods are invoked. Such a config field does not have to be public.
+ *
Fields are injected right after construction of a bean, before any config methods
+ * are invoked. Such a config field does not have to be public.
*
- *
Config methods may have an arbitrary name and any number of arguments;
- * each of those arguments will be autowired with a matching bean in the
- * Spring container. Bean property setter methods are effectively just
- * a special case of such a general config method. Such config methods
- * do not have to be public.
+ *
Config methods may have an arbitrary name and any number of arguments; each of
+ * those arguments will be autowired with a matching bean in the Spring container.
+ * Bean property setter methods are effectively just a special case of such a general
+ * config method. Such config methods do not have to be public.
*
- *
In the case of multiple argument methods, the 'required' parameter is
- * applicable for all arguments.
+ *
In the case of a multi-arg constructor or method, the 'required' parameter is
+ * applicable to all arguments. Individual parameters may be declared as Java-8-style
+ * {@link java.util.Optional}, overriding the base required semantics.
*
- *
In case of a {@link java.util.Collection} or {@link java.util.Map}
- * dependency type, the container will autowire all beans matching the
- * declared value type. In case of a Map, the keys must be declared as
- * type String and will be resolved to the corresponding bean names.
+ *
In case of a {@link java.util.Collection} or {@link java.util.Map} dependency type,
+ * the container autowires all beans matching the declared value type. For such purposes,
+ * the map keys must be declared as type String which will be resolved to the corresponding
+ * bean names. Such a container-provided collection will be ordered, taking into account
+ * {@link org.springframework.core.Ordered}/{@link org.springframework.core.annotation.Order}
+ * values of the target components, otherwise following their registration order in the
+ * container. Alternatively, a single matching target bean may also be a generally typed
+ * {@code Collection} or {@code Map} itself, getting injected as such.
*
*
Note that actual injection is performed through a
* {@link org.springframework.beans.factory.config.BeanPostProcessor
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
index 3848510f98..b0e8c609cc 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -74,15 +74,15 @@
*
Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation,
* if available, as a direct alternative to Spring's own {@code @Autowired}.
*
- *
Only one constructor (at max) of any given bean class may carry this
- * annotation with the 'required' parameter set to {@code true},
- * indicating the constructor to autowire when used as a Spring bean.
- * If multiple non-required constructors carry the annotation, they
- * will be considered as candidates for autowiring. The constructor with
- * the greatest number of dependencies that can be satisfied by matching
- * beans in the Spring container will be chosen. If none of the candidates
- * can be satisfied, then a default constructor (if present) will be used.
- * An annotated constructor does not have to be public.
+ *
Only one constructor (at max) of any given bean class may declare this annotation
+ * with the 'required' parameter set to {@code true}, indicating the constructor
+ * to autowire when used as a Spring bean. If multiple non-required constructors
+ * declare the annotation, they will be considered as candidates for autowiring.
+ * The constructor with the greatest number of dependencies that can be satisfied by
+ * matching beans in the Spring container will be chosen. If none of the candidates
+ * can be satisfied, then a standard default constructor (if present) will be used.
+ * If a class only declares a single constructor to begin with, it will always be used,
+ * even if not annotated. An annotated constructor does not have to be public.
*
*
Fields are injected right after construction of a bean, before any
* config methods are invoked. Such a config field does not have to be public.
@@ -120,7 +120,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
protected final Log logger = LogFactory.getLog(getClass());
private final Set> autowiredAnnotationTypes =
- new LinkedHashSet>();
+ new LinkedHashSet>(4);
private String requiredParameterName = "required";
@@ -163,11 +163,11 @@ public AutowiredAnnotationBeanPostProcessor() {
/**
* Set the 'autowired' annotation type, to be used on constructors, fields,
* setter methods and arbitrary config methods.
- * The default autowired annotation type is the Spring-provided
- * {@link Autowired} annotation, as well as {@link Value}.
+ *
The default autowired annotation type is the Spring-provided {@link Autowired}
+ * annotation, as well as {@link Value}.
*
This setter property exists so that developers can provide their own
- * (non-Spring-specific) annotation type to indicate that a member is
- * supposed to be autowired.
+ * (non-Spring-specific) annotation type to indicate that a member is supposed
+ * to be autowired.
*/
public void setAutowiredAnnotationType(Class extends Annotation> autowiredAnnotationType) {
Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null");
@@ -178,11 +178,11 @@ public void setAutowiredAnnotationType(Class extends Annotation> autowiredAnno
/**
* Set the 'autowired' annotation types, to be used on constructors, fields,
* setter methods and arbitrary config methods.
- *
The default autowired annotation type is the Spring-provided
- * {@link Autowired} annotation, as well as {@link Value}.
+ *
The default autowired annotation type is the Spring-provided {@link Autowired}
+ * annotation, as well as {@link Value}.
*
This setter property exists so that developers can provide their own
- * (non-Spring-specific) annotation types to indicate that a member is
- * supposed to be autowired.
+ * (non-Spring-specific) annotation types to indicate that a member is supposed
+ * to be autowired.
*/
public void setAutowiredAnnotationTypes(Set> autowiredAnnotationTypes) {
Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty");
@@ -191,8 +191,7 @@ public void setAutowiredAnnotationTypes(Set> autowir
}
/**
- * Set the name of a parameter of the annotation that specifies
- * whether it is required.
+ * Set the name of a parameter of the annotation that specifies whether it is required.
* @see #setRequiredParameterValue(boolean)
*/
public void setRequiredParameterName(String requiredParameterName) {
@@ -201,9 +200,8 @@ public void setRequiredParameterName(String requiredParameterName) {
/**
* Set the boolean value that marks a dependency as required
- * For example if using 'required=true' (the default),
- * this value should be {@code true}; but if using
- * 'optional=false', this value should be {@code false}.
+ *
For example if using 'required=true' (the default), this value should be
+ * {@code true}; but if using 'optional=false', this value should be {@code false}.
* @see #setRequiredParameterName(String)
*/
public void setRequiredParameterValue(boolean requiredParameterValue) {
@@ -220,10 +218,10 @@ public int getOrder() {
}
@Override
- public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException(
- "AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory");
+ "AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
}
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
@@ -238,35 +236,56 @@ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, C
}
@Override
- public Constructor>[] determineCandidateConstructors(Class> beanClass, final String beanName) throws BeansException {
+ public Constructor>[] determineCandidateConstructors(Class> beanClass, final String beanName)
+ throws BeanCreationException {
+
+ // Let's check for lookup methods here..
if (!this.lookupMethodsChecked.contains(beanName)) {
- ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
- @Override
- public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
- Lookup lookup = method.getAnnotation(Lookup.class);
- if (lookup != null) {
- LookupOverride override = new LookupOverride(method, lookup.value());
- try {
- RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName);
- mbd.getMethodOverrides().addOverride(override);
- }
- catch (NoSuchBeanDefinitionException ex) {
- throw new BeanCreationException(beanName,
- "Cannot apply @Lookup to beans without corresponding bean definition");
+ try {
+ ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
+ @Override
+ public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
+ Lookup lookup = method.getAnnotation(Lookup.class);
+ if (lookup != null) {
+ LookupOverride override = new LookupOverride(method, lookup.value());
+ try {
+ RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName);
+ mbd.getMethodOverrides().addOverride(override);
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ throw new BeanCreationException(beanName,
+ "Cannot apply @Lookup to beans without corresponding bean definition");
+ }
}
}
- }
- });
+ });
+ }
+ catch (IllegalStateException ex) {
+ throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
+ }
+ catch (NoClassDefFoundError err) {
+ throw new BeanCreationException(beanName, "Failed to introspect bean class [" + beanClass.getName() +
+ "] for lookup method metadata: could not find class that it depends on", err);
+ }
this.lookupMethodsChecked.add(beanName);
}
// Quick check on the concurrent map first, with minimal locking.
Constructor>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
+ // Fully synchronized resolution now...
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
- Constructor>[] rawCandidates = beanClass.getDeclaredConstructors();
+ Constructor>[] rawCandidates;
+ try {
+ rawCandidates = beanClass.getDeclaredConstructors();
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(beanName,
+ "Resolution of declared constructors on bean Class [" + beanClass.getName() +
+ "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
+ }
List> candidates = new ArrayList>(rawCandidates.length);
Constructor> requiredConstructor = null;
Constructor> defaultConstructor = null;
@@ -292,10 +311,6 @@ public void doWith(Method method) throws IllegalArgumentException, IllegalAccess
". Found constructor with 'required' Autowired annotation already: " +
requiredConstructor);
}
- if (candidate.getParameterTypes().length == 0) {
- throw new IllegalStateException(
- "Autowired annotation requires at least one argument: " + candidate);
- }
boolean required = determineRequiredStatus(ann);
if (required) {
if (!candidates.isEmpty()) {
@@ -320,9 +335,9 @@ else if (candidate.getParameterTypes().length == 0) {
}
else if (candidates.size() == 1 && logger.isWarnEnabled()) {
logger.warn("Inconsistent constructor declaration on bean with name '" + beanName +
- "': single autowire-marked constructor flagged as optional - this constructor " +
- "is effectively required since there is no default constructor to fall back to: " +
- candidates.get(0));
+ "': single autowire-marked constructor flagged as optional - " +
+ "this constructor is effectively required since there is no " +
+ "default constructor to fall back to: " + candidates.get(0));
}
}
candidateConstructors = candidates.toArray(new Constructor>[candidates.size()]);
@@ -342,7 +357,7 @@ else if (rawCandidates.length == 1 && rawCandidates[0].getParameterTypes().lengt
@Override
public PropertyValues postProcessPropertyValues(
- PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
+ PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
@@ -361,9 +376,9 @@ public PropertyValues postProcessPropertyValues(
* 'Native' processing method for direct calls with an arbitrary target instance,
* resolving all of its fields and methods which are annotated with {@code @Autowired}.
* @param bean the target instance to process
- * @throws BeansException if autowiring failed
+ * @throws BeanCreationException if autowiring failed
*/
- public void processInjection(Object bean) throws BeansException {
+ public void processInjection(Object bean) throws BeanCreationException {
Class> clazz = bean.getClass();
InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null);
try {
@@ -373,7 +388,8 @@ public void processInjection(Object bean) throws BeansException {
throw ex;
}
catch (Throwable ex) {
- throw new BeanCreationException("Injection of autowired dependencies failed for class [" + clazz + "]", ex);
+ throw new BeanCreationException(
+ "Injection of autowired dependencies failed for class [" + clazz + "]", ex);
}
}
@@ -446,7 +462,8 @@ public void doWith(Method method) throws IllegalArgumentException, IllegalAccess
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
- logger.warn("Autowired annotation should be used on methods with parameters: " + method);
+ logger.warn("Autowired annotation should only be used on methods with parameters: " +
+ method);
}
}
boolean required = determineRequiredStatus(ann);
@@ -465,7 +482,7 @@ public void doWith(Method method) throws IllegalArgumentException, IllegalAccess
}
private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
- if (ao.getAnnotations().length > 0) {
+ if (ao.getAnnotations().length > 0) { // autowiring annotations have to be local
for (Class extends Annotation> type : this.autowiredAnnotationTypes) {
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
if (attributes != null) {
@@ -575,10 +592,10 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
- if (beanFactory.containsBean(autowiredBeanName)) {
- if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
- this.cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);
- }
+ if (beanFactory.containsBean(autowiredBeanName) &&
+ beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
+ this.cachedFieldValue = new ShortcutDependencyDescriptor(
+ desc, autowiredBeanName, field.getType());
}
}
}
@@ -628,7 +645,7 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
Class>[] paramTypes = method.getParameterTypes();
arguments = new Object[paramTypes.length];
DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];
- Set autowiredBeanNames = new LinkedHashSet(paramTypes.length);
+ Set autowiredBeans = new LinkedHashSet(paramTypes.length);
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
@@ -636,7 +653,7 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
- Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter);
+ Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
@@ -654,15 +671,15 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
for (int i = 0; i < arguments.length; i++) {
this.cachedMethodArguments[i] = descriptors[i];
}
- registerDependentBeans(beanName, autowiredBeanNames);
- if (autowiredBeanNames.size() == paramTypes.length) {
- Iterator it = autowiredBeanNames.iterator();
+ registerDependentBeans(beanName, autowiredBeans);
+ if (autowiredBeans.size() == paramTypes.length) {
+ Iterator it = autowiredBeans.iterator();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName)) {
if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
- this.cachedMethodArguments[i] =
- new ShortcutDependencyDescriptor(descriptors[i], autowiredBeanName);
+ this.cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
+ descriptors[i], autowiredBeanName, paramTypes[i]);
}
}
}
@@ -705,16 +722,19 @@ private Object[] resolveCachedArguments(String beanName) {
@SuppressWarnings("serial")
private static class ShortcutDependencyDescriptor extends DependencyDescriptor {
- private final String shortcutBeanName;
+ private final String shortcut;
+
+ private final Class> requiredType;
- public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcutBeanName) {
+ public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class> requiredType) {
super(original);
- this.shortcutBeanName = shortcutBeanName;
+ this.shortcut = shortcut;
+ this.requiredType = requiredType;
}
@Override
public Object resolveShortcut(BeanFactory beanFactory) {
- return resolveCandidate(this.shortcutBeanName, beanFactory);
+ return resolveCandidate(this.shortcut, this.requiredType, beanFactory);
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java
index 6590100488..acc87d6495 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -18,27 +18,30 @@
import java.lang.reflect.Method;
+import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Convenience methods performing bean lookups related to annotations, for example
* Spring's {@link Qualifier @Qualifier} annotation.
*
- * @author Chris Beams
* @author Juergen Hoeller
+ * @author Chris Beams
* @since 3.1.2
* @see BeanFactoryUtils
*/
-public class BeanFactoryAnnotationUtils {
+public abstract class BeanFactoryAnnotationUtils {
/**
* Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a
@@ -48,9 +51,16 @@ public class BeanFactoryAnnotationUtils {
* @param beanType the type of bean to retrieve
* @param qualifier the qualifier for selecting between multiple bean matches
* @return the matching bean of type {@code T} (never {@code null})
+ * @throws NoUniqueBeanDefinitionException if multiple matching beans of type {@code T} found
* @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found
+ * @throws BeansException if the bean could not be created
+ * @see BeanFactory#getBean(Class)
*/
- public static T qualifiedBeanOfType(BeanFactory beanFactory, Class beanType, String qualifier) {
+ public static T qualifiedBeanOfType(BeanFactory beanFactory, Class beanType, String qualifier)
+ throws BeansException {
+
+ Assert.notNull(beanFactory, "BeanFactory must not be null");
+
if (beanFactory instanceof ConfigurableListableBeanFactory) {
// Full qualifier matching supported.
return qualifiedBeanOfType((ConfigurableListableBeanFactory) beanFactory, beanType, qualifier);
@@ -74,7 +84,6 @@ else if (beanFactory.containsBean(qualifier)) {
* @param beanType the type of bean to retrieve
* @param qualifier the qualifier for selecting between multiple bean matches
* @return the matching bean of type {@code T} (never {@code null})
- * @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found
*/
private static T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Class beanType, String qualifier) {
String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(bf, beanType);
@@ -82,8 +91,7 @@ private static T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Cla
for (String beanName : candidateBeans) {
if (isQualifierMatch(qualifier, beanName, bf)) {
if (matchingBean != null) {
- throw new NoSuchBeanDefinitionException(qualifier, "No unique " + beanType.getSimpleName() +
- " bean found for qualifier '" + qualifier + "'");
+ throw new NoUniqueBeanDefinitionException(beanType, matchingBean, beanName);
}
matchingBean = beanName;
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java
index 374332395c..2804302c22 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java
@@ -40,6 +40,7 @@
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
+import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
@@ -349,7 +350,7 @@ public LifecycleElement(Method method) {
}
this.method = method;
this.identifier = (Modifier.isPrivate(method.getModifiers()) ?
- method.getDeclaringClass() + "." + method.getName() : method.getName());
+ ClassUtils.getQualifiedMethodName(method) : method.getName());
}
public Method getMethod() {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java
index af64331ce9..6f9caba818 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InjectionMetadata.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -80,9 +80,8 @@ public void inject(Object target, String beanName, PropertyValues pvs) throws Th
Collection elementsToIterate =
(this.checkedElements != null ? this.checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
- boolean debug = logger.isDebugEnabled();
for (InjectedElement element : elementsToIterate) {
- if (debug) {
+ if (logger.isDebugEnabled()) {
logger.debug("Processing injected element of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, pvs);
@@ -109,7 +108,10 @@ public static boolean needsRefresh(InjectionMetadata metadata, Class> clazz) {
}
- public static abstract class InjectedElement {
+ /**
+ * A single injected element.
+ */
+ public abstract static class InjectedElement {
protected final Member member;
@@ -216,6 +218,7 @@ else if (pvs instanceof MutablePropertyValues) {
}
/**
+ * Clear property skipping for this element.
* @since 3.2.13
*/
protected void clearPropertySkipping(PropertyValues pvs) {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
index 0f6d10216c..8e6ecb3a5f 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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,6 +17,7 @@
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -49,6 +50,7 @@
*
* @author Mark Fisher
* @author Juergen Hoeller
+ * @author Stephane Nicoll
* @since 2.5
* @see AutowireCandidateQualifier
* @see Qualifier
@@ -225,8 +227,12 @@ protected boolean checkQualifier(
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
}
if (qualifier == null) {
- // First, check annotation on factory method, if applicable
- Annotation targetAnnotation = getFactoryMethodAnnotation(bd, type);
+ // First, check annotation on qualified element, if any
+ Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type);
+ // Then, check annotation on factory method, if applicable
+ if (targetAnnotation == null) {
+ targetAnnotation = getFactoryMethodAnnotation(bd, type);
+ }
if (targetAnnotation == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
if (dbd != null) {
@@ -291,6 +297,11 @@ protected boolean checkQualifier(
return true;
}
+ protected Annotation getQualifiedElementAnnotation(RootBeanDefinition bd, Class extends Annotation> type) {
+ AnnotatedElement qualifiedElement = bd.getQualifiedElement();
+ return (qualifiedElement != null ? AnnotationUtils.getAnnotation(qualifiedElement, type) : null);
+ }
+
protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class extends Annotation> type) {
Method resolvedFactoryMethod = bd.getResolvedFactoryMethod();
return (resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null);
@@ -298,7 +309,21 @@ protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class e
/**
- * Determine whether the given dependency carries a value annotation.
+ * Determine whether the given dependency declares an autowired annotation,
+ * checking its required flag.
+ * @see Autowired#required()
+ */
+ @Override
+ public boolean isRequired(DependencyDescriptor descriptor) {
+ if (!super.isRequired(descriptor)) {
+ return false;
+ }
+ Autowired autowired = descriptor.getAnnotation(Autowired.class);
+ return (autowired == null || autowired.required());
+ }
+
+ /**
+ * Determine whether the given dependency declares a value annotation.
* @see Value
*/
@Override
@@ -317,10 +342,12 @@ public Object getSuggestedValue(DependencyDescriptor descriptor) {
* Determine a suggested value from any of the given candidate annotations.
*/
protected Object findValue(Annotation[] annotationsToSearch) {
- AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
- AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
- if (attr != null) {
- return extractValue(attr);
+ if (annotationsToSearch.length > 0) { // qualifier annotations have to be local
+ AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
+ AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
+ if (attr != null) {
+ return extractValue(attr);
+ }
}
return null;
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java
index ebc3cbf53c..31c0352879 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -55,8 +55,8 @@
* and obviates the need (in part ) for a developer to code a method that
* simply checks that all required properties have actually been set.
*
- * Please note that an 'init' method may still need to implemented (and may
- * still be desirable), because all that this class does is enforce that a
+ *
Please note that an 'init' method may still need to be implemented (and may
+ * still be desirable), because all that this class does is enforcing that a
* 'required' property has actually been configured with a value. It does
* not check anything else... In particular, it does not check that a
* configured value is not {@code null}.
@@ -141,8 +141,7 @@ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, C
@Override
public PropertyValues postProcessPropertyValues(
- PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
- throws BeansException {
+ PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
if (!this.validatedBeanNames.contains(beanName)) {
if (!shouldSkip(this.beanFactory, beanName)) {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java
index bc8a42f71b..ab473c3121 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -33,6 +33,7 @@
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
@@ -153,7 +154,7 @@ public final T getObject() throws Exception {
}
/**
- * Determine an 'eager singleton' instance, exposed in case of a
+ * Determine an 'early singleton' instance, exposed in case of a
* circular reference. Not called in a non-circular scenario.
*/
@SuppressWarnings("unchecked")
@@ -176,9 +177,7 @@ private T getEarlySingletonInstance() throws Exception {
* @throws IllegalStateException if the singleton instance is not initialized
*/
private T getSingletonInstance() throws IllegalStateException {
- if (!this.initialized) {
- throw new IllegalStateException("Singleton instance not initialized yet");
- }
+ Assert.state(this.initialized, "Singleton instance not initialized yet");
return this.singletonInstance;
}
@@ -208,7 +207,7 @@ public void destroy() throws Exception {
*
Invoked on initialization of this FactoryBean in case of
* a singleton; else, on each {@link #getObject()} call.
* @return the object returned by this factory
- * @throws Exception if an exception occured during object creation
+ * @throws Exception if an exception occurred during object creation
* @see #getObject()
*/
protected abstract T createInstance() throws Exception;
@@ -218,7 +217,7 @@ public void destroy() throws Exception {
* FactoryBean is supposed to implement, for use with an 'early singleton
* proxy' that will be exposed in case of a circular reference.
*
The default implementation returns this FactoryBean's object type,
- * provided that it is an interface, or {@code null} else. The latter
+ * provided that it is an interface, or {@code null} otherwise. The latter
* indicates that early singleton access is not supported by this FactoryBean.
* This will lead to a FactoryBeanNotInitializedException getting thrown.
* @return the interfaces to use for 'early singletons',
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java
index 43602b48eb..61b5240770 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java
@@ -21,6 +21,8 @@
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
/**
* Extension of the {@link org.springframework.beans.factory.BeanFactory}
@@ -154,15 +156,6 @@ public interface AutowireCapableBeanFactory extends BeanFactory {
*/
Object configureBean(Object existingBean, String beanName) throws BeansException;
- /**
- * Resolve the specified dependency against the beans defined in this factory.
- * @param descriptor the descriptor for the dependency
- * @param beanName the name of the bean which declares the present dependency
- * @return the resolved object, or {@code null} if none found
- * @throws BeansException if dependency resolution failed
- */
- Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException;
-
//-------------------------------------------------------------------------
// Specialized methods for fine-grained control over the bean lifecycle
@@ -312,18 +305,55 @@ Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String be
*/
void destroyBean(Object existingBean);
+
+ //-------------------------------------------------------------------------
+ // Delegate methods for resolving injection points
+ //-------------------------------------------------------------------------
+
+ /**
+ * Resolve the bean instance that uniquely matches the given object type, if any,
+ * including its bean name.
+ *
This is effectively a variant of {@link #getBean(Class)} which preserves the
+ * bean name of the matching instance.
+ * @param requiredType type the bean must match; can be an interface or superclass.
+ * {@code null} is disallowed.
+ * @return the bean name plus bean instance
+ * @throws NoSuchBeanDefinitionException if no matching bean was found
+ * @throws NoUniqueBeanDefinitionException if more than one matching bean was found
+ * @throws BeansException if the bean could not be created
+ * @since 4.3.3
+ * @see #getBean(Class)
+ */
+ NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException;
+
+ /**
+ * Resolve the specified dependency against the beans defined in this factory.
+ * @param descriptor the descriptor for the dependency (field/method/constructor)
+ * @param requestingBeanName the name of the bean which declares the given dependency
+ * @return the resolved object, or {@code null} if none found
+ * @throws NoSuchBeanDefinitionException if no matching bean was found
+ * @throws NoUniqueBeanDefinitionException if more than one matching bean was found
+ * @throws BeansException if dependency resolution failed for any other reason
+ * @since 2.5
+ * @see #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)
+ */
+ Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName) throws BeansException;
+
/**
* Resolve the specified dependency against the beans defined in this factory.
- * @param descriptor the descriptor for the dependency
- * @param beanName the name of the bean which declares the present dependency
+ * @param descriptor the descriptor for the dependency (field/method/constructor)
+ * @param requestingBeanName the name of the bean which declares the given dependency
* @param autowiredBeanNames a Set that all names of autowired beans (used for
- * resolving the present dependency) are supposed to be added to
- * @param typeConverter the TypeConverter to use for populating arrays and
- * collections
+ * resolving the given dependency) are supposed to be added to
+ * @param typeConverter the TypeConverter to use for populating arrays and collections
* @return the resolved object, or {@code null} if none found
- * @throws BeansException if dependency resolution failed
+ * @throws NoSuchBeanDefinitionException if no matching bean was found
+ * @throws NoUniqueBeanDefinitionException if more than one matching bean was found
+ * @throws BeansException if dependency resolution failed for any other reason
+ * @since 2.5
+ * @see DependencyDescriptor
*/
- Object resolveDependency(DependencyDescriptor descriptor, String beanName,
+ Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName,
Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException;
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java
index 535d2a8e96..5ba7068f41 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanDefinition.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -79,10 +79,7 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
int ROLE_INFRASTRUCTURE = 2;
- /**
- * Return the name of the parent definition of this bean definition, if any.
- */
- String getParentName();
+ // Modifiable attributes
/**
* Set the name of the parent definition of this bean definition, if any.
@@ -90,46 +87,40 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
void setParentName(String parentName);
/**
- * Return the current bean class name of this bean definition.
- * Note that this does not have to be the actual class name used at runtime, in
- * case of a child definition overriding/inheriting the class name from its parent.
- * Hence, do not consider this to be the definitive bean type at runtime but
- * rather only use it for parsing purposes at the individual bean definition level.
+ * Return the name of the parent definition of this bean definition, if any.
*/
- String getBeanClassName();
+ String getParentName();
/**
- * Override the bean class name of this bean definition.
+ * Specify the bean class name of this bean definition.
*
The class name can be modified during bean factory post-processing,
* typically replacing the original class name with a parsed variant of it.
+ * @see #setParentName
+ * @see #setFactoryBeanName
+ * @see #setFactoryMethodName
*/
void setBeanClassName(String beanClassName);
/**
- * Return the factory bean name, if any.
- */
- String getFactoryBeanName();
-
- /**
- * Specify the factory bean to use, if any.
- */
- void setFactoryBeanName(String factoryBeanName);
-
- /**
- * Return a factory method, if any.
+ * Return the current bean class name of this bean definition.
+ *
Note that this does not have to be the actual class name used at runtime, in
+ * case of a child definition overriding/inheriting the class name from its parent.
+ * Also, this may just be the class that a factory method is called on, or it may
+ * even be empty in case of a factory bean reference that a method is called on.
+ * Hence, do not consider this to be the definitive bean type at runtime but
+ * rather only use it for parsing purposes at the individual bean definition level.
+ * @see #getParentName()
+ * @see #getFactoryBeanName()
+ * @see #getFactoryMethodName()
*/
- String getFactoryMethodName();
+ String getBeanClassName();
/**
- * Specify a factory method, if any. This method will be invoked with
- * constructor arguments, or with no arguments if none are specified.
- * The method will be invoked on the specified factory bean, if any,
- * or otherwise as a static method on the local bean class.
- * @param factoryMethodName static factory method name,
- * or {@code null} if normal constructor creation should be used
- * @see #getBeanClassName()
+ * Override the target scope of this bean, specifying a new scope name.
+ * @see #SCOPE_SINGLETON
+ * @see #SCOPE_PROTOTYPE
*/
- void setFactoryMethodName(String factoryMethodName);
+ void setScope(String scope);
/**
* Return the name of the current target scope for this bean,
@@ -138,11 +129,11 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String getScope();
/**
- * Override the target scope of this bean, specifying a new scope name.
- * @see #SCOPE_SINGLETON
- * @see #SCOPE_PROTOTYPE
+ * Set whether this bean should be lazily initialized.
+ *
If {@code false}, the bean will get instantiated on startup by bean
+ * factories that perform eager initialization of singletons.
*/
- void setScope(String scope);
+ void setLazyInit(boolean lazyInit);
/**
* Return whether this bean should be lazily initialized, i.e. not
@@ -151,11 +142,10 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
boolean isLazyInit();
/**
- * Set whether this bean should be lazily initialized.
- *
If {@code false}, the bean will get instantiated on startup by bean
- * factories that perform eager initialization of singletons.
+ * Set the names of the beans that this bean depends on being initialized.
+ * The bean factory will guarantee that these beans get initialized first.
*/
- void setLazyInit(boolean lazyInit);
+ void setDependsOn(String... dependsOn);
/**
* Return the bean names that this bean depends on.
@@ -163,10 +153,13 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String[] getDependsOn();
/**
- * Set the names of the beans that this bean depends on being initialized.
- * The bean factory will guarantee that these beans get initialized first.
+ * Set whether this bean is a candidate for getting autowired into some other bean.
+ *
Note that this flag is designed to only affect type-based autowiring.
+ * It does not affect explicit references by name, which will get resolved even
+ * if the specified bean is not marked as an autowire candidate. As a consequence,
+ * autowiring by name will nevertheless inject a bean if the name matches.
*/
- void setDependsOn(String... dependsOn);
+ void setAutowireCandidate(boolean autowireCandidate);
/**
* Return whether this bean is a candidate for getting autowired into some other bean.
@@ -174,24 +167,43 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
boolean isAutowireCandidate();
/**
- * Set whether this bean is a candidate for getting autowired into some other bean.
+ * Set whether this bean is a primary autowire candidate.
+ *
If this value is {@code true} for exactly one bean among multiple
+ * matching candidates, it will serve as a tie-breaker.
*/
- void setAutowireCandidate(boolean autowireCandidate);
+ void setPrimary(boolean primary);
/**
* Return whether this bean is a primary autowire candidate.
- * If this value is true for exactly one bean among multiple
- * matching candidates, it will serve as a tie-breaker.
*/
boolean isPrimary();
/**
- * Set whether this bean is a primary autowire candidate.
- *
If this value is true for exactly one bean among multiple
- * matching candidates, it will serve as a tie-breaker.
+ * Specify the factory bean to use, if any.
+ * This the name of the bean to call the specified factory method on.
+ * @see #setFactoryMethodName
*/
- void setPrimary(boolean primary);
+ void setFactoryBeanName(String factoryBeanName);
+
+ /**
+ * Return the factory bean name, if any.
+ */
+ String getFactoryBeanName();
+
+ /**
+ * Specify a factory method, if any. This method will be invoked with
+ * constructor arguments, or with no arguments if none are specified.
+ * The method will be invoked on the specified factory bean, if any,
+ * or otherwise as a static method on the local bean class.
+ * @see #setFactoryBeanName
+ * @see #setBeanClassName
+ */
+ void setFactoryMethodName(String factoryMethodName);
+ /**
+ * Return a factory method, if any.
+ */
+ String getFactoryMethodName();
/**
* Return the constructor argument values for this bean.
@@ -208,6 +220,8 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
MutablePropertyValues getPropertyValues();
+ // Read-only attributes
+
/**
* Return whether this a Singleton , with a single, shared instance
* returned on all calls.
@@ -218,6 +232,7 @@ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* Return whether this a Prototype , with an independent instance
* returned for each call.
+ * @since 3.0
* @see #SCOPE_PROTOTYPE
*/
boolean isPrototype();
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/CustomScopeConfigurer.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/CustomScopeConfigurer.java
index dc1516ab02..a110bc4461 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/CustomScopeConfigurer.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/CustomScopeConfigurer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -102,12 +102,12 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
}
else if (value instanceof Class) {
Class> scopeClass = (Class>) value;
- Assert.isAssignable(Scope.class, scopeClass);
+ Assert.isAssignable(Scope.class, scopeClass, "Invalid scope class");
beanFactory.registerScope(scopeKey, (Scope) BeanUtils.instantiateClass(scopeClass));
}
else if (value instanceof String) {
Class> scopeClass = ClassUtils.resolveClassName((String) value, this.beanClassLoader);
- Assert.isAssignable(Scope.class, scopeClass);
+ Assert.isAssignable(Scope.class, scopeClass, "Invalid scope class");
beanFactory.registerScope(scopeKey, (Scope) BeanUtils.instantiateClass(scopeClass));
}
else {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
index 3533840635..bda5e0c0b7 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -28,7 +28,6 @@
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
-import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
@@ -63,6 +62,8 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
private Class> containingClass;
+ private volatile ResolvableType resolvableType;
+
/**
* Create a new descriptor for a method or constructor parameter.
@@ -172,21 +173,6 @@ public Object resolveNotUnique(Class> type, Map matchingBeans)
throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());
}
- /**
- * Resolve the specified bean name, as a candidate result of the matching
- * algorithm for this dependency, to a bean instance from the given factory.
- * The default implementation calls {@link BeanFactory#getBean(String)}.
- * Subclasses may provide additional arguments or other customizations.
- * @param beanName the bean name, as a candidate result for this dependency
- * @param beanFactory the associated factory
- * @return the bean instance (never {@code null})
- * @since 4.3
- * @see BeanFactory#getBean(String)
- */
- public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
- return beanFactory.getBean(beanName);
- }
-
/**
* Resolve a shortcut for this dependency against the given factory, for example
* taking some pre-resolved information into account.
@@ -196,12 +182,32 @@ public Object resolveCandidate(String beanName, BeanFactory beanFactory) {
* pre-cached information while still receiving {@link InjectionPoint} exposure etc.
* @param beanFactory the associated factory
* @return the shortcut result if any, or {@code null} if none
+ * @throws BeansException if the shortcut could not be obtained
* @since 4.3.1
*/
- public Object resolveShortcut(BeanFactory beanFactory) {
+ public Object resolveShortcut(BeanFactory beanFactory) throws BeansException {
return null;
}
+ /**
+ * Resolve the specified bean name, as a candidate result of the matching
+ * algorithm for this dependency, to a bean instance from the given factory.
+ *
The default implementation calls {@link BeanFactory#getBean(String)}.
+ * Subclasses may provide additional arguments or other customizations.
+ * @param beanName the bean name, as a candidate result for this dependency
+ * @param requiredType the expected type of the bean (as an assertion)
+ * @param beanFactory the associated factory
+ * @return the bean instance (never {@code null})
+ * @throws BeansException if the bean could not be obtained
+ * @since 4.3.2
+ * @see BeanFactory#getBean(String)
+ */
+ public Object resolveCandidate(String beanName, Class> requiredType, BeanFactory beanFactory)
+ throws BeansException {
+
+ return beanFactory.getBean(beanName, requiredType);
+ }
+
/**
* Increase this descriptor's nesting level.
@@ -209,6 +215,7 @@ public Object resolveShortcut(BeanFactory beanFactory) {
*/
public void increaseNestingLevel() {
this.nestingLevel++;
+ this.resolvableType = null;
if (this.methodParameter != null) {
this.methodParameter.increaseNestingLevel();
}
@@ -222,6 +229,7 @@ public void increaseNestingLevel() {
*/
public void setContainingClass(Class> containingClass) {
this.containingClass = containingClass;
+ this.resolvableType = null;
if (this.methodParameter != null) {
GenericTypeResolver.resolveParameterType(this.methodParameter, containingClass);
}
@@ -232,14 +240,20 @@ public void setContainingClass(Class> containingClass) {
* @since 4.0
*/
public ResolvableType getResolvableType() {
- return (this.field != null ? ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) :
- ResolvableType.forMethodParameter(this.methodParameter));
+ ResolvableType resolvableType = this.resolvableType;
+ if (resolvableType == null) {
+ resolvableType = (this.field != null ?
+ ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) :
+ ResolvableType.forMethodParameter(this.methodParameter));
+ this.resolvableType = resolvableType;
+ }
+ return resolvableType;
}
/**
* Return whether a fallback match is allowed.
*
This is {@code false} by default but may be overridden to return {@code true} in order
- * to suggest to a {@link org.springframework.beans.factory.support.AutowireCandidateResolver}
+ * to suggest to an {@link org.springframework.beans.factory.support.AutowireCandidateResolver}
* that a fallback match is acceptable as well.
* @since 4.0
*/
@@ -294,7 +308,6 @@ public Class> getDependencyType() {
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
type = args[args.length - 1];
}
- // TODO: Object.class if unresolvable
}
if (type instanceof Class) {
return (Class>) type;
@@ -319,31 +332,37 @@ else if (type instanceof ParameterizedType) {
/**
* Determine the generic element type of the wrapped Collection parameter/field, if any.
* @return the generic type, or {@code null} if none
+ * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage
*/
+ @Deprecated
public Class> getCollectionType() {
return (this.field != null ?
- GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel) :
- GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter));
+ org.springframework.core.GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel) :
+ org.springframework.core.GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter));
}
/**
* Determine the generic key type of the wrapped Map parameter/field, if any.
* @return the generic type, or {@code null} if none
+ * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage
*/
+ @Deprecated
public Class> getMapKeyType() {
return (this.field != null ?
- GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel) :
- GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter));
+ org.springframework.core.GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel) :
+ org.springframework.core.GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter));
}
/**
* Determine the generic value type of the wrapped Map parameter/field, if any.
* @return the generic type, or {@code null} if none
+ * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage
*/
+ @Deprecated
public Class> getMapValueType() {
return (this.field != null ?
- GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel) :
- GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter));
+ org.springframework.core.GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel) :
+ org.springframework.core.GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter));
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java
index 92316f13b2..e2e8c46172 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -30,16 +30,16 @@
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {
/**
- * Apply this BeanPostProcessor to the given bean instance before
- * its destruction. Can invoke custom destruction callbacks.
- *
Like DisposableBean's {@code destroy} and a custom destroy method,
- * this callback just applies to singleton beans in the factory (including
- * inner beans).
+ * Apply this BeanPostProcessor to the given bean instance before its
+ * destruction, e.g. invoking custom destruction callbacks.
+ *
Like DisposableBean's {@code destroy} and a custom destroy method, this
+ * callback will only apply to beans which the container fully manages the
+ * lifecycle for. This is usually the case for singletons and scoped beans.
* @param bean the bean instance to be destroyed
* @param beanName the name of the bean
* @throws org.springframework.beans.BeansException in case of errors
- * @see org.springframework.beans.factory.DisposableBean
- * @see org.springframework.beans.factory.support.AbstractBeanDefinition#setDestroyMethodName
+ * @see org.springframework.beans.factory.DisposableBean#destroy()
+ * @see org.springframework.beans.factory.support.AbstractBeanDefinition#setDestroyMethodName(String)
*/
void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java
index 964ee204b2..62c0dbdfaa 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -71,9 +71,8 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
/**
* Perform operations after the bean has been instantiated, via a constructor or factory method,
* but before Spring property population (from explicit properties or autowiring) occurs.
- *
This is the ideal callback for performing field injection on the given bean instance.
- * See Spring's own {@link org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor}
- * for a typical example.
+ *
This is the ideal callback for performing custom field injection on the given bean
+ * instance, right before Spring's autowiring kicks in.
* @param bean the bean instance created, with properties not having been set yet
* @param beanName the name of the bean
* @return {@code true} if properties should be set on the bean; {@code false}
@@ -103,7 +102,6 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
* @see org.springframework.beans.MutablePropertyValues
*/
PropertyValues postProcessPropertyValues(
- PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
- throws BeansException;
+ PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java
index 3e02846fcb..3f7e1746b7 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -66,8 +66,7 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw
@Override
public PropertyValues postProcessPropertyValues(
- PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
- throws BeansException {
+ PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
return pvs;
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ListFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ListFactoryBean.java
index fb251c4243..3ab9683109 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ListFactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ListFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -21,7 +21,7 @@
import org.springframework.beans.BeanUtils;
import org.springframework.beans.TypeConverter;
-import org.springframework.core.GenericCollectionTypeResolver;
+import org.springframework.core.ResolvableType;
/**
* Simple factory for shared List instances. Allows for central setup
@@ -86,7 +86,7 @@ protected List createInstance() {
}
Class> valueType = null;
if (this.targetListClass != null) {
- valueType = GenericCollectionTypeResolver.getCollectionType(this.targetListClass);
+ valueType = ResolvableType.forClass(this.targetListClass).asCollection().resolveGeneric();
}
if (valueType != null) {
TypeConverter converter = getBeanTypeConverter();
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java
index f1ba9cfd45..2e9ca7378b 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -21,7 +21,7 @@
import org.springframework.beans.BeanUtils;
import org.springframework.beans.TypeConverter;
-import org.springframework.core.GenericCollectionTypeResolver;
+import org.springframework.core.ResolvableType;
/**
* Simple factory for shared Map instances. Allows for central setup
@@ -87,8 +87,9 @@ protected Map createInstance() {
Class> keyType = null;
Class> valueType = null;
if (this.targetMapClass != null) {
- keyType = GenericCollectionTypeResolver.getMapKeyType(this.targetMapClass);
- valueType = GenericCollectionTypeResolver.getMapValueType(this.targetMapClass);
+ ResolvableType mapType = ResolvableType.forClass(this.targetMapClass).asMap();
+ keyType = mapType.resolveGeneric(0);
+ valueType = mapType.resolveGeneric(1);
}
if (keyType != null || valueType != null) {
TypeConverter converter = getBeanTypeConverter();
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java
index fe3f25b47b..1c3c3d9489 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -69,7 +69,7 @@
* </bean>
*
* <bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
- * <property name="targetObject" value="sysProps"/>
+ * <property name="targetObject" ref="sysProps"/>
* <property name="targetMethod" value="getProperty"/>
* <property name="arguments" value="java.version"/>
* </bean>
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java
new file mode 100644
index 0000000000..04e5e39a37
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2002-2016 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.beans.factory.config;
+
+import org.springframework.beans.factory.NamedBean;
+import org.springframework.util.Assert;
+
+/**
+ * A simple holder for a given bean name plus bean instance.
+ *
+ * @author Juergen Hoeller
+ * @since 4.3.3
+ * @see AutowireCapableBeanFactory#resolveNamedBean(Class)
+ */
+public class NamedBeanHolder implements NamedBean {
+
+ private final String beanName;
+
+ private final T beanInstance;
+
+
+ /**
+ * Create a new holder for the given bean name plus instance.
+ * @param beanName the name of the bean
+ * @param beanInstance the corresponding bean instance
+ */
+ public NamedBeanHolder(String beanName, T beanInstance) {
+ Assert.notNull(beanName, "Bean name must not be null");
+ this.beanName = beanName;
+ this.beanInstance = beanInstance;
+ }
+
+
+ /**
+ * Return the name of the bean (never {@code null}).
+ */
+ @Override
+ public String getBeanName() {
+ return this.beanName;
+ }
+
+ /**
+ * Return the corresponding bean instance (can be {@code null}).
+ */
+ public T getBeanInstance() {
+ return this.beanInstance;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java
index 93a97a8dcf..adc415d0d5 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -95,7 +95,7 @@ public Class getObjectType() {
* Invoked on initialization of this FactoryBean in case of a
* shared singleton; else, on each {@link #getObject()} call.
* @return the object returned by this factory
- * @throws IOException if an exception occured during properties loading
+ * @throws IOException if an exception occurred during properties loading
* @see #mergeProperties()
*/
protected Properties createProperties() throws IOException {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java
index 2911a123a1..4e313c7919 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -68,6 +68,7 @@ public interface Scope {
* @param objectFactory the {@link ObjectFactory} to use to create the scoped
* object if it is not present in the underlying storage mechanism
* @return the desired object (never {@code null})
+ * @throws IllegalStateException if the underlying scope is not currently active
*/
Object get(String name, ObjectFactory> objectFactory);
@@ -84,6 +85,7 @@ public interface Scope {
* removing an object.
* @param name the name of the object to remove
* @return the removed object, or {@code null} if no object was present
+ * @throws IllegalStateException if the underlying scope is not currently active
* @see #registerDestructionCallback
*/
Object remove(String name);
@@ -112,6 +114,7 @@ public interface Scope {
* so it can safely be executed without an enclosing try-catch block.
* Furthermore, the Runnable will usually be serializable, provided
* that its target object is serializable as well.
+ * @throws IllegalStateException if the underlying scope is not currently active
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#getDestroyMethodName()
* @see DestructionAwareBeanPostProcessor
@@ -123,6 +126,7 @@ public interface Scope {
* E.g. the HttpServletRequest object for key "request".
* @param key the contextual key
* @return the corresponding object, or {@code null} if none found
+ * @throws IllegalStateException if the underlying scope is not currently active
*/
Object resolveContextualObject(String key);
@@ -139,6 +143,7 @@ public interface Scope {
* underlying storage mechanism has no obvious candidate for such an ID.
* @return the conversation ID, or {@code null} if there is no
* conversation ID for the current scope
+ * @throws IllegalStateException if the underlying scope is not currently active
*/
String getConversationId();
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java
index 92345d7eab..729abc7cc5 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -281,15 +281,15 @@ public void afterPropertiesSet() {
@SuppressWarnings("unchecked")
protected Constructor determineServiceLocatorExceptionConstructor(Class extends Exception> exceptionClass) {
try {
- return (Constructor) exceptionClass.getConstructor(new Class>[] {String.class, Throwable.class});
+ return (Constructor) exceptionClass.getConstructor(String.class, Throwable.class);
}
catch (NoSuchMethodException ex) {
try {
- return (Constructor) exceptionClass.getConstructor(new Class>[] {Throwable.class});
+ return (Constructor) exceptionClass.getConstructor(Throwable.class);
}
catch (NoSuchMethodException ex2) {
try {
- return (Constructor) exceptionClass.getConstructor(new Class>[] {String.class});
+ return (Constructor) exceptionClass.getConstructor(String.class);
}
catch (NoSuchMethodException ex3) {
throw new IllegalArgumentException(
@@ -357,7 +357,7 @@ else if (ReflectionUtils.isHashCodeMethod(method)) {
return System.identityHashCode(proxy);
}
else if (ReflectionUtils.isToStringMethod(method)) {
- return "Service locator: " + serviceLocatorInterface.getName();
+ return "Service locator: " + serviceLocatorInterface;
}
else {
return invokeServiceLocatorMethod(method, args);
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java
index c6b573d3db..cd05acd238 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -21,7 +21,7 @@
import org.springframework.beans.BeanUtils;
import org.springframework.beans.TypeConverter;
-import org.springframework.core.GenericCollectionTypeResolver;
+import org.springframework.core.ResolvableType;
/**
* Simple factory for shared Set instances. Allows for central setup
@@ -86,7 +86,7 @@ protected Set createInstance() {
}
Class> valueType = null;
if (this.targetSetClass != null) {
- valueType = GenericCollectionTypeResolver.getCollectionType(this.targetSetClass);
+ valueType = ResolvableType.forClass(this.targetSetClass).asCollection().resolveGeneric();
}
if (valueType != null) {
TypeConverter converter = getBeanTypeConverter();
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlMapFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlMapFactoryBean.java
index 7428f997f9..755819a10a 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlMapFactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlMapFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -25,12 +25,16 @@
import org.springframework.beans.factory.InitializingBean;
/**
- * Factory for a Map that reads from a YAML source. YAML is a nice human-readable
- * format for configuration, and it has some useful hierarchical properties. It's
- * more or less a superset of JSON, so it has a lot of similar features. If
- * multiple resources are provided the later ones will override entries in the
- * earlier ones hierarchically - that is all entries with the same nested key of
- * type Map at any depth are merged. For example:
+ * Factory for a {@code Map} that reads from a YAML source, preserving the
+ * YAML-declared value types and their structure.
+ *
+ * YAML is a nice human-readable format for configuration, and it has some
+ * useful hierarchical properties. It's more or less a superset of JSON, so it
+ * has a lot of similar features.
+ *
+ *
If multiple resources are provided the later ones will override entries in
+ * the earlier ones hierarchically; that is, all entries with the same nested key
+ * of type {@code Map} at any depth are merged. For example:
*
*
* foo:
@@ -62,6 +66,7 @@
* with the value in the second, but its nested values are merged.
*
* @author Dave Syer
+ * @author Juergen Hoeller
* @since 4.1
*/
public class YamlMapFactoryBean extends YamlProcessor implements FactoryBean>, InitializingBean {
@@ -104,10 +109,10 @@ public Class> getObjectType() {
/**
* Template method that subclasses may override to construct the object
- * returned by this factory. The default implementation returns the
- * merged Map instance.
+ * returned by this factory.
* Invoked lazily the first time {@link #getObject()} is invoked in
* case of a shared singleton; else, on each {@link #getObject()} call.
+ *
The default implementation returns the merged {@code Map} instance.
* @return the object returned by this factory
* @see #process(java.util.Map, MatchCallback)
*/
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java
index ac01eb74b7..2f43f2e84c 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -25,7 +25,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
@@ -37,6 +36,7 @@
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.reader.UnicodeReader;
+import org.springframework.core.CollectionFactory;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -45,6 +45,7 @@
* Base class for YAML factories.
*
* @author Dave Syer
+ * @author Juergen Hoeller
* @since 4.1
*/
public abstract class YamlProcessor {
@@ -75,15 +76,16 @@ public abstract class YamlProcessor {
* name: My Cool App
*
* when mapped with
- * documentMatchers = YamlProcessor.mapMatcher({"environment": "prod"})
+ *
+ * setDocumentMatchers(properties ->
+ * ("prod".equals(properties.getProperty("environment")) ? MatchStatus.FOUND : MatchStatus.NOT_FOUND));
+ *
* would end up as
*
* environment=prod
* url=http://foo.bar.com
* name=My Cool App
- * url=http://dev.bar.com
*
- * @param matchers a map of keys to value patterns (regular expressions)
*/
public void setDocumentMatchers(DocumentMatcher... matchers) {
this.documentMatchers = Arrays.asList(matchers);
@@ -92,8 +94,7 @@ public void setDocumentMatchers(DocumentMatcher... matchers) {
/**
* Flag indicating that a document for which all the
* {@link #setDocumentMatchers(DocumentMatcher...) document matchers} abstain will
- * nevertheless match.
- * @param matchDefault the flag to set (default true)
+ * nevertheless match. Default is {@code true}.
*/
public void setMatchDefault(boolean matchDefault) {
this.matchDefault = matchDefault;
@@ -102,9 +103,7 @@ public void setMatchDefault(boolean matchDefault) {
/**
* Method to use for resolving resources. Each resource will be converted to a Map,
* so this property is used to decide which map entries to keep in the final output
- * from this factory.
- * @param resolutionMethod the resolution method to set (defaults to
- * {@link ResolutionMethod#OVERRIDE}).
+ * from this factory. Default is {@link ResolutionMethod#OVERRIDE}.
*/
public void setResolutionMethod(ResolutionMethod resolutionMethod) {
Assert.notNull(resolutionMethod, "ResolutionMethod must not be null");
@@ -199,7 +198,7 @@ private Map asMap(Object object) {
}
Map map = (Map) object;
- for (Entry entry : map.entrySet()) {
+ for (Map.Entry entry : map.entrySet()) {
Object value = entry.getValue();
if (value instanceof Map) {
value = asMap(value);
@@ -217,7 +216,7 @@ private Map asMap(Object object) {
}
private boolean process(Map map, MatchCallback callback) {
- Properties properties = new Properties();
+ Properties properties = CollectionFactory.createStringAdaptingProperties();
properties.putAll(getFlattenedMap(map));
if (this.documentMatchers.isEmpty()) {
@@ -271,14 +270,14 @@ protected final Map getFlattenedMap(Map source)
}
private void buildFlattenedMap(Map result, Map source, String path) {
- for (Entry entry : source.entrySet()) {
+ for (Map.Entry entry : source.entrySet()) {
String key = entry.getKey();
if (StringUtils.hasText(path)) {
if (key.startsWith("[")) {
key = path + key;
}
else {
- key = path + "." + key;
+ key = path + '.' + key;
}
}
Object value = entry.getValue();
@@ -302,21 +301,23 @@ else if (value instanceof Collection) {
}
}
else {
- result.put(key, value != null ? value : "");
+ result.put(key, (value != null ? value : ""));
}
}
}
/**
- * Callback interface used to process properties in a resulting map.
+ * Callback interface used to process the YAML parsing results.
*/
public interface MatchCallback {
/**
- * Process the properties.
- * @param properties the properties to process
- * @param map a mutable result map
+ * Process the given representation of the parsing results.
+ * @param properties the properties to process (as a flattened
+ * representation with indexed keys in case of a collection or map)
+ * @param map the result map (preserving the original value structure
+ * in the YAML document)
*/
void process(Properties properties, Map map);
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBean.java
index 0374ddbf55..951ef3183b 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBean.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -21,13 +21,23 @@
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.core.CollectionFactory;
/**
- * Factory for Java Properties that reads from a YAML source. YAML is a nice
- * human-readable format for configuration, and it has some useful hierarchical
- * properties. It's more or less a superset of JSON, so it has a lot of similar
- * features. The Properties created by this factory have nested paths for
- * hierarchical objects, so for instance this YAML
+ * Factory for {@link java.util.Properties} that reads from a YAML source,
+ * exposing a flat structure of String property values.
+ *
+ * YAML is a nice human-readable format for configuration, and it has some
+ * useful hierarchical properties. It's more or less a superset of JSON, so it
+ * has a lot of similar features.
+ *
+ *
Note: All exposed values are of type {@code String} for access through
+ * the common {@link Properties#getProperty} method (e.g. in configuration property
+ * resolution through {@link PropertyResourceConfigurer#setProperties(Properties)}).
+ * If this is not desirable, use {@link YamlMapFactoryBean} instead.
+ *
+ *
The Properties created by this factory have nested paths for hierarchical
+ * objects, so for instance this YAML
*
*
* environments:
@@ -39,7 +49,7 @@
* name: My Cool App
*
*
- * is transformed into these Properties:
+ * is transformed into these properties:
*
*
* environments.dev.url=http://dev.bar.com
@@ -57,7 +67,7 @@
* - foo.bar.com
*
*
- * becomes Java Properties like this:
+ * becomes properties like this:
*
*
* servers[0]=dev.bar.com
@@ -66,6 +76,7 @@
*
* @author Dave Syer
* @author Stephane Nicoll
+ * @author Juergen Hoeller
* @since 4.1
*/
public class YamlPropertiesFactoryBean extends YamlProcessor implements FactoryBean, InitializingBean {
@@ -116,7 +127,7 @@ public Class> getObjectType() {
* @see #process(MatchCallback) ()
*/
protected Properties createProperties() {
- final Properties result = new Properties();
+ final Properties result = CollectionFactory.createStringAdaptingProperties();
process(new MatchCallback() {
@Override
public void process(Properties properties, Map map) {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanComponentDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanComponentDefinition.java
index 2b88d82765..e4b3b45b12 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanComponentDefinition.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanComponentDefinition.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -76,8 +76,7 @@ private void findInnerBeanDefinitionsAndBeanReferences(BeanDefinition beanDefini
List innerBeans = new ArrayList();
List references = new ArrayList();
PropertyValues propertyValues = beanDefinition.getPropertyValues();
- for (int i = 0; i < propertyValues.getPropertyValues().length; i++) {
- PropertyValue propertyValue = propertyValues.getPropertyValues()[i];
+ for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
Object value = propertyValue.getValue();
if (value instanceof BeanDefinitionHolder) {
innerBeans.add(((BeanDefinitionHolder) value).getBeanDefinition());
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ComponentDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ComponentDefinition.java
index 49c5f9f7b5..2a32e6750f 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ComponentDefinition.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ComponentDefinition.java
@@ -52,7 +52,7 @@
* all {@link BeanReference BeanReferences} that are required to validate the configuration of the
* overall logical entity as well as those required to provide full user visualisation of the configuration.
* It is expected that certain {@link BeanReference BeanReferences} will not be important to
- * validation or to the user view of the configuration and as such these may be ommitted. A tool may wish to
+ * validation or to the user view of the configuration and as such these may be omitted. A tool may wish to
* display any additional {@link BeanReference BeanReferences} sourced through the supplied
* {@link BeanDefinition BeanDefinitions} but this is not considered to be a typical case.
*
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Problem.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Problem.java
index 9f78a2909d..c317d5f935 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Problem.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Problem.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -42,7 +42,7 @@ public class Problem {
/**
* Create a new instance of the {@link Problem} class.
- * @param message a message detailing the problem
+ * @param message a message detailing the problem
* @param location the location within a bean configuration source that triggered the error
*/
public Problem(String message, Location location) {
@@ -51,7 +51,7 @@ public Problem(String message, Location location) {
/**
* Create a new instance of the {@link Problem} class.
- * @param message a message detailing the problem
+ * @param message a message detailing the problem
* @param parseState the {@link ParseState} at the time of the error
* @param location the location within a bean configuration source that triggered the error
*/
@@ -61,8 +61,8 @@ public Problem(String message, Location location, ParseState parseState) {
/**
* Create a new instance of the {@link Problem} class.
- * @param message a message detailing the problem
- * @param rootCause the underlying expection that caused the error (may be {@code null})
+ * @param message a message detailing the problem
+ * @param rootCause the underlying exception that caused the error (may be {@code null})
* @param parseState the {@link ParseState} at the time of the error
* @param location the location within a bean configuration source that triggered the error
*/
@@ -107,7 +107,7 @@ public ParseState getParseState() {
}
/**
- * Get the underlying expection that caused the error (may be {@code null}).
+ * Get the underlying exception that caused the error (may be {@code null}).
*/
public Throwable getRootCause() {
return this.rootCause;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java
index 64689f6486..95f81d8a9f 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2007 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -37,6 +37,13 @@ public class ReaderContext {
private final SourceExtractor sourceExtractor;
+ /**
+ * Construct a new {@code ReaderContext}.
+ * @param resource the XML bean definition resource
+ * @param problemReporter the problem reporter in use
+ * @param eventListener the event listener in use
+ * @param sourceExtractor the source extractor in use
+ */
public ReaderContext(Resource resource, ProblemReporter problemReporter,
ReaderEventListener eventListener, SourceExtractor sourceExtractor) {
@@ -51,83 +58,150 @@ public final Resource getResource() {
}
+ // Errors and warnings
+
+ /**
+ * Raise a fatal error.
+ */
public void fatal(String message, Object source) {
fatal(message, source, null, null);
}
+ /**
+ * Raise a fatal error.
+ */
public void fatal(String message, Object source, Throwable ex) {
fatal(message, source, null, ex);
}
+ /**
+ * Raise a fatal error.
+ */
public void fatal(String message, Object source, ParseState parseState) {
fatal(message, source, parseState, null);
}
+ /**
+ * Raise a fatal error.
+ */
public void fatal(String message, Object source, ParseState parseState, Throwable cause) {
Location location = new Location(getResource(), source);
this.problemReporter.fatal(new Problem(message, location, parseState, cause));
}
+ /**
+ * Raise a regular error.
+ */
public void error(String message, Object source) {
error(message, source, null, null);
}
+ /**
+ * Raise a regular error.
+ */
public void error(String message, Object source, Throwable ex) {
error(message, source, null, ex);
}
+ /**
+ * Raise a regular error.
+ */
public void error(String message, Object source, ParseState parseState) {
error(message, source, parseState, null);
}
+ /**
+ * Raise a regular error.
+ */
public void error(String message, Object source, ParseState parseState, Throwable cause) {
Location location = new Location(getResource(), source);
this.problemReporter.error(new Problem(message, location, parseState, cause));
}
+ /**
+ * Raise a non-critical warning.
+ */
public void warning(String message, Object source) {
warning(message, source, null, null);
}
+ /**
+ * Raise a non-critical warning.
+ */
public void warning(String message, Object source, Throwable ex) {
warning(message, source, null, ex);
}
+ /**
+ * Raise a non-critical warning.
+ */
public void warning(String message, Object source, ParseState parseState) {
warning(message, source, parseState, null);
}
+ /**
+ * Raise a non-critical warning.
+ */
public void warning(String message, Object source, ParseState parseState, Throwable cause) {
Location location = new Location(getResource(), source);
this.problemReporter.warning(new Problem(message, location, parseState, cause));
}
+ // Explicit parse events
+
+ /**
+ * Fire an defaults-registered event.
+ */
public void fireDefaultsRegistered(DefaultsDefinition defaultsDefinition) {
this.eventListener.defaultsRegistered(defaultsDefinition);
}
+ /**
+ * Fire an component-registered event.
+ */
public void fireComponentRegistered(ComponentDefinition componentDefinition) {
this.eventListener.componentRegistered(componentDefinition);
}
+ /**
+ * Fire an alias-registered event.
+ */
public void fireAliasRegistered(String beanName, String alias, Object source) {
this.eventListener.aliasRegistered(new AliasDefinition(beanName, alias, source));
}
+ /**
+ * Fire an import-processed event.
+ */
public void fireImportProcessed(String importedResource, Object source) {
this.eventListener.importProcessed(new ImportDefinition(importedResource, source));
}
+ /**
+ * Fire an import-processed event.
+ */
public void fireImportProcessed(String importedResource, Resource[] actualResources, Object source) {
this.eventListener.importProcessed(new ImportDefinition(importedResource, actualResources, source));
}
+ // Source extraction
+
+ /**
+ * Return the source extractor in use.
+ */
public SourceExtractor getSourceExtractor() {
return this.sourceExtractor;
}
+ /**
+ * Call the source extractor for the given source object.
+ * @param sourceCandidate the original source object
+ * @return the source object to store, or {@code null} for none.
+ * @see #getSourceExtractor()
+ * @see SourceExtractor#extractSource
+ */
public Object extractSource(Object sourceCandidate) {
return this.sourceExtractor.extractSource(sourceCandidate, this.resource);
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
index cf25cd5b9a..55d89dc83c 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -31,9 +31,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
@@ -74,6 +72,7 @@
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.PriorityOrdered;
+import org.springframework.core.ResolvableType;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
@@ -145,7 +144,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
private final Set> ignoredDependencyInterfaces = new HashSet>();
/** Cache of unfinished FactoryBean instances: FactoryBean name --> BeanWrapper */
- private final Map factoryBeanInstanceCache =
+ private final ConcurrentMap factoryBeanInstanceCache =
new ConcurrentHashMap(16);
/** Cache of filtered PropertyDescriptors: bean Class -> PropertyDescriptor array */
@@ -325,8 +324,8 @@ public Object configureBean(Object existingBean, String beanName) throws BeansEx
}
@Override
- public Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException {
- return resolveDependency(descriptor, beanName, null, null);
+ public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName) throws BeansException {
+ return resolveDependency(descriptor, requestingBeanName, null, null);
}
@@ -404,8 +403,8 @@ public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, S
throws BeansException {
Object result = existingBean;
- for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
- result = beanProcessor.postProcessBeforeInitialization(result, beanName);
+ for (BeanPostProcessor processor : getBeanPostProcessors()) {
+ result = processor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
@@ -418,8 +417,8 @@ public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, St
throws BeansException {
Object result = existingBean;
- for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
- result = beanProcessor.postProcessAfterInitialization(result, beanName);
+ for (BeanPostProcessor processor : getBeanPostProcessors()) {
+ result = processor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
@@ -500,7 +499,9 @@ protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] ar
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
*/
- protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
+ protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
+ throws BeanCreationException {
+
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
@@ -511,11 +512,18 @@ protected Object doCreateBean(final String beanName, final RootBeanDefinition mb
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
+ mbd.resolvedTargetType = beanType;
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
- applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
+ try {
+ applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Post-processing of merged bean definition failed", ex);
+ }
mbd.postProcessed = true;
}
}
@@ -550,7 +558,8 @@ public Object getObject() throws BeansException {
throw (BeanCreationException) ex;
}
else {
- throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
+ throw new BeanCreationException(
+ mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
@@ -586,7 +595,8 @@ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
- throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
+ throw new BeanCreationException(
+ mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
@@ -624,10 +634,11 @@ protected Class> predictBeanType(String beanName, RootBeanDefinition mbd, Clas
protected Class> determineTargetType(String beanName, RootBeanDefinition mbd, Class>... typesToMatch) {
Class> targetType = mbd.getTargetType();
if (targetType == null) {
- targetType = (mbd.getFactoryMethodName() != null ? getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
+ targetType = (mbd.getFactoryMethodName() != null ?
+ getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
resolveBeanClass(mbd, beanName, typesToMatch));
if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
- mbd.setTargetType(targetType);
+ mbd.resolvedTargetType = targetType;
}
}
return targetType;
@@ -648,9 +659,9 @@ protected Class> determineTargetType(String beanName, RootBeanDefinition mbd,
* @see #createBean
*/
protected Class> getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class>... typesToMatch) {
- Class> preResolved = mbd.resolvedFactoryMethodReturnType;
- if (preResolved != null) {
- return preResolved;
+ ResolvableType cachedReturnType = mbd.factoryMethodReturnType;
+ if (cachedReturnType != null) {
+ return cachedReturnType.resolve();
}
Class> factoryClass;
@@ -674,26 +685,26 @@ protected Class> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
if (factoryClass == null) {
return null;
}
+ factoryClass = ClassUtils.getUserClass(factoryClass);
// If all factory methods have the same return type, return that type.
// Can't clearly figure out exact method due to type converting / autowiring!
Class> commonType = null;
- boolean cache = false;
+ Method uniqueCandidate = null;
int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount();
Method[] candidates = ReflectionUtils.getUniqueDeclaredMethods(factoryClass);
- for (Method factoryMethod : candidates) {
- if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic &&
- factoryMethod.getName().equals(mbd.getFactoryMethodName()) &&
- factoryMethod.getParameterTypes().length >= minNrOfArgs) {
+ for (Method candidate : candidates) {
+ if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate) &&
+ candidate.getParameterTypes().length >= minNrOfArgs) {
// Declared type variables to inspect?
- if (factoryMethod.getTypeParameters().length > 0) {
+ if (candidate.getTypeParameters().length > 0) {
try {
// Fully resolve parameter names and argument values.
- Class>[] paramTypes = factoryMethod.getParameterTypes();
+ Class>[] paramTypes = candidate.getParameterTypes();
String[] paramNames = null;
ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
if (pnd != null) {
- paramNames = pnd.getParameterNames(factoryMethod);
+ paramNames = pnd.getParameterNames(candidate);
}
ConstructorArgumentValues cav = mbd.getConstructorArgumentValues();
Set usedValueHolders =
@@ -711,10 +722,15 @@ protected Class> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
}
}
Class> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
- factoryMethod, args, getBeanClassLoader());
+ candidate, args, getBeanClassLoader());
if (returnType != null) {
- cache = true;
+ uniqueCandidate = (commonType == null && returnType == candidate.getReturnType() ?
+ candidate : null);
commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
+ if (commonType == null) {
+ // Ambiguous return types found: return null to indicate "not determinable".
+ return null;
+ }
}
}
catch (Throwable ex) {
@@ -724,22 +740,25 @@ protected Class> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
}
}
else {
- commonType = ClassUtils.determineCommonAncestor(factoryMethod.getReturnType(), commonType);
+ uniqueCandidate = (commonType == null ? candidate : null);
+ commonType = ClassUtils.determineCommonAncestor(candidate.getReturnType(), commonType);
+ if (commonType == null) {
+ // Ambiguous return types found: return null to indicate "not determinable".
+ return null;
+ }
}
}
}
- if (commonType != null) {
- // Clear return type found: all factory methods return same type.
- if (cache) {
- mbd.resolvedFactoryMethodReturnType = commonType;
- }
- return commonType;
- }
- else {
- // Ambiguous return types found: return null to indicate "not determinable".
+ if (commonType == null) {
return null;
}
+ // Common return type found: all factory methods return same type. For a non-parameterized
+ // unique candidate, cache the full type declaration context of the target factory method.
+ cachedReturnType = (uniqueCandidate != null ?
+ ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType));
+ mbd.factoryMethodReturnType = cachedReturnType;
+ return cachedReturnType.resolve();
}
/**
@@ -755,32 +774,21 @@ protected Class> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
*/
@Override
protected Class> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
- class Holder { Class> value = null; }
- final Holder objectType = new Holder();
String factoryBeanName = mbd.getFactoryBeanName();
- final String factoryMethodName = mbd.getFactoryMethodName();
+ String factoryMethodName = mbd.getFactoryMethodName();
if (factoryBeanName != null) {
if (factoryMethodName != null) {
- // Try to obtain the FactoryBean's object type without instantiating it at all.
+ // Try to obtain the FactoryBean's object type from its factory method declaration
+ // without instantiating the containing bean at all.
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
- if (fbDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) fbDef).hasBeanClass()) {
- // CGLIB subclass methods hide generic parameters; look at the original user class.
- Class> fbClass = ClassUtils.getUserClass(((AbstractBeanDefinition) fbDef).getBeanClass());
- // Find the given factory method, taking into account that in the case of
- // @Bean methods, there may be parameters present.
- ReflectionUtils.doWithMethods(fbClass,
- new ReflectionUtils.MethodCallback() {
- @Override
- public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
- if (method.getName().equals(factoryMethodName) &&
- FactoryBean.class.isAssignableFrom(method.getReturnType())) {
- objectType.value = GenericTypeResolver.resolveReturnTypeArgument(method, FactoryBean.class);
- }
- }
- });
- if (objectType.value != null && Object.class != objectType.value) {
- return objectType.value;
+ if (fbDef instanceof AbstractBeanDefinition) {
+ AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
+ if (afbDef.hasBeanClass()) {
+ Class> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
+ if (result != null) {
+ return result;
+ }
}
}
}
@@ -792,20 +800,70 @@ public void doWith(Method method) throws IllegalArgumentException, IllegalAccess
}
}
+ // Let's obtain a shortcut instance for an early getObjectType() call...
FactoryBean> fb = (mbd.isSingleton() ?
getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));
if (fb != null) {
// Try to obtain the FactoryBean's object type from this early stage of the instance.
- objectType.value = getTypeForFactoryBean(fb);
- if (objectType.value != null) {
- return objectType.value;
+ Class> result = getTypeForFactoryBean(fb);
+ if (result != null) {
+ return result;
+ }
+ else {
+ // No type found for shortcut FactoryBean instance:
+ // fall back to full creation of the FactoryBean instance.
+ return super.getTypeForFactoryBean(beanName, mbd);
}
}
- // No type found - fall back to full creation of the FactoryBean instance.
- return super.getTypeForFactoryBean(beanName, mbd);
+ if (factoryBeanName == null && mbd.hasBeanClass()) {
+ // No early bean instantiation possible: determine FactoryBean's type from
+ // static factory method signature or from class inheritance hierarchy...
+ if (factoryMethodName != null) {
+ return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
+ }
+ else {
+ return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Introspect the factory method signatures on the given bean class,
+ * trying to find a common {@code FactoryBean} object type declared there.
+ * @param beanClass the bean class to find the factory method on
+ * @param factoryMethodName the name of the factory method
+ * @return the common {@code FactoryBean} object type, or {@code null} if none
+ */
+ private Class> getTypeForFactoryBeanFromMethod(Class> beanClass, final String factoryMethodName) {
+ class Holder { Class> value = null; }
+ final Holder objectType = new Holder();
+
+ // CGLIB subclass methods hide generic parameters; look at the original user class.
+ Class> fbClass = ClassUtils.getUserClass(beanClass);
+
+ // Find the given factory method, taking into account that in the case of
+ // @Bean methods, there may be parameters present.
+ ReflectionUtils.doWithMethods(fbClass,
+ new ReflectionUtils.MethodCallback() {
+ @Override
+ public void doWith(Method method) {
+ if (method.getName().equals(factoryMethodName) &&
+ FactoryBean.class.isAssignableFrom(method.getReturnType())) {
+ Class> currentType = GenericTypeResolver.resolveReturnTypeArgument(
+ method, FactoryBean.class);
+ if (currentType != null) {
+ objectType.value = ClassUtils.determineCommonAncestor(currentType, objectType.value);
+ }
+ }
+ }
+ });
+
+ return (objectType.value != null && Object.class != objectType.value ? objectType.value : null);
}
/**
@@ -824,7 +882,7 @@ protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd,
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
if (exposedObject == null) {
- return exposedObject;
+ return null;
}
}
}
@@ -851,11 +909,16 @@ private FactoryBean> getSingletonFactoryBeanForTypeCheck(String beanName, Root
if (bw != null) {
return (FactoryBean>) bw.getWrappedInstance();
}
+ Object beanInstance = getSingleton(beanName, false);
+ if (beanInstance instanceof FactoryBean) {
+ return (FactoryBean>) beanInstance;
+ }
if (isSingletonCurrentlyInCreation(beanName) ||
(mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName()))) {
return null;
}
- Object instance = null;
+
+ Object instance;
try {
// Mark this bean as currently in creation, even if just partially.
beforeSingletonCreation(beanName);
@@ -870,6 +933,7 @@ private FactoryBean> getSingletonFactoryBeanForTypeCheck(String beanName, Root
// Finished partial creation of this bean.
afterSingletonCreation(beanName);
}
+
FactoryBean> fb = getFactoryBean(beanName, instance);
if (bw != null) {
this.factoryBeanInstanceCache.put(beanName, bw);
@@ -890,6 +954,7 @@ private FactoryBean> getNonSingletonFactoryBeanForTypeCheck(String beanName, R
if (isPrototypeCurrentlyInCreation(beanName)) {
return null;
}
+
Object instance = null;
try {
// Mark this bean as currently in creation, even if just partially.
@@ -913,6 +978,7 @@ private FactoryBean> getNonSingletonFactoryBeanForTypeCheck(String beanName, R
// Finished partial creation of this bean.
afterPrototypeCreation(beanName);
}
+
return getFactoryBean(beanName, instance);
}
@@ -922,24 +988,15 @@ private FactoryBean> getNonSingletonFactoryBeanForTypeCheck(String beanName, R
* @param mbd the merged bean definition for the bean
* @param beanType the actual type of the managed bean instance
* @param beanName the name of the bean
- * @throws BeansException if any post-processing failed
* @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
*/
- protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class> beanType, String beanName)
- throws BeansException {
-
- try {
- for (BeanPostProcessor bp : getBeanPostProcessors()) {
- if (bp instanceof MergedBeanDefinitionPostProcessor) {
- MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
- bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
- }
+ protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class> beanType, String beanName) {
+ for (BeanPostProcessor bp : getBeanPostProcessors()) {
+ if (bp instanceof MergedBeanDefinitionPostProcessor) {
+ MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
+ bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
- catch (Exception ex) {
- throw new BeanCreationException(mbd.getResourceDescription(), beanName,
- "Post-processing failed of bean type [" + beanType + "] failed", ex);
- }
}
/**
@@ -976,12 +1033,9 @@ protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition
* @param beanClass the class of the bean to be instantiated
* @param beanName the name of the bean
* @return the bean object to use instead of a default instance of the target bean, or {@code null}
- * @throws BeansException if any post-processing failed
* @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
*/
- protected Object applyBeanPostProcessorsBeforeInstantiation(Class> beanClass, String beanName)
- throws BeansException {
-
+ protected Object applyBeanPostProcessorsBeforeInstantiation(Class> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
@@ -1000,7 +1054,7 @@ protected Object applyBeanPostProcessorsBeforeInstantiation(Class> beanClass,
* @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 BeanWrapper for the new instance
+ * @return a BeanWrapper for the new instance
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
* @see #instantiateBean
@@ -1080,7 +1134,7 @@ protected Constructor>[] determineConstructorsFromBeanPostProcessors(Class>
* Instantiate the given bean using its default constructor.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
- * @return BeanWrapper for the new instance
+ * @return a BeanWrapper for the new instance
*/
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
@@ -1102,7 +1156,8 @@ public Object run() {
return bw;
}
catch (Throwable ex) {
- throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
+ throw new BeanCreationException(
+ mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
@@ -1114,7 +1169,7 @@ public Object run() {
* @param mbd the bean definition for the bean
* @param explicitArgs argument values passed in programmatically via the getBean method,
* or {@code null} if none (-> use constructor argument values from bean definition)
- * @return BeanWrapper for the new instance
+ * @return a BeanWrapper for the new instance
* @see #getBean(String, Object[])
*/
protected BeanWrapper instantiateUsingFactoryMethod(
@@ -1135,7 +1190,7 @@ protected BeanWrapper instantiateUsingFactoryMethod(
* @param ctors the chosen candidate constructors
* @param explicitArgs argument values passed in programmatically via the getBean method,
* or {@code null} if none (-> use constructor argument values from bean definition)
- * @return BeanWrapper for the new instance
+ * @return a BeanWrapper for the new instance
*/
protected BeanWrapper autowireConstructor(
String beanName, RootBeanDefinition mbd, Constructor>[] ctors, Object[] explicitArgs) {
@@ -1148,7 +1203,7 @@ protected BeanWrapper autowireConstructor(
* from the bean definition.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
- * @param bw BeanWrapper with bean instance
+ * @param bw the BeanWrapper with bean instance
*/
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
PropertyValues pvs = mbd.getPropertyValues();
@@ -1232,7 +1287,7 @@ protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper
* @param beanName the name of the bean we're wiring up.
* Useful for debugging messages; not used functionally.
* @param mbd bean definition to update through autowiring
- * @param bw BeanWrapper from which we can obtain information about the bean
+ * @param bw the BeanWrapper from which we can obtain information about the bean
* @param pvs the PropertyValues to register wired objects with
*/
protected void autowireByName(
@@ -1266,7 +1321,7 @@ protected void autowireByName(
* behavior for bigger applications.
* @param beanName the name of the bean to autowire by type
* @param mbd the merged bean definition to update through autowiring
- * @param bw BeanWrapper from which we can obtain information about the bean
+ * @param bw the BeanWrapper from which we can obtain information about the bean
* @param pvs the PropertyValues to register wired objects with
*/
protected void autowireByType(
@@ -1365,7 +1420,7 @@ protected PropertyDescriptor[] filterPropertyDescriptorsForDependencyCheck(BeanW
*/
protected PropertyDescriptor[] filterPropertyDescriptorsForDependencyCheck(BeanWrapper bw) {
List pds =
- new LinkedList(Arrays.asList(bw.getPropertyDescriptors()));
+ new ArrayList(Arrays.asList(bw.getPropertyDescriptors()));
for (Iterator it = pds.iterator(); it.hasNext();) {
PropertyDescriptor pd = it.next();
if (isExcludedFromDependencyCheck(pd)) {
@@ -1434,15 +1489,13 @@ protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrap
return;
}
+ if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
+ ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
+ }
+
MutablePropertyValues mpvs = null;
List original;
- if (System.getSecurityManager() != null) {
- if (bw instanceof BeanWrapperImpl) {
- ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
- }
- }
-
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
if (mpvs.isConverted()) {
@@ -1578,7 +1631,6 @@ public Object run() {
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
-
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
@@ -1654,7 +1706,9 @@ public Object run() throws Exception {
* methods with arguments.
* @see #invokeInitMethods
*/
- protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable {
+ protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
+ throws Throwable {
+
String initMethodName = mbd.getInitMethodName();
final Method initMethod = (mbd.isNonPublicAccessAllowed() ?
BeanUtils.findMethod(bean.getClass(), initMethodName) :
@@ -1728,8 +1782,21 @@ protected Object postProcessObjectFromFactoryBean(Object object, String beanName
*/
@Override
protected void removeSingleton(String beanName) {
- super.removeSingleton(beanName);
- this.factoryBeanInstanceCache.remove(beanName);
+ synchronized (getSingletonMutex()) {
+ super.removeSingleton(beanName);
+ this.factoryBeanInstanceCache.remove(beanName);
+ }
+ }
+
+ /**
+ * Overridden to clear FactoryBean instance cache as well.
+ */
+ @Override
+ protected void clearSingletonCache() {
+ synchronized (getSingletonMutex()) {
+ super.clearSingletonCache();
+ this.factoryBeanInstanceCache.clear();
+ }
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java
index a0d7dc21dd..1ce334fbf5 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java
@@ -48,6 +48,7 @@
* @author Juergen Hoeller
* @author Rob Harrop
* @author Mark Fisher
+ * @see GenericBeanDefinition
* @see RootBeanDefinition
* @see ChildBeanDefinition
*/
@@ -159,16 +160,16 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
private boolean lenientConstructorResolution = true;
+ private String factoryBeanName;
+
+ private String factoryMethodName;
+
private ConstructorArgumentValues constructorArgumentValues;
private MutablePropertyValues propertyValues;
private MethodOverrides methodOverrides = new MethodOverrides();
- private String factoryBeanName;
-
- private String factoryMethodName;
-
private String initMethodName;
private String destroyMethodName;
@@ -210,14 +211,14 @@ protected AbstractBeanDefinition(ConstructorArgumentValues cargs, MutablePropert
protected AbstractBeanDefinition(BeanDefinition original) {
setParentName(original.getParentName());
setBeanClassName(original.getBeanClassName());
- setFactoryBeanName(original.getFactoryBeanName());
- setFactoryMethodName(original.getFactoryMethodName());
setScope(original.getScope());
setAbstract(original.isAbstract());
setLazyInit(original.isLazyInit());
- setRole(original.getRole());
+ setFactoryBeanName(original.getFactoryBeanName());
+ setFactoryMethodName(original.getFactoryMethodName());
setConstructorArgumentValues(new ConstructorArgumentValues(original.getConstructorArgumentValues()));
setPropertyValues(new MutablePropertyValues(original.getPropertyValues()));
+ setRole(original.getRole());
setSource(original.getSource());
copyAttributesFrom(original);
@@ -230,15 +231,15 @@ protected AbstractBeanDefinition(BeanDefinition original) {
setDependencyCheck(originalAbd.getDependencyCheck());
setDependsOn(originalAbd.getDependsOn());
setAutowireCandidate(originalAbd.isAutowireCandidate());
- copyQualifiersFrom(originalAbd);
setPrimary(originalAbd.isPrimary());
+ copyQualifiersFrom(originalAbd);
setNonPublicAccessAllowed(originalAbd.isNonPublicAccessAllowed());
setLenientConstructorResolution(originalAbd.isLenientConstructorResolution());
+ setMethodOverrides(new MethodOverrides(originalAbd.getMethodOverrides()));
setInitMethodName(originalAbd.getInitMethodName());
setEnforceInitMethod(originalAbd.isEnforceInitMethod());
setDestroyMethodName(originalAbd.getDestroyMethodName());
setEnforceDestroyMethod(originalAbd.isEnforceDestroyMethod());
- setMethodOverrides(new MethodOverrides(originalAbd.getMethodOverrides()));
setSynthetic(originalAbd.isSynthetic());
setResource(originalAbd.getResource());
}
@@ -268,20 +269,20 @@ public void overrideFrom(BeanDefinition other) {
if (StringUtils.hasLength(other.getBeanClassName())) {
setBeanClassName(other.getBeanClassName());
}
+ if (StringUtils.hasLength(other.getScope())) {
+ setScope(other.getScope());
+ }
+ setAbstract(other.isAbstract());
+ setLazyInit(other.isLazyInit());
if (StringUtils.hasLength(other.getFactoryBeanName())) {
setFactoryBeanName(other.getFactoryBeanName());
}
if (StringUtils.hasLength(other.getFactoryMethodName())) {
setFactoryMethodName(other.getFactoryMethodName());
}
- if (StringUtils.hasLength(other.getScope())) {
- setScope(other.getScope());
- }
- setAbstract(other.isAbstract());
- setLazyInit(other.isLazyInit());
- setRole(other.getRole());
getConstructorArgumentValues().addArgumentValues(other.getConstructorArgumentValues());
getPropertyValues().addPropertyValues(other.getPropertyValues());
+ setRole(other.getRole());
setSource(other.getSource());
copyAttributesFrom(other);
@@ -290,14 +291,15 @@ public void overrideFrom(BeanDefinition other) {
if (otherAbd.hasBeanClass()) {
setBeanClass(otherAbd.getBeanClass());
}
- setAutowireCandidate(otherAbd.isAutowireCandidate());
setAutowireMode(otherAbd.getAutowireMode());
- copyQualifiersFrom(otherAbd);
- setPrimary(otherAbd.isPrimary());
setDependencyCheck(otherAbd.getDependencyCheck());
setDependsOn(otherAbd.getDependsOn());
+ setAutowireCandidate(otherAbd.isAutowireCandidate());
+ setPrimary(otherAbd.isPrimary());
+ copyQualifiersFrom(otherAbd);
setNonPublicAccessAllowed(otherAbd.isNonPublicAccessAllowed());
setLenientConstructorResolution(otherAbd.isLenientConstructorResolution());
+ getMethodOverrides().addOverrides(otherAbd.getMethodOverrides());
if (StringUtils.hasLength(otherAbd.getInitMethodName())) {
setInitMethodName(otherAbd.getInitMethodName());
setEnforceInitMethod(otherAbd.isEnforceInitMethod());
@@ -306,7 +308,6 @@ public void overrideFrom(BeanDefinition other) {
setDestroyMethodName(otherAbd.getDestroyMethodName());
setEnforceDestroyMethod(otherAbd.isEnforceDestroyMethod());
}
- getMethodOverrides().addOverrides(otherAbd.getMethodOverrides());
setSynthetic(otherAbd.isSynthetic());
setResource(otherAbd.getResource());
}
@@ -331,10 +332,25 @@ public void applyDefaults(BeanDefinitionDefaults defaults) {
/**
- * Return whether this definition specifies a bean class.
+ * Specify the bean class name of this bean definition.
*/
- public boolean hasBeanClass() {
- return (this.beanClass instanceof Class);
+ @Override
+ public void setBeanClassName(String beanClassName) {
+ this.beanClass = beanClassName;
+ }
+
+ /**
+ * Return the current bean class name of this bean definition.
+ */
+ @Override
+ public String getBeanClassName() {
+ Object beanClassObject = this.beanClass;
+ if (beanClassObject instanceof Class) {
+ return ((Class>) beanClassObject).getName();
+ }
+ else {
+ return (String) beanClassObject;
+ }
}
/**
@@ -362,20 +378,11 @@ public Class> getBeanClass() throws IllegalStateException {
return (Class>) beanClassObject;
}
- @Override
- public void setBeanClassName(String beanClassName) {
- this.beanClass = beanClassName;
- }
-
- @Override
- public String getBeanClassName() {
- Object beanClassObject = this.beanClass;
- if (beanClassObject instanceof Class) {
- return ((Class>) beanClassObject).getName();
- }
- else {
- return (String) beanClassObject;
- }
+ /**
+ * Return whether this definition specifies a bean class.
+ */
+ public boolean hasBeanClass() {
+ return (this.beanClass instanceof Class);
}
/**
@@ -396,7 +403,6 @@ public Class> resolveBeanClass(ClassLoader classLoader) throws ClassNotFoundEx
return resolvedClass;
}
-
/**
* Set the name of the target scope for the bean.
* The default is singleton status, although this is only applied once
@@ -478,7 +484,6 @@ public boolean isLazyInit() {
return this.lazyInit;
}
-
/**
* Set the autowire mode. This determines whether any automagical detection
* and setting of bean references will happen. Default is AUTOWIRE_NO,
@@ -569,6 +574,12 @@ public String[] getDependsOn() {
/**
* Set whether this bean is a candidate for getting autowired into some other bean.
+ *
Note that this flag is designed to only affect type-based autowiring.
+ * It does not affect explicit references by name, which will get resolved even
+ * if the specified bean is not marked as an autowire candidate. As a consequence,
+ * autowiring by name will nevertheless inject a bean if the name matches.
+ * @see #AUTOWIRE_BY_TYPE
+ * @see #AUTOWIRE_BY_NAME
*/
@Override
public void setAutowireCandidate(boolean autowireCandidate) {
@@ -585,7 +596,7 @@ public boolean isAutowireCandidate() {
/**
* Set whether this bean is a primary autowire candidate.
- * If this value is true for exactly one bean among multiple
+ *
If this value is {@code true} for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
@Override
@@ -595,8 +606,6 @@ public void setPrimary(boolean primary) {
/**
* Return whether this bean is a primary autowire candidate.
- * If this value is true for exactly one bean among multiple
- * matching candidates, it will serve as a tie-breaker.
*/
@Override
public boolean isPrimary() {
@@ -643,7 +652,6 @@ public void copyQualifiersFrom(AbstractBeanDefinition source) {
this.qualifiers.putAll(source.qualifiers);
}
-
/**
* Specify whether to allow access to non-public constructors and methods,
* for the case of externalized metadata pointing to those. The default is
@@ -683,6 +691,45 @@ public boolean isLenientConstructorResolution() {
return this.lenientConstructorResolution;
}
+ /**
+ * Specify the factory bean to use, if any.
+ * This the name of the bean to call the specified factory method on.
+ * @see #setFactoryMethodName
+ */
+ @Override
+ public void setFactoryBeanName(String factoryBeanName) {
+ this.factoryBeanName = factoryBeanName;
+ }
+
+ /**
+ * Return the factory bean name, if any.
+ */
+ @Override
+ public String getFactoryBeanName() {
+ return this.factoryBeanName;
+ }
+
+ /**
+ * Specify a factory method, if any. This method will be invoked with
+ * constructor arguments, or with no arguments if none are specified.
+ * The method will be invoked on the specified factory bean, if any,
+ * or otherwise as a static method on the local bean class.
+ * @see #setFactoryBeanName
+ * @see #setBeanClassName
+ */
+ @Override
+ public void setFactoryMethodName(String factoryMethodName) {
+ this.factoryMethodName = factoryMethodName;
+ }
+
+ /**
+ * Return a factory method, if any.
+ */
+ @Override
+ public String getFactoryMethodName() {
+ return this.factoryMethodName;
+ }
+
/**
* Specify constructor argument values for this bean.
*/
@@ -737,27 +784,6 @@ public MethodOverrides getMethodOverrides() {
return this.methodOverrides;
}
-
- @Override
- public void setFactoryBeanName(String factoryBeanName) {
- this.factoryBeanName = factoryBeanName;
- }
-
- @Override
- public String getFactoryBeanName() {
- return this.factoryBeanName;
- }
-
- @Override
- public void setFactoryMethodName(String factoryMethodName) {
- this.factoryMethodName = factoryMethodName;
- }
-
- @Override
- public String getFactoryMethodName() {
- return this.factoryMethodName;
- }
-
/**
* Set the name of the initializer method. The default is {@code null}
* in which case there is no initializer method.
@@ -822,7 +848,6 @@ public boolean isEnforceDestroyMethod() {
return this.enforceDestroyMethod;
}
-
/**
* Set whether this bean definition is 'synthetic', that is, not defined
* by the application itself (for example, an infrastructure bean such
@@ -855,7 +880,6 @@ public int getRole() {
return this.role;
}
-
/**
* Set a human-readable description of this bean definition.
*/
@@ -863,6 +887,9 @@ public void setDescription(String description) {
this.description = description;
}
+ /**
+ * Return a human-readable description of this bean definition.
+ */
@Override
public String getDescription() {
return this.description;
@@ -891,6 +918,10 @@ public void setResourceDescription(String resourceDescription) {
this.resource = new DescriptiveResource(resourceDescription);
}
+ /**
+ * Return a description of the resource that this bean definition
+ * came from (for the purpose of showing context in case of errors).
+ */
@Override
public String getResourceDescription() {
return (this.resource != null ? this.resource.getDescription() : null);
@@ -903,6 +934,12 @@ public void setOriginatingBeanDefinition(BeanDefinition originatingBd) {
this.resource = new BeanDefinitionResource(originatingBd);
}
+ /**
+ * Return the originating BeanDefinition, or {@code null} if none.
+ * Allows for retrieving the decorated bean definition, if any.
+ *
Note that this method returns the immediate originator. Iterate through the
+ * originator chain to find the original BeanDefinition as defined by the user.
+ */
@Override
public BeanDefinition getOriginatingBeanDefinition() {
return (this.resource instanceof BeanDefinitionResource ?
@@ -981,7 +1018,6 @@ public Object clone() {
*/
public abstract AbstractBeanDefinition cloneBeanDefinition();
-
@Override
public boolean equals(Object other) {
if (this == other) {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
index 62dfa38e42..4ddde75736 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -30,11 +30,11 @@
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
@@ -134,24 +134,24 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
private final Set propertyEditorRegistrars =
new LinkedHashSet(4);
- /** A custom TypeConverter to use, overriding the default PropertyEditor mechanism */
- private TypeConverter typeConverter;
-
/** Custom PropertyEditors to apply to the beans of this factory */
private final Map, Class extends PropertyEditor>> customEditors =
new HashMap, Class extends PropertyEditor>>(4);
+ /** A custom TypeConverter to use, overriding the default PropertyEditor mechanism */
+ private TypeConverter typeConverter;
+
/** String resolvers to apply e.g. to annotation attribute values */
- private final List embeddedValueResolvers = new LinkedList();
+ private final List embeddedValueResolvers = new CopyOnWriteArrayList();
/** BeanPostProcessors to apply in createBean */
- private final List beanPostProcessors = new ArrayList();
+ private volatile List beanPostProcessors = new ArrayList();
/** Indicates whether any InstantiationAwareBeanPostProcessors have been registered */
- private boolean hasInstantiationAwareBeanPostProcessors;
+ private volatile boolean hasInstantiationAwareBeanPostProcessors;
/** Indicates whether any DestructionAwareBeanPostProcessors have been registered */
- private boolean hasDestructionAwareBeanPostProcessors;
+ private volatile boolean hasDestructionAwareBeanPostProcessors;
/** Map from scope identifier String to corresponding Scope */
private final Map scopes = new LinkedHashMap(8);
@@ -287,13 +287,19 @@ protected T doGetBean(
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
- for (String dependsOnBean : dependsOn) {
- if (isDependent(beanName, dependsOnBean)) {
+ 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,
- "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
+ "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
- registerDependentBean(dependsOnBean, beanName);
- getBean(dependsOnBean);
}
}
@@ -366,14 +372,14 @@ public Object getObject() throws BeansException {
}
// Check if required type matches the type of the actual bean instance.
- if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
+ if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
- logger.debug("Failed to convert bean '" + name + "' to required type [" +
- ClassUtils.getQualifiedName(requiredType) + "]", ex);
+ logger.debug("Failed to convert bean '" + name + "' to required type '" +
+ ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
@@ -409,33 +415,31 @@ else if (containsSingleton(beanName)) {
return true;
}
- else {
- // No singleton instance found -> check bean definition.
- BeanFactory parentBeanFactory = getParentBeanFactory();
- if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
- // No bean definition found in this factory -> delegate to parent.
- return parentBeanFactory.isSingleton(originalBeanName(name));
- }
+ // No singleton instance found -> check bean definition.
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
+ // No bean definition found in this factory -> delegate to parent.
+ return parentBeanFactory.isSingleton(originalBeanName(name));
+ }
- RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
- // In case of FactoryBean, return singleton status of created object if not a dereference.
- if (mbd.isSingleton()) {
- if (isFactoryBean(beanName, mbd)) {
- if (BeanFactoryUtils.isFactoryDereference(name)) {
- return true;
- }
- FactoryBean> factoryBean = (FactoryBean>) getBean(FACTORY_BEAN_PREFIX + beanName);
- return factoryBean.isSingleton();
- }
- else {
- return !BeanFactoryUtils.isFactoryDereference(name);
+ // In case of FactoryBean, return singleton status of created object if not a dereference.
+ if (mbd.isSingleton()) {
+ if (isFactoryBean(beanName, mbd)) {
+ if (BeanFactoryUtils.isFactoryDereference(name)) {
+ return true;
}
+ FactoryBean> factoryBean = (FactoryBean>) getBean(FACTORY_BEAN_PREFIX + beanName);
+ return factoryBean.isSingleton();
}
else {
- return false;
+ return !BeanFactoryUtils.isFactoryDereference(name);
}
}
+ else {
+ return false;
+ }
}
@Override
@@ -453,32 +457,31 @@ public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
// In case of FactoryBean, return singleton status of created object if not a dereference.
return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName, mbd));
}
- else {
- // Singleton or scoped - not a prototype.
- // However, FactoryBean may still produce a prototype object...
- if (BeanFactoryUtils.isFactoryDereference(name)) {
- return false;
- }
- if (isFactoryBean(beanName, mbd)) {
- final FactoryBean> factoryBean = (FactoryBean>) getBean(FACTORY_BEAN_PREFIX + beanName);
- if (System.getSecurityManager() != null) {
- return AccessController.doPrivileged(new PrivilegedAction() {
- @Override
- public Boolean run() {
- return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean>) factoryBean).isPrototype()) ||
- !factoryBean.isSingleton());
- }
- }, getAccessControlContext());
- }
- else {
- return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean>) factoryBean).isPrototype()) ||
- !factoryBean.isSingleton());
- }
+
+ // Singleton or scoped - not a prototype.
+ // However, FactoryBean may still produce a prototype object...
+ if (BeanFactoryUtils.isFactoryDereference(name)) {
+ return false;
+ }
+ if (isFactoryBean(beanName, mbd)) {
+ final FactoryBean> fb = (FactoryBean>) getBean(FACTORY_BEAN_PREFIX + beanName);
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public Boolean run() {
+ return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean>) fb).isPrototype()) ||
+ !fb.isSingleton());
+ }
+ }, getAccessControlContext());
}
else {
- return false;
+ return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean>) fb).isPrototype()) ||
+ !fb.isSingleton());
}
}
+ else {
+ return false;
+ }
}
@Override
@@ -497,68 +500,91 @@ public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuc
return typeToMatch.isInstance(beanInstance);
}
}
- else {
- return (!BeanFactoryUtils.isFactoryDereference(name) && typeToMatch.isInstance(beanInstance));
+ else if (!BeanFactoryUtils.isFactoryDereference(name)) {
+ if (typeToMatch.isInstance(beanInstance)) {
+ // Direct match for exposed instance?
+ return true;
+ }
+ else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
+ // Generics potentially only match on the target class, not on the proxy...
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ Class> targetType = mbd.getTargetType();
+ if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
+ typeToMatch.isAssignableFrom(targetType)) {
+ // Check raw class match as well, making sure it's exposed on the proxy.
+ Class> classToMatch = typeToMatch.resolve();
+ return (classToMatch == null || classToMatch.isInstance(beanInstance));
+ }
+ }
}
+ return false;
}
else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
// null instance registered
return false;
}
- else {
- // No singleton instance found -> check bean definition.
- BeanFactory parentBeanFactory = getParentBeanFactory();
- if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
- // No bean definition found in this factory -> delegate to parent.
- return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
- }
+ // No singleton instance found -> check bean definition.
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
+ // No bean definition found in this factory -> delegate to parent.
+ return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
+ }
- // Retrieve corresponding bean definition.
- RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
-
- Class> classToMatch = typeToMatch.getRawClass();
- Class>[] typesToMatch = (FactoryBean.class == classToMatch ?
- new Class>[] {classToMatch} : new Class>[] {FactoryBean.class, classToMatch});
-
- // Check decorated bean definition, if any: We assume it'll be easier
- // to determine the decorated bean's type than the proxy's type.
- BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
- if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
- RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
- Class> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
- if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
- return typeToMatch.isAssignableFrom(targetClass);
- }
- }
+ // Retrieve corresponding bean definition.
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
- Class> beanType = predictBeanType(beanName, mbd, typesToMatch);
- if (beanType == null) {
- return false;
+ Class> classToMatch = typeToMatch.resolve();
+ if (classToMatch == null) {
+ classToMatch = FactoryBean.class;
+ }
+ Class>[] typesToMatch = (FactoryBean.class == classToMatch ?
+ new Class>[] {classToMatch} : new Class>[] {FactoryBean.class, classToMatch});
+
+ // Check decorated bean definition, if any: We assume it'll be easier
+ // to determine the decorated bean's type than the proxy's type.
+ BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
+ if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
+ RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
+ Class> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
+ if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
+ return typeToMatch.isAssignableFrom(targetClass);
}
+ }
- // Check bean class whether we're dealing with a FactoryBean.
- if (FactoryBean.class.isAssignableFrom(beanType)) {
- if (!BeanFactoryUtils.isFactoryDereference(name)) {
- // If it's a FactoryBean, we want to look at what it creates, not the factory class.
- beanType = getTypeForFactoryBean(beanName, mbd);
- if (beanType == null) {
- return false;
- }
- }
- }
- else if (BeanFactoryUtils.isFactoryDereference(name)) {
- // Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
- // type but we nevertheless are being asked to dereference a FactoryBean...
- // Let's check the original bean class and proceed with it if it is a FactoryBean.
- beanType = predictBeanType(beanName, mbd, FactoryBean.class);
- if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
+ Class> beanType = predictBeanType(beanName, mbd, typesToMatch);
+ if (beanType == null) {
+ return false;
+ }
+
+ // Check bean class whether we're dealing with a FactoryBean.
+ if (FactoryBean.class.isAssignableFrom(beanType)) {
+ if (!BeanFactoryUtils.isFactoryDereference(name)) {
+ // If it's a FactoryBean, we want to look at what it creates, not the factory class.
+ beanType = getTypeForFactoryBean(beanName, mbd);
+ if (beanType == null) {
return false;
}
}
+ }
+ else if (BeanFactoryUtils.isFactoryDereference(name)) {
+ // Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
+ // type but we nevertheless are being asked to dereference a FactoryBean...
+ // Let's check the original bean class and proceed with it if it is a FactoryBean.
+ beanType = predictBeanType(beanName, mbd, FactoryBean.class);
+ if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
+ return false;
+ }
+ }
- return typeToMatch.isAssignableFrom(beanType);
+ ResolvableType resolvableType = mbd.targetType;
+ if (resolvableType == null) {
+ resolvableType = mbd.factoryMethodReturnType;
+ }
+ if (resolvableType != null && resolvableType.resolve() == beanType) {
+ return typeToMatch.isAssignableFrom(resolvableType);
}
+ return typeToMatch.isAssignableFrom(beanType);
}
@Override
@@ -585,43 +611,41 @@ else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
return null;
}
- else {
- // No singleton instance found -> check bean definition.
- BeanFactory parentBeanFactory = getParentBeanFactory();
- if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
- // No bean definition found in this factory -> delegate to parent.
- return parentBeanFactory.getType(originalBeanName(name));
- }
+ // No singleton instance found -> check bean definition.
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
+ // No bean definition found in this factory -> delegate to parent.
+ return parentBeanFactory.getType(originalBeanName(name));
+ }
- RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
- // Check decorated bean definition, if any: We assume it'll be easier
- // to determine the decorated bean's type than the proxy's type.
- BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
- if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
- RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
- Class> targetClass = predictBeanType(dbd.getBeanName(), tbd);
- if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
- return targetClass;
- }
+ // Check decorated bean definition, if any: We assume it'll be easier
+ // to determine the decorated bean's type than the proxy's type.
+ BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
+ if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
+ RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
+ Class> targetClass = predictBeanType(dbd.getBeanName(), tbd);
+ if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
+ return targetClass;
}
+ }
- Class> beanClass = predictBeanType(beanName, mbd);
+ Class> beanClass = predictBeanType(beanName, mbd);
- // Check bean class whether we're dealing with a FactoryBean.
- if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
- if (!BeanFactoryUtils.isFactoryDereference(name)) {
- // If it's a FactoryBean, we want to look at what it creates, not at the factory class.
- return getTypeForFactoryBean(beanName, mbd);
- }
- else {
- return beanClass;
- }
+ // Check bean class whether we're dealing with a FactoryBean.
+ if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
+ if (!BeanFactoryUtils.isFactoryDereference(name)) {
+ // If it's a FactoryBean, we want to look at what it creates, not at the factory class.
+ return getTypeForFactoryBean(beanName, mbd);
}
else {
- return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
+ return beanClass;
}
}
+ else {
+ return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
+ }
}
@Override
@@ -748,7 +772,7 @@ public Set getPropertyEditorRegistrars() {
@Override
public void registerCustomEditor(Class> requiredType, Class extends PropertyEditor> propertyEditorClass) {
Assert.notNull(requiredType, "Required type must not be null");
- Assert.isAssignable(PropertyEditor.class, propertyEditorClass);
+ Assert.notNull(propertyEditorClass, "PropertyEditor class must not be null");
this.customEditors.put(requiredType, propertyEditorClass);
}
@@ -805,12 +829,15 @@ public boolean hasEmbeddedValueResolver() {
@Override
public String resolveEmbeddedValue(String value) {
+ if (value == null) {
+ return null;
+ }
String result = value;
for (StringValueResolver resolver : this.embeddedValueResolvers) {
+ result = resolver.resolveStringValue(result);
if (result == null) {
return null;
}
- result = resolver.resolveStringValue(result);
}
return result;
}
@@ -818,14 +845,22 @@ public String resolveEmbeddedValue(String value) {
@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
- this.beanPostProcessors.remove(beanPostProcessor);
- this.beanPostProcessors.add(beanPostProcessor);
+ // Local copy set into volatile field, as an alternative to CopyOnWriteArrayList
+ // (which doesn't support Iterator.remove for our getBeanPostProcessors result List)
+ List beanPostProcessors = new ArrayList();
+ beanPostProcessors.addAll(this.beanPostProcessors);
+ // Remove from old position, if any
+ beanPostProcessors.remove(beanPostProcessor);
+ // Track whether it is instantiation/destruction aware
if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
this.hasInstantiationAwareBeanPostProcessors = true;
}
if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
this.hasDestructionAwareBeanPostProcessors = true;
}
+ // Add to end of list
+ beanPostProcessors.add(beanPostProcessor);
+ this.beanPostProcessors = beanPostProcessors;
}
@Override
@@ -918,10 +953,12 @@ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
setBeanClassLoader(otherFactory.getBeanClassLoader());
setCacheBeanMetadata(otherFactory.isCacheBeanMetadata());
setBeanExpressionResolver(otherFactory.getBeanExpressionResolver());
+ setConversionService(otherFactory.getConversionService());
if (otherFactory instanceof AbstractBeanFactory) {
AbstractBeanFactory otherAbstractFactory = (AbstractBeanFactory) otherFactory;
- this.customEditors.putAll(otherAbstractFactory.customEditors);
this.propertyEditorRegistrars.addAll(otherAbstractFactory.propertyEditorRegistrars);
+ this.customEditors.putAll(otherAbstractFactory.customEditors);
+ this.typeConverter = otherAbstractFactory.typeConverter;
this.beanPostProcessors.addAll(otherAbstractFactory.beanPostProcessors);
this.hasInstantiationAwareBeanPostProcessors = this.hasInstantiationAwareBeanPostProcessors ||
otherAbstractFactory.hasInstantiationAwareBeanPostProcessors;
@@ -932,6 +969,10 @@ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
}
else {
setTypeConverter(otherFactory.getTypeConverter());
+ String[] otherScopeNames = otherFactory.getRegisteredScopeNames();
+ for (String scopeName : otherScopeNames) {
+ this.scopes.put(scopeName, otherFactory.getRegisteredScope(scopeName));
+ }
}
}
@@ -949,7 +990,6 @@ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
@Override
public BeanDefinition getMergedBeanDefinition(String name) throws BeansException {
String beanName = transformedBeanName(name);
-
// Efficiently check whether bean definition exists in this factory.
if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName);
@@ -961,7 +1001,6 @@ public BeanDefinition getMergedBeanDefinition(String name) throws BeansException
@Override
public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
String beanName = transformedBeanName(name);
-
Object beanInstance = getSingleton(beanName, false);
if (beanInstance != null) {
return (beanInstance instanceof FactoryBean);
@@ -970,13 +1009,11 @@ else if (containsSingleton(beanName)) {
// null instance registered
return false;
}
-
// No singleton instance found -> check bean definition.
if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
// No bean definition found in this factory -> delegate to parent.
return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
}
-
return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
}
@@ -1050,11 +1087,11 @@ public void destroyBean(String beanName, Object beanInstance) {
* Destroy the given bean instance (usually a prototype instance
* obtained from this factory) according to the given bean definition.
* @param beanName the name of the bean definition
- * @param beanInstance the bean instance to destroy
+ * @param bean the bean instance to destroy
* @param mbd the merged bean definition
*/
- protected void destroyBean(String beanName, Object beanInstance, RootBeanDefinition mbd) {
- new DisposableBeanAdapter(beanInstance, beanName, mbd, getBeanPostProcessors(), getAccessControlContext()).destroy();
+ protected void destroyBean(String beanName, Object bean, RootBeanDefinition mbd) {
+ new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), getAccessControlContext()).destroy();
}
@Override
@@ -1235,12 +1272,13 @@ protected RootBeanDefinition getMergedBeanDefinition(
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
- if (getParentBeanFactory() instanceof ConfigurableBeanFactory) {
- pbd = ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(parentBeanName);
+ BeanFactory parent = getParentBeanFactory();
+ if (parent instanceof ConfigurableBeanFactory) {
+ pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {
- throw new NoSuchBeanDefinitionException(bd.getParentName(),
- "Parent name '" + bd.getParentName() + "' is equal to bean name '" + beanName +
+ throw new NoSuchBeanDefinitionException(parentBeanName,
+ "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
"': cannot be resolved without an AbstractBeanFactory parent");
}
}
@@ -1267,8 +1305,8 @@ protected RootBeanDefinition getMergedBeanDefinition(
mbd.setScope(containingBd.getScope());
}
- // Only cache the merged bean definition if we're already about to create an
- // instance of the bean, or at least have already created an instance before.
+ // Cache the merged bean definition for the time being
+ // (it might still get re-merged later on in order to pick up metadata changes)
if (containingBd == null && isCacheBeanMetadata()) {
this.mergedBeanDefinitions.put(beanName, mbd);
}
@@ -1361,7 +1399,9 @@ public Class> run() throws Exception {
}
}
- private Class> doResolveBeanClass(RootBeanDefinition mbd, Class>... typesToMatch) throws ClassNotFoundException {
+ private Class> doResolveBeanClass(RootBeanDefinition mbd, Class>... typesToMatch)
+ throws ClassNotFoundException {
+
ClassLoader beanClassLoader = getBeanClassLoader();
ClassLoader classLoaderToUse = beanClassLoader;
if (!ObjectUtils.isEmpty(typesToMatch)) {
@@ -1435,6 +1475,10 @@ protected Object evaluateBeanDefinitionString(String value, BeanDefinition beanD
* @return the type of the bean, or {@code null} if not predictable
*/
protected Class> predictBeanType(String beanName, RootBeanDefinition mbd, Class>... typesToMatch) {
+ Class> targetType = mbd.getTargetType();
+ if (targetType != null) {
+ return targetType;
+ }
if (mbd.getFactoryMethodName() != null) {
return null;
}
@@ -1475,7 +1519,7 @@ protected Class> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd
return getTypeForFactoryBean(factoryBean);
}
catch (BeanCreationException ex) {
- if (ex instanceof BeanCurrentlyInCreationException) {
+ if (ex.contains(BeanCurrentlyInCreationException.class)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean currently in creation on FactoryBean type check: " + ex);
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateQualifier.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateQualifier.java
index f5991ac930..9674e5ccb6 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateQualifier.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateQualifier.java
@@ -32,7 +32,7 @@
@SuppressWarnings("serial")
public class AutowireCandidateQualifier extends BeanMetadataAttributeAccessor {
- public static String VALUE_KEY = "value";
+ public static final String VALUE_KEY = "value";
private final String typeName;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
index 1bf5403455..0b650df823 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -162,8 +162,8 @@ public static Object resolveAutowiringValue(Object autowiringValue, Class> req
* Determine the target type for the generic return type of the given
* generic factory method , where formal type variables are declared
* on the given method itself.
- * For example, given a factory method with the following signature,
- * if {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected
+ *
For example, given a factory method with the following signature, if
+ * {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected
* method for {@code creatProxy()} and an {@code Object[]} array containing
* {@code MyService.class}, {@code resolveReturnTypeForFactoryMethod()} will
* infer that the target return type is {@code MyService}.
@@ -184,9 +184,9 @@ public static Object resolveAutowiringValue(Object autowiringValue, Class> req
* @param method the method to introspect (never {@code null})
* @param args the arguments that will be supplied to the method when it is
* invoked (never {@code null})
- * @param classLoader the ClassLoader to resolve class names against, if necessary
- * (never {@code null})
- * @return the resolved target return type, the standard return type, or {@code null}
+ * @param classLoader the ClassLoader to resolve class names against,
+ * if necessary (never {@code null})
+ * @return the resolved target return type or the standard method return type
* @since 3.2.5
*/
public static Class> resolveReturnTypeForFactoryMethod(Method method, Object[] args, ClassLoader classLoader) {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java
index a1533835f9..6eb6ec5240 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -159,13 +159,25 @@ public BeanDefinitionBuilder setParentName(String parentName) {
}
/**
- * Set the name of the factory method to use for this definition.
+ * Set the name of a static factory method to use for this definition,
+ * to be called on this bean's class.
*/
public BeanDefinitionBuilder setFactoryMethod(String factoryMethod) {
this.beanDefinition.setFactoryMethodName(factoryMethod);
return this;
}
+ /**
+ * Set the name of a non-static factory method to use for this definition,
+ * including the bean name of the factory instance to call the method on.
+ * @since 4.3.6
+ */
+ public BeanDefinitionBuilder setFactoryMethodOnBean(String factoryMethod, String factoryBean) {
+ this.beanDefinition.setFactoryMethodName(factoryMethod);
+ this.beanDefinition.setFactoryBeanName(factoryBean);
+ return this;
+ }
+
/**
* Add an indexed constructor arg value. The current index is tracked internally
* and all additions are at the present point.
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java
index db7ccda800..2ca5393169 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2017 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.
@@ -62,7 +62,7 @@ public int getAutowireMode() {
}
public void setInitMethodName(String initMethodName) {
- this.initMethodName = (StringUtils.hasText(initMethodName)) ? initMethodName : null;
+ this.initMethodName = (StringUtils.hasText(initMethodName) ? initMethodName : null);
}
public String getInitMethodName() {
@@ -70,7 +70,7 @@ public String getInitMethodName() {
}
public void setDestroyMethodName(String destroyMethodName) {
- this.destroyMethodName = (StringUtils.hasText(destroyMethodName)) ? destroyMethodName : null;
+ this.destroyMethodName = (StringUtils.hasText(destroyMethodName) ? destroyMethodName : null);
}
public String getDestroyMethodName() {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java
index f56f0e527a..8a745dab2c 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -69,6 +69,23 @@ public static AbstractBeanDefinition createBeanDefinition(
return bd;
}
+ /**
+ * Generate a bean name for the given top-level bean definition,
+ * unique within the given bean factory.
+ * @param beanDefinition the bean definition to generate a bean name for
+ * @param registry the bean factory that the definition is going to be
+ * registered with (to check for existing bean names)
+ * @return the generated bean name
+ * @throws BeanDefinitionStoreException if no unique name can be generated
+ * for the given bean definition
+ * @see #generateBeanName(BeanDefinition, BeanDefinitionRegistry, boolean)
+ */
+ public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry)
+ throws BeanDefinitionStoreException {
+
+ return generateBeanName(beanDefinition, registry, false);
+ }
+
/**
* Generate a bean name for the given bean definition, unique within the
* given bean factory.
@@ -117,22 +134,6 @@ else if (definition.getFactoryBeanName() != null) {
return id;
}
- /**
- * Generate a bean name for the given top-level bean definition,
- * unique within the given bean factory.
- * @param beanDefinition the bean definition to generate a bean name for
- * @param registry the bean factory that the definition is going to be
- * registered with (to check for existing bean names)
- * @return the generated bean name
- * @throws BeanDefinitionStoreException if no unique name can be generated
- * for the given bean definition
- */
- public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry)
- throws BeanDefinitionStoreException {
-
- return generateBeanName(beanDefinition, registry, false);
- }
-
/**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistry.java
index 326e1fb58c..c99271bc32 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistry.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -55,6 +55,7 @@ public interface BeanDefinitionRegistry extends AliasRegistry {
* @throws BeanDefinitionStoreException if the BeanDefinition is invalid
* or if there is already a BeanDefinition for the specified bean name
* (and we are not allowed to override it)
+ * @see GenericBeanDefinition
* @see RootBeanDefinition
* @see ChildBeanDefinition
*/
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
index 41653c8453..cbb9d2f153 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -115,7 +115,7 @@ public Object instantiate(Constructor> ctor, Object... args) {
Class> subclass = createEnhancedSubclass(this.beanDefinition);
Object instance;
if (ctor == null) {
- instance = BeanUtils.instantiate(subclass);
+ instance = BeanUtils.instantiateClass(subclass);
}
else {
try {
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ChildBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ChildBeanDefinition.java
index 87fc66ba87..a6204ae216 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ChildBeanDefinition.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ChildBeanDefinition.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -53,10 +53,7 @@ public class ChildBeanDefinition extends AbstractBeanDefinition {
* configured through its bean properties and configuration methods.
* @param parentName the name of the parent bean
* @see #setBeanClass
- * @see #setBeanClassName
* @see #setScope
- * @see #setAutowireMode
- * @see #setDependencyCheck
* @see #setConstructorArgumentValues
* @see #setPropertyValues
*/
@@ -174,9 +171,7 @@ public int hashCode() {
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("Child bean with parent '");
- sb.append(this.parentName).append("': ").append(super.toString());
- return sb.toString();
+ return "Child bean with parent '" + this.parentName + "': " + super.toString();
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
index 157bb1cd9e..aa485efc71 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
@@ -609,8 +609,8 @@ public Object run() {
private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
- TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ?
- this.beanFactory.getCustomTypeConverter() : bw);
+ TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
+ TypeConverter converter = (customConverter != null ? customConverter : bw);
BeanDefinitionValueResolver valueResolver =
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
@@ -665,8 +665,8 @@ private ArgumentsHolder createArgumentArray(
BeanWrapper bw, Class>[] paramTypes, String[] paramNames, Object methodOrCtor,
boolean autowiring) throws UnsatisfiedDependencyException {
- TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ?
- this.beanFactory.getCustomTypeConverter() : bw);
+ TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
+ TypeConverter converter = (customConverter != null ? customConverter : bw);
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
Set usedValueHolders =
@@ -769,12 +769,13 @@ private ArgumentsHolder createArgumentArray(
private Object[] resolvePreparedArguments(
String beanName, RootBeanDefinition mbd, BeanWrapper bw, Member methodOrCtor, Object[] argsToResolve) {
- Class>[] paramTypes = (methodOrCtor instanceof Method ?
- ((Method) methodOrCtor).getParameterTypes() : ((Constructor>) methodOrCtor).getParameterTypes());
- TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ?
- this.beanFactory.getCustomTypeConverter() : bw);
+ TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
+ TypeConverter converter = (customConverter != null ? customConverter : bw);
BeanDefinitionValueResolver valueResolver =
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
+ Class>[] paramTypes = (methodOrCtor instanceof Method ?
+ ((Method) methodOrCtor).getParameterTypes() : ((Constructor>) methodOrCtor).getParameterTypes());
+
Object[] resolvedArgs = new Object[argsToResolve.length];
for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
Object argValue = argsToResolve[argIndex];
@@ -854,11 +855,11 @@ static InjectionPoint setCurrentInjectionPoint(InjectionPoint injectionPoint) {
*/
private static class ArgumentsHolder {
- public final Object rawArguments[];
+ public final Object[] rawArguments;
- public final Object arguments[];
+ public final Object[] arguments;
- public final Object preparedArguments[];
+ public final Object[] preparedArguments;
public boolean resolveNecessary = false;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
index 6205fa69cf..c3ea84a0f9 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -32,7 +32,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -44,6 +43,7 @@
import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Provider;
+import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
@@ -52,6 +52,7 @@
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InjectionPoint;
@@ -61,11 +62,13 @@
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
+import org.springframework.beans.factory.config.NamedBeanHolder;
import org.springframework.core.OrderComparator;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
@@ -77,19 +80,17 @@
import org.springframework.util.StringUtils;
/**
- * Default implementation of the
- * {@link org.springframework.beans.factory.ListableBeanFactory} and
- * {@link BeanDefinitionRegistry} interfaces: a full-fledged bean factory
- * based on bean definition objects.
+ * Spring's default implementation of the {@link ConfigurableListableBeanFactory}
+ * and {@link BeanDefinitionRegistry} interfaces: a full-fledged bean factory
+ * based on bean definition metadata, extensible through post-processors.
*
* Typical usage is registering all bean definitions first (possibly read
- * from a bean definition file), before accessing beans. Bean definition lookup
+ * from a bean definition file), before accessing beans. Bean lookup by name
* is therefore an inexpensive operation in a local bean definition table,
- * operating on pre-built bean definition metadata objects.
+ * operating on pre-resolved bean definition metadata objects.
*
- *
Can be used as a standalone bean factory, or as a superclass for custom
- * bean factories. Note that readers for specific bean definition formats are
- * typically implemented separately rather than as bean factory subclasses:
+ *
Note that readers for specific bean definition formats are typically
+ * implemented separately rather than as bean factory subclasses:
* see for example {@link PropertiesBeanDefinitionReader} and
* {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
*
@@ -106,9 +107,10 @@
* @author Phillip Webb
* @author Stephane Nicoll
* @since 16 April 2001
- * @see StaticListableBeanFactory
- * @see PropertiesBeanDefinitionReader
- * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
+ * @see #registerBeanDefinition
+ * @see #addBeanPostProcessor
+ * @see #getBean
+ * @see #resolveDependency
*/
@SuppressWarnings("serial")
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
@@ -264,6 +266,7 @@ public boolean isAllowEagerClassLoading() {
/**
* Set a {@link java.util.Comparator} for dependency Lists and arrays.
+ * @since 4.0
* @see org.springframework.core.OrderComparator
* @see org.springframework.core.annotation.AnnotationAwareOrderComparator
*/
@@ -273,6 +276,7 @@ public void setDependencyComparator(Comparator dependencyComparator) {
/**
* Return the dependency comparator for this BeanFactory (may be {@code null}.
+ * @since 4.0
*/
public Comparator getDependencyComparator() {
return this.dependencyComparator;
@@ -287,11 +291,10 @@ public void setAutowireCandidateResolver(final AutowireCandidateResolver autowir
Assert.notNull(autowireCandidateResolver, "AutowireCandidateResolver must not be null");
if (autowireCandidateResolver instanceof BeanFactoryAware) {
if (System.getSecurityManager() != null) {
- final BeanFactory target = this;
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Object run() {
- ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(target);
+ ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(DefaultListableBeanFactory.this);
return null;
}
}, getAccessControlContext());
@@ -318,7 +321,10 @@ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
DefaultListableBeanFactory otherListableFactory = (DefaultListableBeanFactory) otherFactory;
this.allowBeanDefinitionOverriding = otherListableFactory.allowBeanDefinitionOverriding;
this.allowEagerClassLoading = otherListableFactory.allowEagerClassLoading;
- this.autowireCandidateResolver = otherListableFactory.autowireCandidateResolver;
+ this.dependencyComparator = otherListableFactory.dependencyComparator;
+ // A clone of the AutowireCandidateResolver since it is potentially BeanFactoryAware...
+ setAutowireCandidateResolver(BeanUtils.instantiateClass(getAutowireCandidateResolver().getClass()));
+ // Make resolvable dependencies (e.g. ResourceLoader) available here as well...
this.resolvableDependencies.putAll(otherListableFactory.resolvableDependencies);
}
}
@@ -335,43 +341,15 @@ public T getBean(Class requiredType) throws BeansException {
@Override
public T getBean(Class requiredType, Object... args) throws BeansException {
- Assert.notNull(requiredType, "Required type must not be null");
- String[] beanNames = getBeanNamesForType(requiredType);
- if (beanNames.length > 1) {
- ArrayList autowireCandidates = new ArrayList();
- for (String beanName : beanNames) {
- if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
- autowireCandidates.add(beanName);
- }
- }
- if (autowireCandidates.size() > 0) {
- beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);
- }
- }
- if (beanNames.length == 1) {
- return getBean(beanNames[0], requiredType, args);
- }
- else if (beanNames.length > 1) {
- Map candidates = new HashMap