`

基于Spring的包含特定注解bean的package扫描工具

阅读更多

Spring框架为Java开发提供了很多便利的工具和编程方式,最近在研究LDAP认证,多数技术问题都已经搞定,但是针对LDAP的ODM(Object-Directory Mapping,也就是LDAP层面的ORM)还有些不足。

 

问题描述:

Spring项目中有一个名为Spring LDAP的子项目,可以简化查询逻辑,但是其中的LDAPTemplate需要做手动的Mapping,另外在查询时默认使用的是类似于SQL中的select *,这样也许会造成很多的网络流量浪费和性能下降。如果需指定返回哪些字段,必须输入一个String[]。这样做的结果就是,同样的一个查询,既要指定字段的String[],又要将返回的字段一个一个地Mapping到Bean属性上,一旦将来字段增加或者减少,需要维护两个地方,增加了出错的几率。于是想到了能否像JPA的注解方式那样来配置ODM,后来查阅相关资料,还真有??使用OdmManagerImplFactoryBean。

首先需要在被映射的对象上增加Entry注解,然后在Bean属性上增加对应的Attribute注解就完成了映射。问题出来了,除了要加入注解外,还要将这些Bean加入到OdmManagerImplFactoryBean的managedClasses属性中,通知管理器哪些Bean属于受管Bean。这有点像早期的Spring对于Hibernate的支持。配置AnnotationSessionFactoryBean的时候需要设置annotatedClasses一样,不过从Spring 2.5.6开始增加了packagesToScan参数设置,它的作用是从指定的包下面扫描全部带有Entity、Embeddable、MappedSuperclass、org.hibernate.annotations.Entity.class注解的Bean,并进行管理。这样做的好处就是你把需要ODM的Bean都统一放到同一个package下,然后让配置去自动扫描,这样在你增加或减少Bean的时候不用再去关心配置中哪些Bean是受管的。

 

代码:

首先声明,这个代码是受org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean中scanPackages方法的启发,借鉴了其中的大部分代码,先贴出来:

 

package net.csdn.blog.chaijunkun.config;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;


public class LoadPackageClasses {
	
	protected final Log logger = LogFactory.getLog(getClass());
	
	private static final String RESOURCE_PATTERN = "/**/*.class";
	
	private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
	
	private List<String> packagesList= new LinkedList<String>();
	
	private List<TypeFilter> typeFilters = new LinkedList<TypeFilter>();
	
	private Set<Class<?>> classSet= new HashSet<Class<?>>();
	
	/**
	 * 构造函数
	 * @param packagesToScan 指定哪些包需要被扫描,支持多个包"package.a,package.b"并对每个包都会递归搜索
	 * @param annotationFilter 指定扫描包中含有特定注解标记的bean,支持多个注解
	 */
	public LoadPackageClasses(String[] packagesToScan, Class<? extends Annotation>... annotationFilter){
		if (packagesToScan != null) {
			for (String packagePath : packagesToScan) {
				this.packagesList.add(packagePath);
			}
		}
		if (annotationFilter != null){
			for (Class<? extends Annotation> annotation : annotationFilter) {
				typeFilters.add(new AnnotationTypeFilter(annotation, false));
			}
		}
	}
	
	/**
	 * 将符合条件的Bean以Class集合的形式返回
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public Set<Class<?>> getClassSet() throws IOException, ClassNotFoundException {
		this.classSet.clear();
		if (!this.packagesList.isEmpty()) {
				for (String pkg : this.packagesList) {
					String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
							ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;
					Resource[] resources = this.resourcePatternResolver.getResources(pattern);
					MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
					for (Resource resource : resources) {
						if (resource.isReadable()) {
							MetadataReader reader = readerFactory.getMetadataReader(resource);
							String className = reader.getClassMetadata().getClassName();
							if (matchesEntityTypeFilter(reader, readerFactory)) {
								this.classSet.add(Class.forName(className));
							}
						}
					}
				}
		}
		//输出日志
		if (logger.isInfoEnabled()){
			for (Class<?> clazz : this.classSet) {
				logger.info(String.format("Found class:%s", clazz.getName()));
			}
		}
		return this.classSet;
	}
	
	

	/**
	 * 检查当前扫描到的Bean含有任何一个指定的注解标记
	 * @param reader
	 * @param readerFactory
	 * @return
	 * @throws IOException
	 */
	private boolean matchesEntityTypeFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
		if (!this.typeFilters.isEmpty()) {
			for (TypeFilter filter : this.typeFilters) {
				if (filter.match(reader, readerFactory)) {
					return true;
				}
			}
		}
		return false;
	}

}

接下来我们就可以来配置了(以下配置为Spring的applicationContext.xml配置节选):

 

 

<bean id="loadPackageClasses" class="net.csdn.blog.chaijunkun.config.LoadPackageClasses">
	<constructor-arg value="net.csdn.blog.chaijunkun.ldap.entity" />
	<constructor-arg>
		<list>
			<value>org.springframework.ldap.odm.annotations.Entry</value>
		</list>
	</constructor-arg>
</bean>
<bean id="odmManager" class="org.springframework.ldap.odm.core.impl.OdmManagerImplFactoryBean">
	<property name="converterManager" ref="converterManager" />
	<property name="contextSource" ref="contextSource" />
	<property name="managedClasses">
		<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
			<property name="targetObject">
				<ref local="loadPackageClasses" />
			</property>
			<property name="targetMethod">
				<value>getClassSet</value>
			</property>
		</bean>
	</property>
</bean>

在上述配置中,指定了扫描的包为net.csdn.blog.chaijunkun.ldap.entity,然后指定了筛选条件是包含org.springframework.ldap.odm.annotations.Entry注解(指定的注解必须在Class级,不能是property级和method级,也就是Bean头部的注解)的所有Bean。

 

因为获取筛选出类的集合要注入到OdmManagerImplFactoryBean中的managedClasses属性,类型为Set<Class<?>>,所以我们需要调用getClassSet()方法,用其返回值进行注入。于是使用了一个MethodInvokingFactoryBean来实现。

 

实验结果表明,加入了这个组件之后确实达到了预期的效果。另外由于LoadPackageClasses本身配置上很灵活,可以用于筛选任何带有特定注解的Bean,所以其他类似的场合也可以使用。当然,我更希望OdmManagerImplFactoryBean中能自带package扫描的配置,这样会让我们省好多事。

文章来源:http://www.itnose.net/detail/6023547.html
更多文章:http://www.itnose.net/type/7.html

分享到:
评论

相关推荐

    springweb3.0MVC注解(附实例)

    -- ②:启动Spring MVC的注解功能,完成请求和注解POJO的映射 --&gt; &lt;bean class="org.springframework.web.servlet.mvc.annotation. AnnotationMethodHandlerAdapter"/&gt; &lt;!-- ③:对模型视图名称的解析,即在模型...

    springjdbc

    -- 自动扫描bean,把作了注解的类转换为bean --&gt; &lt;context:component-scan base-package="com.test.controller" /&gt; &lt;!-- 启动Spring MVC的注解功能,完成请求和注解POJO的映射 --&gt; &lt;bean class="org.spring...

    Spring3中配置DBCP,C3P0,Proxool,Bonecp数据源

    在Spring3中配置数据源,包括DBCP,C3P0,Proxool,Bonecp主要的数据源,里面包含这些数据源的jar文件和依赖文件及配置文件。。 如Bonecp目前听说是最快的数据源,速度是传统的c3p0的25倍, bonecp.properties文件: ...

    Spring组件自动扫描详解及实例代码

    它能从classpath里自动扫描、侦测和实例化具有特定注解的组件。基本的注解是@Component,它标识一个受Spring管理的组件。其他特定的注解有@Repository、@Service和@Controller,它们分别标识了持久层、服务处和表现...

    JAVA Spring框架实现登陆查询 完整搭建框架方法的word文档 包含mysql文件

    .1 修改controller包的自动扫描注解,如图 2 定义jsp文件存放的路径 &lt;property name="prefix" value="/jsp/" /&gt; 如图 3.5修改spring-mabtis.xml 1 修改自动扫描的包 我建的包是com.hqyj.mana就配置 base-...

    spring_MVC源码

    弃用了struts,用spring mvc框架做了几个项目,感觉都不错,而且使用了注解方式,可以省掉一大堆配置文件。本文主要介绍使用注解方式配置的spring mvc,之前写的spring3.0 mvc和rest小例子没有介绍到数据层的内容,...

    spring基础

    这样,当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配(默认按类型匹配)的 Bean,并注入到对应的地方中去。...

    Spring AOP配置源码

    此单元测试基于spring的AbstractJUnit4SpringContextTests,你需要添加spring的关于单元测试的支持 在类上标注@ContextConfiguration(locations="classpath:applicationContext.xml")意思是去classpath路径下加载...

    SpringMVC+Hibernate全注解整合

    &lt;bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt; &lt;property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /&gt; ...

    SpringMVC-SSH全注解

    &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"&gt; &lt;value&gt;com.org.core.entity&lt;/value&gt; ${hibernate....

    springmybatis

    MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plan Old Java Objects,普通的Java对象)映射成数据库中的记录. orm工具的基本思想 无论是用过的hibernate,mybatis,你都可以法相他们有一个...

    spring3.2+strut2+hibernate4

    -- 启用spring注解支持 --&gt; &lt;!-- &lt;bean id="sessionFactory"--&gt; &lt;!-- class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"&gt;--&gt; &lt;!-- &lt;!-- value="classpath:hibernate.cfg.xml"&gt;--&gt; &lt;!--...

    BOS 技术整理

    基于工具使用 SQL 插入中文有乱码问题,设置客户端字符集 gb2312 配置 环境变量 NLS_LANG = SIMPLIFIED CHINESE_CHINA.ZHS16GBK 弹窗了,就表示成功了! 工具补充: Navicat Premium 安装和使用 ...

    SpringMVC+Hibernate实例

    &lt;bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"&gt; &lt;property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/&gt; ...

    springmvcwendang

    (1)扫描基包下的所有注解类 &lt;context:component-scan base-package="com.createnets.springmvc.web" /&gt; (2)新建一个Student类 用于测试注解 (3)配置注解 @Controller @RequestMapping("/list") (3)配置...

    springExperiments

    Spring注解 @SpringBootApplication : @Configuration :将类标记为应用程序上下文的bean定义的源 @EnableAutoConfiguration :告诉Spring引导开始基于类路径和其他bean添加bean。 例如,如果spring-webmvc在类...

    java微信公众号MVC开发框架

    jwx是开源的java公众号开发MVC框架,基于spring配置文件和微信消息或事件注解,通过微信上下文处理一个或多个微信公众号服务请求。目的主要有两个,其一生封装微信请求xml消息为java实体对象,将返回对象转换为xml...

    客户关系管理系统框架搭建(二)

    -- 1 配置注解的自动扫描--&gt; &lt;context:component-scan base-package="cn.itcast.crm"/&gt; &lt;!--3 配置本地化代理工程bean,这是spring整合hibernate的入口 --&gt; &lt;bean id="sessionFactory" class=...

Global site tag (gtag.js) - Google Analytics