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
相关推荐
2000-2021年中国科技统计年鉴(分省年度)面板数据集-最新更新.zip
PPT保护工具PDFeditor专业版-精心整理.zip
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
考研英语真题及详解-精心整理.zip
Jupyter-Notebook
全国电子地图行政区划道路水系数据-最新shp.zip
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
地级市进出口贸易及外资利用数据(297城)-最新.zip
HengCe-18900-2024-2030中国皮革制品市场现状研究分析与发展前景预测报告-样本.docx
猪脚饭超好吃 java制作的小游戏,作为巩固java知识之用.zip
【基于Python的大麦网自动抢票工具的设计与实现】 随着互联网技术的发展,网络购票已经成为人们生活中不可或缺的一部分。尤其是在文化娱乐领域,如音乐会、演唱会、戏剧等活动中,热门演出的门票往往在开售后瞬间就被抢购一空。为了解决这个问题,本论文探讨了一种基于Python的自动抢票工具的设计与实现,旨在提高购票的成功率,减轻用户手动抢票的压力。 Python作为一种高级编程语言,因其简洁明了的语法和丰富的第三方库,成为了开发自动化工具的理想选择。Python的特性使得开发过程高效且易于维护。本论文深入介绍了Python语言的基础知识,包括数据类型、控制结构、函数以及模块化编程思想,这些都是构建抢票工具的基础。 自动化工具在现代社会中广泛应用,尤其在网络爬虫、自动化测试等领域。在抢票工具的设计中,主要利用了自动化工具的模拟用户行为、数据解析和定时任务等功能。本论文详细阐述了如何使用Python中的Selenium库来模拟浏览器操作,通过识别网页元素、触发事件,实现对大麦网购票流程的自动化控制。同时,还讨论了BeautifulSoup和requests库在抓取和解析网页数据中的应用。 大麦网作为国内知名的票务平台,其网站结构和购票流程对于抢票工具的实现至关重要。论文中介绍了大麦网的基本情况,包括其业务模式、用户界面特点以及购票流程,为工具的设计提供了实际背景。 在系统需求分析部分,功能需求主要集中在自动登录、监控余票、自动下单和异常处理等方面。抢票工具需要能够自动填充用户信息,实时监控目标演出的票务状态,并在有票时立即下单。此外,为了应对可能出现的网络延迟或服务器错误,工具还需要具备一定的错误恢复能力。性能需求则关注工具的响应速度和稳定性,要求在大量用户同时使用时仍能保持高效运行。 在系统设计阶段,论文详细描述了整体架构,包括前端用户界面、后端逻辑处理以及与大麦网交互的部分。在实现过程中,采用了多线程技术以提高并发性,确保在抢票关键环节的快速响应。此外,还引入了异常处理机制,以应对网络故障或程序错误。 测试与优化是确保抢票工具质量的关键步骤。论文中提到了不同场景下的测试策略,如压力测试、功能测试和性能测试,以验证工具的有效性和稳定性。同时,通过对抢票算法的不断优化,提高工具的成功率。 论文讨论了该工具可能带来的社会影响,包括对消费者体验的改善、对黄牛现象的抑制以及可能引发的公平性问题。此外,还提出了未来的研究方向,如增加多平台支持、优化抢票策略以及考虑云服务的集成,以进一步提升抢票工具的实用性。 本论文全面介绍了基于Python的大麦网自动抢票工具的设计与实现,从理论到实践,从需求分析到系统优化,为读者提供了一个完整的开发案例,对于学习Python编程、自动化工具设计以及理解网络购票市场的运作具有重要的参考价值。
校园驿站全天候辅助取货管理系统 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B
1970年至2010年美国所有乳制品的供应和利用情况
java基础 java_leetcode题解之Possible Bipartition.java
该开源项目为阿里巴巴数据库事业部精心打造的druid连接池设计源码,包含4689个文件,涵盖4069个Java源文件、297个SQL脚本、102个文本文件以及其他多种文件类型。druid连接池以其独特的监控功能,旨在为数据库连接管理提供高效、可靠的解决方案。项目文件类型丰富,包括HTML、JavaScript、CSS和Shell脚本等,适用于多种开发需求。
Jupyter-Notebook
金融风险管理计算手册(CoVaR)最新版.zip
网上选课系统 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B
Jupyter-Notebook
Jupyter-Notebook