All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * 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.
 * 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.annotation;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

/**
 * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
 * that invokes annotated init and destroy methods. Allows for an annotation
 * alternative to Spring's {@link org.springframework.beans.factory.InitializingBean}
 * and {@link org.springframework.beans.factory.DisposableBean} callback interfaces.
 *
 * 

The actual annotation types that this post-processor checks for can be * configured through the {@link #setInitAnnotationType "initAnnotationType"} * and {@link #setDestroyAnnotationType "destroyAnnotationType"} properties. * Any custom annotation can be used, since there are no required annotation * attributes. * *

Init and destroy annotations may be applied to methods of any visibility: * public, package-protected, protected, or private. Multiple such methods * may be annotated, but it is recommended to only annotate one single * init method and destroy method, respectively. * *

Spring's {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor} * supports the JSR-250 {@link javax.annotation.PostConstruct} and {@link javax.annotation.PreDestroy} * annotations out of the box, as init annotation and destroy annotation, respectively. * Furthermore, it also supports the {@link javax.annotation.Resource} annotation * for annotation-driven injection of named beans. * * @author Juergen Hoeller * @since 2.5 * @see #setInitAnnotationType * @see #setDestroyAnnotationType */ @SuppressWarnings("serial") public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable { protected transient Log logger = LogFactory.getLog(getClass()); @Nullable private Class initAnnotationType; @Nullable private Class destroyAnnotationType; private int order = Ordered.LOWEST_PRECEDENCE; @Nullable private final transient Map, LifecycleMetadata> lifecycleMetadataCache = new ConcurrentHashMap<>(256); /** * Specify the init annotation to check for, indicating initialization * methods to call after configuration of a bean. *

Any custom annotation can be used, since there are no required * annotation attributes. There is no default, although a typical choice * is the JSR-250 {@link javax.annotation.PostConstruct} annotation. */ public void setInitAnnotationType(Class initAnnotationType) { this.initAnnotationType = initAnnotationType; } /** * Specify the destroy annotation to check for, indicating destruction * methods to call when the context is shutting down. *

Any custom annotation can be used, since there are no required * annotation attributes. There is no default, although a typical choice * is the JSR-250 {@link javax.annotation.PreDestroy} annotation. */ public void setDestroyAnnotationType(Class destroyAnnotationType) { this.destroyAnnotationType = destroyAnnotationType; } public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { LifecycleMetadata metadata = findLifecycleMetadata(beanType); metadata.checkConfigMembers(beanDefinition); } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass()); try { metadata.invokeInitMethods(bean, beanName); } catch (InvocationTargetException ex) { throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Failed to invoke init method", ex); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException { LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass()); try { metadata.invokeDestroyMethods(bean, beanName); } catch (InvocationTargetException ex) { String msg = "Destroy method on bean with name '" + beanName + "' threw an exception"; if (logger.isDebugEnabled()) { logger.warn(msg, ex.getTargetException()); } else { logger.warn(msg + ": " + ex.getTargetException()); } } catch (Throwable ex) { logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex); } } @Override public boolean requiresDestruction(Object bean) { return findLifecycleMetadata(bean.getClass()).hasDestroyMethods(); } private LifecycleMetadata findLifecycleMetadata(Class clazz) { if (this.lifecycleMetadataCache == null) { // Happens after deserialization, during destruction... return buildLifecycleMetadata(clazz); } // Quick check on the concurrent map first, with minimal locking. LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz); if (metadata == null) { synchronized (this.lifecycleMetadataCache) { metadata = this.lifecycleMetadataCache.get(clazz); if (metadata == null) { metadata = buildLifecycleMetadata(clazz); this.lifecycleMetadataCache.put(clazz, metadata); } return metadata; } } return metadata; } private LifecycleMetadata buildLifecycleMetadata(final Class clazz) { List initMethods = new ArrayList<>(); List destroyMethods = new ArrayList<>(); Class targetClass = clazz; do { final List currInitMethods = new ArrayList<>(); final List currDestroyMethods = new ArrayList<>(); ReflectionUtils.doWithLocalMethods(targetClass, method -> { if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) { LifecycleElement element = new LifecycleElement(method); currInitMethods.add(element); if (logger.isTraceEnabled()) { logger.trace("Found init method on class [" + clazz.getName() + "]: " + method); } } if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) { currDestroyMethods.add(new LifecycleElement(method)); if (logger.isTraceEnabled()) { logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method); } } }); initMethods.addAll(0, currInitMethods); destroyMethods.addAll(currDestroyMethods); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new LifecycleMetadata(clazz, initMethods, destroyMethods); } //--------------------------------------------------------------------- // Serialization support //--------------------------------------------------------------------- private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { // Rely on default serialization; just initialize state after deserialization. ois.defaultReadObject(); // Initialize transient fields. this.logger = LogFactory.getLog(getClass()); } /** * Class representing information about annotated init and destroy methods. */ private class LifecycleMetadata { private final Class targetClass; private final Collection initMethods; private final Collection destroyMethods; @Nullable private volatile Set checkedInitMethods; @Nullable private volatile Set checkedDestroyMethods; public LifecycleMetadata(Class targetClass, Collection initMethods, Collection destroyMethods) { this.targetClass = targetClass; this.initMethods = initMethods; this.destroyMethods = destroyMethods; } public void checkConfigMembers(RootBeanDefinition beanDefinition) { Set checkedInitMethods = new LinkedHashSet<>(this.initMethods.size()); for (LifecycleElement element : this.initMethods) { String methodIdentifier = element.getIdentifier(); if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) { beanDefinition.registerExternallyManagedInitMethod(methodIdentifier); checkedInitMethods.add(element); if (logger.isTraceEnabled()) { logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + element); } } } Set checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size()); for (LifecycleElement element : this.destroyMethods) { String methodIdentifier = element.getIdentifier(); if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) { beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier); checkedDestroyMethods.add(element); if (logger.isTraceEnabled()) { logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + element); } } } this.checkedInitMethods = checkedInitMethods; this.checkedDestroyMethods = checkedDestroyMethods; } public void invokeInitMethods(Object target, String beanName) throws Throwable { Collection checkedInitMethods = this.checkedInitMethods; Collection initMethodsToIterate = (checkedInitMethods != null ? checkedInitMethods : this.initMethods); if (!initMethodsToIterate.isEmpty()) { for (LifecycleElement element : initMethodsToIterate) { if (logger.isTraceEnabled()) { logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod()); } element.invoke(target); } } } public void invokeDestroyMethods(Object target, String beanName) throws Throwable { Collection checkedDestroyMethods = this.checkedDestroyMethods; Collection destroyMethodsToUse = (checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods); if (!destroyMethodsToUse.isEmpty()) { for (LifecycleElement element : destroyMethodsToUse) { if (logger.isTraceEnabled()) { logger.trace("Invoking destroy method on bean '" + beanName + "': " + element.getMethod()); } element.invoke(target); } } } public boolean hasDestroyMethods() { Collection checkedDestroyMethods = this.checkedDestroyMethods; Collection destroyMethodsToUse = (checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods); return !destroyMethodsToUse.isEmpty(); } } /** * Class representing injection information about an annotated method. */ private static class LifecycleElement { private final Method method; private final String identifier; public LifecycleElement(Method method) { if (method.getParameterCount() != 0) { throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method); } this.method = method; this.identifier = (Modifier.isPrivate(method.getModifiers()) ? ClassUtils.getQualifiedMethodName(method) : method.getName()); } public Method getMethod() { return this.method; } public String getIdentifier() { return this.identifier; } public void invoke(Object target) throws Throwable { ReflectionUtils.makeAccessible(this.method); this.method.invoke(target, (Object[]) null); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof LifecycleElement)) { return false; } LifecycleElement otherElement = (LifecycleElement) other; return (this.identifier.equals(otherElement.identifier)); } @Override public int hashCode() { return this.identifier.hashCode(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy