package com.mycompany.cevent.config.reload; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.annotation.RestController; import javax.annotation.PostConstruct; import javax.inject.Inject; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Reloads Spring Beans. */ public class SpringReloader { private static final Logger log = LoggerFactory.getLogger(SpringReloader.class); private ConfigurableApplicationContext applicationContext; public SpringReloader(ConfigurableApplicationContext applicationContext) { log.debug("Hot reloading Spring Beans enabled"); this.applicationContext = applicationContext; } public void reloadEvent(String typename, Class<?> clazz) { log.trace("Hot reloading - checking if this is a Spring bean: " + typename); Annotation annotation = AnnotationUtils.findAnnotation(clazz, Component.class); if (annotation == null) { annotation = AnnotationUtils.findAnnotation(clazz, RestController.class); } if (annotation == null) { annotation = AnnotationUtils.findAnnotation(clazz, Controller.class); } if (annotation == null) { annotation = AnnotationUtils.findAnnotation(clazz, Service.class); } if (annotation == null) { annotation = AnnotationUtils.findAnnotation(clazz, Repository.class); } if (annotation != null) { log.debug("This is a Spring Bean, annotation=" + annotation.annotationType()); try { Object beanInstance = applicationContext.getBean(clazz); log.debug("Existing bean, autowiring fields"); // We only support autowiring on fields if (AopUtils.isCglibProxy(beanInstance)) { log.trace("This is a CGLIB proxy, getting the real object"); beanInstance = ((Advised) beanInstance).getTargetSource().getTarget(); } else if (AopUtils.isJdkDynamicProxy(beanInstance)) { log.trace("This is a JDK proxy, getting the real object"); beanInstance = ((Advised) beanInstance).getTargetSource().getTarget(); } Field[] fields = beanInstance.getClass().getDeclaredFields(); for (Field field : fields) { Annotation[] annotations = field.getDeclaredAnnotations(); if (AnnotationUtils.getAnnotation(field, Inject.class) != null || AnnotationUtils.getAnnotation(field, Autowired.class) != null) { log.debug("@Inject annotation found on field {}", field.getName()); Object beanToInject = applicationContext.getBean(field.getType()); ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, beanInstance, beanToInject); } } // TODO check aspects, at least @Transactional and @RolesAllowed // aspects already work at the class level, ie @Transactional at the class level works Method[] methods = beanInstance.getClass().getDeclaredMethods(); for (Method method : methods) { Annotation[] annotations = method.getDeclaredAnnotations(); if (AnnotationUtils.getAnnotation(method, PostConstruct.class) != null) { log.debug("@PostConstruct annotation found on method {}", method.getName()); method.invoke(beanInstance); } } log.debug("Spring bean reloaded: {}", beanInstance.getClass()); } catch (NoUniqueBeanDefinitionException nbde) { log.warn("There are several instances of {}, reloading is not yet supported for non-unique beans", typename); } catch (NoSuchBeanDefinitionException nsbde) { log.info("Bean {} is not registered yet, creating it", typename); // TODO manage new beans // Spring Loaded doesn't send events for new classes, so it might not be possible } catch (Exception e) { log.warn("Could not hot reload Spring bean!"); e.printStackTrace(); } } else { log.trace("This class does not have Spring annotations, it is not considered a Spring Bean"); } } }