欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

通过bean parser实现spring scan groovy文件装载bean

程序员文章站 2022-06-05 23:19:53
...

Spring使用groovy作为bean,官方用了lang标签,但都是一个个文件。Groovy本身编译成class文件后当然可以和Java完全一样可以被component-scan。

 

但是我想实现能够扫描groovy文件,并且groovy文件发生修改时候能够重新load(方便开发环境中提高效率),网上查查了,然后自己摸索了下,简单实现了。

 

思路:

1. 通过NamespaceHandlerSupport自己写一个parser,parser和已有的component-scan的区别就是修改了ClassPathBeanDefinitionScanner的reourceLoader,classloader用GroovyClassLoader,metadataReaderFactory重写了一个支持groovy class的,spring的是用asm去解析class文件(所以他也只支持编译后的)。

 

2. 写个timer监听groovy文件的最后修改时间,发生变化的,就让application context reload。

 

代码献上

 

import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ComponentScanBeanDefinitionParser;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
import org.w3c.dom.Element;

public class ComponentGroovyScanParser extends ComponentScanBeanDefinitionParser {
	
	@Override
	protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
		ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), true);
		scanner.setEnvironment(parserContext.getReaderContext().getEnvironment());
		scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
		scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());

		scanner.addIncludeFilter(new AnnotationTypeFilter(Component.class));

		scanner.setResourceLoader(new PathMatchingResourcePatternResolver(ClassLoaderHolder.gcl));
		scanner.setResourcePattern("**/*.groovy");
		PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(
				ClassLoaderHolder.gcl);
		scanner.setMetadataReaderFactory(new CachingMetadataReaderFactory2(resourcePatternResolver));

		return scanner;
	}
}

 

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.LinkedHashMap;
import java.util.Map;

import org.springframework.asm.Opcodes;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;

public class CachingMetadataReaderFactory2 extends SimpleMetadataReaderFactory {

	/** Default maximum number of entries for the MetadataReader cache: 256 */
	public static final int DEFAULT_CACHE_LIMIT = 256;

	private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;

	@SuppressWarnings("serial")
	private final Map<Resource, MetadataReader> metadataReaderCache = new LinkedHashMap<Resource, MetadataReader>(
			DEFAULT_CACHE_LIMIT, 0.75f, true) {
		@Override
		protected boolean removeEldestEntry(Map.Entry<Resource, MetadataReader> eldest) {
			return size() > getCacheLimit();
		}
	};

	/**
	 * Create a new CachingMetadataReaderFactory for the default class loader.
	 */
	public CachingMetadataReaderFactory2() {
		super();
	}

	/**
	 * Create a new CachingMetadataReaderFactory for the given resource loader.
	 * 
	 * @param resourceLoader
	 *            the Spring ResourceLoader to use (also determines the
	 *            ClassLoader to use)
	 */
	public CachingMetadataReaderFactory2(ResourceLoader resourceLoader) {
		super(resourceLoader);
	}

	/**
	 * Create a new CachingMetadataReaderFactory for the given class loader.
	 * 
	 * @param classLoader
	 *            the ClassLoader to use
	 */
	public CachingMetadataReaderFactory2(ClassLoader classLoader) {
		super(classLoader);
	}

	/**
	 * Specify the maximum number of entries for the MetadataReader cache.
	 * Default is 256.
	 */
	public void setCacheLimit(int cacheLimit) {
		this.cacheLimit = cacheLimit;
	}

	/**
	 * Return the maximum number of entries for the MetadataReader cache.
	 */
	public int getCacheLimit() {
		return this.cacheLimit;
	}

	@Override
	public MetadataReader getMetadataReader(Resource resource) throws IOException {
		if (getCacheLimit() <= 0) {
			return super.getMetadataReader(resource);
		}
		synchronized (this.metadataReaderCache) {
			MetadataReader metadataReader = this.metadataReaderCache.get(resource);
			if (metadataReader == null) {
				metadataReader = genReader(resource);
				this.metadataReaderCache.put(resource, metadataReader);
			}
			return metadataReader;
		}
	}

	@SuppressWarnings("rawtypes")
	private MetadataReader genReader(final Resource resource) throws IOException {
		Class clz = ClassLoaderHolder.gcl.parseClass(resource.getFile());

		final AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(
				super.getResourceLoader().getClassLoader());

		Class[] ll = clz.getInterfaces();
		// must has GroovyObject
		String[] interfaces = new String[ll.length];
		for (int i = 0; i < interfaces.length; i++) {
			interfaces[i] = ll[i].getName();
		}
		visitor.visit(0, Opcodes.ACC_PUBLIC, clz.getName(), null, clz.getSuperclass().getName(), interfaces);

		Annotation[] ll2 = clz.getAnnotations();
		if (ll2.length > 0) {
			for (Annotation ano : ll2) {
				visitor.getAnnotationTypes().add(ano.annotationType().getName());
			}
		}

		MetadataReader reader = new MetadataReader() {
			@Override
			public Resource getResource() {
				return resource;
			}

			@Override
			public ClassMetadata getClassMetadata() {
				return visitor;
			}

			@Override
			public AnnotationMetadata getAnnotationMetadata() {
				return visitor;
			}

		};
		return reader;
	}

	/**
	 * Clear the entire MetadataReader cache, removing all cached class
	 * metadata.
	 */
	public void clearCache() {
		synchronized (this.metadataReaderCache) {
			this.metadataReaderCache.clear();
		}
	}

}

 

import groovy.lang.GroovyClassLoader;

public class ClassLoaderHolder {
	public static GroovyClassLoader gcl = new GroovyClassLoader();

	static {
		gcl.addClasspath("src");
	}
}