Package org.springframework.beans.factory.annotation

Source Code of org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata

/*
* Copyright 2002-2012 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.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
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.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.
*
* <p>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.
*
* <p>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.
*
* <p>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());

  private Class<? extends Annotation> initAnnotationType;

  private Class<? extends Annotation> destroyAnnotationType;

  private int order = Ordered.LOWEST_PRECEDENCE;

  private transient final Map<Class<?>, LifecycleMetadata> lifecycleMetadataCache =
      new ConcurrentHashMap<Class<?>, LifecycleMetadata>(64);


  /**
   * Specify the init annotation to check for, indicating initialization
   * methods to call after configuration of a bean.
   * <p>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<? extends Annotation> initAnnotationType) {
    this.initAnnotationType = initAnnotationType;
  }

  /**
   * Specify the destroy annotation to check for, indicating destruction
   * methods to call when the context is shutting down.
   * <p>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<? extends Annotation> destroyAnnotationType) {
    this.destroyAnnotationType = destroyAnnotationType;
  }

  public void setOrder(int order) {
    this.order = order;
  }

  public int getOrder() {
    return this.order;
  }


  public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    if (beanType != null) {
      LifecycleMetadata metadata = findLifecycleMetadata(beanType);
      metadata.checkConfigMembers(beanDefinition);
    }
  }

  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, "Couldn't invoke init method", ex);
    }
    return bean;
  }

  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    return bean;
  }

  public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    try {
      metadata.invokeDestroyMethods(bean, beanName);
    }
    catch (InvocationTargetException ex) {
      String msg = "Invocation of destroy method failed on bean with name '" + beanName + "'";
      if (logger.isDebugEnabled()) {
        logger.warn(msg, ex.getTargetException());
      }
      else {
        logger.warn(msg + ": " + ex.getTargetException());
      }
    }
    catch (Throwable ex) {
      logger.error("Couldn't invoke destroy method on bean with name '" + beanName + "'", ex);
    }
  }


  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(Class<?> clazz) {
    final boolean debug = logger.isDebugEnabled();
    LinkedList<LifecycleElement> initMethods = new LinkedList<LifecycleElement>();
    LinkedList<LifecycleElement> destroyMethods = new LinkedList<LifecycleElement>();
    Class<?> targetClass = clazz;

    do {
      LinkedList<LifecycleElement> currInitMethods = new LinkedList<LifecycleElement>();
      LinkedList<LifecycleElement> currDestroyMethods = new LinkedList<LifecycleElement>();
      for (Method method : targetClass.getDeclaredMethods()) {
        if (this.initAnnotationType != null) {
          if (method.getAnnotation(this.initAnnotationType) != null) {
            LifecycleElement element = new LifecycleElement(method);
            currInitMethods.add(element);
            if (debug) {
              logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
            }
          }
        }
        if (this.destroyAnnotationType != null) {
          if (method.getAnnotation(this.destroyAnnotationType) != null) {
            currDestroyMethods.add(new LifecycleElement(method));
            if (debug) {
              logger.debug("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<LifecycleElement> initMethods;

    private final Collection<LifecycleElement> destroyMethods;

    private volatile Set<LifecycleElement> checkedInitMethods;

    private volatile Set<LifecycleElement> checkedDestroyMethods;

    public LifecycleMetadata(Class<?> targetClass, Collection<LifecycleElement> initMethods,
        Collection<LifecycleElement> destroyMethods) {

      this.targetClass = targetClass;
      this.initMethods = initMethods;
      this.destroyMethods = destroyMethods;
    }

    public void checkConfigMembers(RootBeanDefinition beanDefinition) {
      Set<LifecycleElement> checkedInitMethods = new LinkedHashSet<LifecycleElement>(this.initMethods.size());
      for (LifecycleElement element : this.initMethods) {
        String methodIdentifier = element.getIdentifier();
        if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {
          beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
          checkedInitMethods.add(element);
          if (logger.isDebugEnabled()) {
            logger.debug("Registered init method on class [" + this.targetClass.getName() + "]: " + element);
          }
        }
      }
      Set<LifecycleElement> checkedDestroyMethods = new LinkedHashSet<LifecycleElement>(this.destroyMethods.size());
      for (LifecycleElement element : this.destroyMethods) {
        String methodIdentifier = element.getIdentifier();
        if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {
          beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
          checkedDestroyMethods.add(element);
          if (logger.isDebugEnabled()) {
            logger.debug("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<LifecycleElement> initMethodsToIterate =
          (this.checkedInitMethods != null ? this.checkedInitMethods : this.initMethods);
      if (!initMethodsToIterate.isEmpty()) {
        boolean debug = logger.isDebugEnabled();
        for (LifecycleElement element : initMethodsToIterate) {
          if (debug) {
            logger.debug("Invoking init method on bean '" + beanName + "': " + element.getMethod());
          }
          element.invoke(target);
        }
      }
    }

    public void invokeDestroyMethods(Object target, String beanName) throws Throwable {
      Collection<LifecycleElement> destroyMethodsToIterate =
          (this.checkedDestroyMethods != null ? this.checkedDestroyMethods : this.destroyMethods);
      if (!destroyMethodsToIterate.isEmpty()) {
        boolean debug = logger.isDebugEnabled();
        for (LifecycleElement element : destroyMethodsToIterate) {
          if (debug) {
            logger.debug("Invoking destroy method on bean '" + beanName + "': " + element.getMethod());
          }
          element.invoke(target);
        }
      }
    }
  }


  /**
   * 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.getParameterTypes().length != 0) {
        throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method);
      }
      this.method = method;
      this.identifier = (Modifier.isPrivate(method.getModifiers()) ?
          method.getDeclaringClass() + "." + method.getName() : 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();
    }
  }

}
TOP

Related Classes of org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.