package org.lpw.tephra.bean; import org.lpw.tephra.storage.StorageListener; import org.lpw.tephra.storage.Storages; import org.lpw.tephra.util.Context; import org.lpw.tephra.util.Converter; import org.lpw.tephra.util.Generator; import org.lpw.tephra.util.Io; import org.lpw.tephra.util.Logger; import org.lpw.tephra.util.Validator; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import javax.inject.Inject; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @author lpw */ @Component("tephra.bean.class-reloader") public class ClassReloaderImpl implements ClassReloader, StorageListener, ApplicationContextAware { @Inject private Converter converter; @Inject private Context context; @Inject private Io io; @Inject private Validator validator; @Inject private Generator generator; @Inject private Logger logger; @Inject private Container container; @Value("${tephra.bean.reload.class-path:}") private String classPath; private List<ClassLoader> loaders; private List<String> names; private Map<Class<?>, List<Injecter>> injecters; private ApplicationContext applicationContext; @Override public boolean isReloadEnable(String name) { return names.contains(name); } @Override public String getClassPath() { return context.getAbsolutePath(classPath); } @Override public String getStorageType() { return Storages.TYPE_DISK; } @Override public String[] getScanPathes() { return validator.isEmpty(classPath) ? null : new String[]{classPath + "/name"}; } @Override public void onStorageChanged(String path, String absolutePath) { if ((names = names(absolutePath)) == null) return; if (logger.isInfoEnable()) logger.info("重新载入类:{}", names); if (loaders == null) { loaders = new ArrayList<>(); loaders.add(applicationContext.getClassLoader()); } if (injecters == null) { injecters = new ConcurrentHashMap<>(); for (String name : container.getBeanNames()) inject(container.getBeanClass(name), name, null); } ClassLoader loader = new DynamicClassLoader(loaders.get(loaders.size() - 1)); names.forEach((name) -> load(loader, name)); loaders.add(loader); } private List<String> names(String absolutePath) { String names = io.readAsString(absolutePath).trim(); if (validator.isEmpty(names)) return null; List<String> list = new ArrayList<>(); for (String name : converter.toArray(names, "\n")) if (name.trim().length() > 0) list.add(name.trim()); io.write(absolutePath, new byte[0]); return list; } private void inject(Class<?> beanClass, String beanName, Object bean) { for (Field field : beanClass.getFields()) { field.setAccessible(true); if (field.getAnnotation(Inject.class) == null) continue; try { Class<?> key = field.getType(); boolean collection = isCollection(key); if (collection) { Type type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; if (type instanceof Class) key = (Class<?>) type; else continue; } List<Injecter> list = injecters.get(key); if (list == null) list = new ArrayList<>(); list.add(new Injecter(bean == null ? container.getBean(beanName) : bean, field, collection)); injecters.put(key, list); } catch (Exception e) { logger.warn(e, "解析[{}]属性[{}]依赖时发生异常!", beanClass, field.getName()); } } } private boolean isCollection(Class<?> clazz) { try { return clazz.equals(clazz.asSubclass(Collection.class)); } catch (Exception e) { return false; } } private void load(ClassLoader loader, String name) { try { DefaultListableBeanFactory lbf = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); BeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(null, name, loader); String dynamicBeanName = generator.uuid(); lbf.registerBeanDefinition(dynamicBeanName, bd); Object bean = lbf.getBean(dynamicBeanName); String beanName = getBeanName(bean.getClass()); Object oldBean = null; if (beanName != null) { oldBean = container.getBean(beanName); container.mapBeanName(beanName, dynamicBeanName); } inject(bean.getClass(), null, bean); inject(bean, oldBean); } catch (Exception e) { logger.warn(e, "重新载入[{}]时发生异常!", name); } } private String getBeanName(Class<?> clazz) { Component component = clazz.getAnnotation(Component.class); if (component != null) return component.value(); Repository repository = clazz.getAnnotation(Repository.class); if (repository != null) return repository.value(); Service service = clazz.getAnnotation(Service.class); if (service != null) return service.value(); Controller controller = clazz.getAnnotation(Controller.class); if (controller != null) return controller.value(); return null; } @SuppressWarnings("unchecked") private void inject(Object bean, Object oldBean) throws IllegalArgumentException, IllegalAccessException { for (Class<?> key : injecters.keySet()) { if (!key.isInstance(bean)) continue; for (Injecter injecter : injecters.get(key)) { Object value = bean; if (injecter.isCollection()) { Collection<Object> collection = (Collection<Object>) injecter.getField().get(injecter.getBean()); if (oldBean != null) collection.remove(oldBean); collection.add(bean); value = collection; } injecter.getField().set(injecter.getBean(), value); } } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }