
com.github.fashionbrot.value.AbstractAnnotationInjectedBeanPostProcessor Maven / Gradle / Ivy
package com.github.fashionbrot.value;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static com.github.fashionbrot.util.MarsUtil.resolveGenericType;
import static org.springframework.core.BridgeMethodResolver.findBridgedMethod;
import static org.springframework.core.BridgeMethodResolver.isVisibilityBridgeMethodPair;
import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;
import static org.springframework.core.annotation.AnnotationUtils.getAnnotation;
/**
* @author fashionbrot
* @date 2021/07/28 22:45
*
* Abstract generic {@link BeanPostProcessor} implementation for customized annotation that annotated injected-object.
*
* @param The type of {@link Annotation customized annotation}
*/
public abstract class AbstractAnnotationInjectedBeanPostProcessor extends
InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered,
BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean {
private final static int CACHE_SIZE = Integer.getInteger("", 32);
private final Log logger = LogFactory.getLog(getClass());
private final Class annotationType;
private final ConcurrentMap injectionMetadataCache = new ConcurrentHashMap<>(CACHE_SIZE);
private final ConcurrentMap injectedObjectsCache = new ConcurrentHashMap<>(CACHE_SIZE);
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader classLoader;
private int order = Ordered.LOWEST_PRECEDENCE;
protected AbstractAnnotationInjectedBeanPostProcessor() {
this.annotationType = resolveGenericType(getClass());
}
private static Collection combine(Collection extends T>... elements) {
List allElements = new ArrayList();
for (Collection extends T> e : elements) {
allElements.addAll(e);
}
return allElements;
}
/**
* Annotation type
*
* @return non-null
*/
private final Class getAnnotationType() {
return annotationType;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
"AnnotationInjectedBeanPostProcessor requires a ConfigurableListableBeanFactory");
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getName()
+ " dependencies is failed", ex);
}
return pvs;
}
/**
* Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link A} fields
*
* @param beanClass The {@link Class} of Bean
* @return non-null {@link List}
*/
private List findFieldAnnotationMetadata(final Class> beanClass) {
final List elements = new LinkedList<>();
ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
A annotation = getAnnotation(field, getAnnotationType());
if (annotation != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@" + getAnnotationType().getName() + " is not supported on static fields: " + field);
}
return;
}
elements.add(new AnnotatedFieldElement(field, annotation));
}
}
});
return elements;
}
/**
* Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated {@link A @A} methods
*
* @param beanClass The {@link Class} of Bean
* @return non-null {@link List}
*/
private List findAnnotatedMethodMetadata(final Class> beanClass) {
final List elements = new LinkedList<>();
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
Method bridgedMethod = findBridgedMethod(method);
if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
A annotation = findAnnotation(bridgedMethod, getAnnotationType());
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@" + getAnnotationType().getSimpleName() + " annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
logger.warn("@" + getAnnotationType().getSimpleName() + " annotation should only be used on methods with parameters: " +
method);
}
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
elements.add(new AnnotatedMethodElement(method, pd, annotation));
}
}
});
return elements;
}
private AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class> beanClass) {
Collection fieldElements = findFieldAnnotationMetadata(beanClass);
Collection methodElements = findAnnotatedMethodMetadata(beanClass);
return new AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
private InjectionMetadata findInjectionMetadata(String beanName, Class> clazz, PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
try {
metadata = buildAnnotatedMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
} catch (NoClassDefFoundError err) {
throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
"] for annotation metadata: could not find class that it depends on", err);
}
}
}
}
return metadata;
}
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
if (beanType != null) {
InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
}
@Override
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public void destroy() throws Exception {
for (Object object : injectedObjectsCache.values()) {
if (logger.isInfoEnabled()) {
logger.info(object + " was destroying!");
}
if (object instanceof DisposableBean) {
((DisposableBean) object).destroy();
}
}
injectionMetadataCache.clear();
injectedObjectsCache.clear();
if (logger.isInfoEnabled()) {
logger.info(getClass() + " was destroying!");
}
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
private Environment getEnvironment() {
return environment;
}
private ClassLoader getClassLoader() {
return classLoader;
}
protected ConfigurableListableBeanFactory getBeanFactory() {
return beanFactory;
}
/**
* Gets all injected-objects.
*
* @return non-null {@link Collection}
*/
protected Collection
© 2015 - 2025 Weber Informatics LLC | Privacy Policy