content: package com.ctrip.framework.apollo.spring.annotation;
  
  import java.lang.reflect.Field;
  import java.lang.reflect.Method;
  
  import org.springframework.beans.BeansException;
  import org.springframework.beans.factory.config.BeanPostProcessor;
  import org.springframework.core.Ordered;
  import org.springframework.core.PriorityOrdered;
  import org.springframework.core.annotation.AnnotationUtils;
  import org.springframework.util.ReflectionUtils;
  
  import com.ctrip.framework.apollo.Config;
  import com.ctrip.framework.apollo.ConfigChangeListener;
  import com.ctrip.framework.apollo.ConfigService;
  import com.ctrip.framework.apollo.model.ConfigChangeEvent;
  import com.google.common.base.Preconditions;
  
  /**
   * Apollo Annotation Processor for Spring Application
   *
   * @author Jason Song(song_s@ctrip.com)
   */
  public class ApolloAnnotationProcessor implements BeanPostProcessor, PriorityOrdered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      Class clazz = bean.getClass();
      processFields(bean, clazz.getDeclaredFields());
      processMethods(bean, clazz.getDeclaredMethods());
      return bean;
    }
  
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      return bean;
    }
  
    private void processFields(Object bean, Field[] declaredFields) {
      for (Field field : declaredFields) {
        ApolloConfig annotation = AnnotationUtils.getAnnotation(field, ApolloConfig.class);
        if (annotation == null) {
          continue;
        }
  
        Preconditions.checkArgument(Config.class.isAssignableFrom(field.getType()),
            "Invalid type: %s for field: %s, should be Config", field.getType(), field);
  
        String namespace = annotation.value();
        Config config = ConfigService.getConfig(namespace);
  
        ReflectionUtils.makeAccessible(field);
        ReflectionUtils.setField(field, bean, config);
      }
    }
  
    private void processMethods(final Object bean, Method[] declaredMethods) {
      for (final Method method : declaredMethods) {
        ApolloConfigChangeListener annotation = AnnotationUtils.findAnnotation(method, ApolloConfigChangeListener.class);
        if (annotation == null) {
          continue;
        }
  
        Class<?>[] parameterTypes = method.getParameterTypes();
        Preconditions.checkArgument(parameterTypes.length == 1,
            "Invalid number of parameters: %s for method: %s, should be 1", parameterTypes.length, method);
        Preconditions.checkArgument(ConfigChangeEvent.class.isAssignableFrom(parameterTypes[0]),
            "Invalid parameter type: %s for method: %s, should be ConfigChangeEvent", parameterTypes[0], method);
  
        ReflectionUtils.makeAccessible(method);
        String[] namespaces = annotation.value();
        for (String namespace : namespaces) {
          Config config = ConfigService.getConfig(namespace);
  
          config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
              ReflectionUtils.invokeMethod(method, bean, changeEvent);
            }
          });
        }
      }
    }
  
    @Override
    public int getOrder() {
      //make it as late as possible
      return Ordered.LOWEST_PRECEDENCE;
    }
  }
  