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
相关推荐
标题《spring3零配置注解实现Bean定义》中蕴含的知识点主要包括Spring框架中的Bean定义配置方法的演进,特别是从Spring 2.5到Spring 3版本的过渡过程中,对于注解方式实现Bean定义的支持如何被引入和优化。...
组件扫描是指Spring容器会遍历指定的包及其子包,寻找带有特定注解的类,这些注解表明该类是一个Spring Bean。常见的注解有@Component、@Service、@Repository和@Controller,它们分别代表不同层次的组件角色。 1. ...
`@ComponentScan`注解用于指定需要进行Bean扫描的包路径,可以包含多个包,并且可以过滤特定的注解。 ### **XML配置的自动扫描** 在XML配置中,`<context:component-scan>`标签用于启用自动扫描。你可以指定base-...
在Spring框架中,自定义注解的解析是一个强大的特性,允许开发者根据业务需求创建特定的注解,并在Spring容器启动时自动处理这些注解。本文将深入探讨如何在Spring环境中通过`component-scan`配置来处理自定义Java...
- **JSR-250**:这是Java标准组织提出的一套注解规范,其中包含了`@ManagedBean`注解,用于指示一个类作为JavaBean管理,虽然Spring并不直接支持`@ManagedBean`,但在某些场景下可能会用到。 - **JSR-330**:又称为...
**组件扫描**的核心思想是:Spring会在启动时自动搜索指定包及其子包下带有特定注解的类,并将这些类作为Spring Bean注册到Spring IoC容器中。 #### 二、组件扫描的配置与使用 1. **指定扫描包路径** 在Spring的...
- `<context:component-scan>`:这个命名空间元素用于开启组件扫描,Spring会搜索指定包及其子包中的类,寻找带有特定注解的bean定义。`base-package`属性指定了扫描的起始位置。 - `过滤器`:可以使用四种过滤...
被此注解标记的类会被Spring容器识别为Bean,并且默认会被扫描到并实例化。 2. **@RequestMapping**:这是一个非常重要的注解,用于定义Controller类和其内部的方法如何映射到URL路径上。它可以作用于类级别,也...
2. **注解Bean定义**:在需要管理的类上添加`@Component`或其派生注解,如`@Service`。 ```java @Service public class UserService { // ... } ``` 3. **依赖注入**:通过`@Autowired`注解来注入依赖。如果依赖...
`@Autowired`注解则更加智能,它会根据属性类型找到合适的Bean进行注入,如果存在多个匹配的Bean,可以通过`@Qualifier`注解指定特定的Bean。在上述例子中,`Student`类中的`teacher`字段使用`@Autowired`注解,...
总结来说,Spring注解提供了声明和注册Bean的简洁方式,通过这些注解,我们能够方便地进行依赖注入和组件扫描,极大地简化了Spring应用的配置工作。同时,理解每个注解背后的原理及使用场景对于开发高效且可维护的...
Spring组件扫描的原理基于Java的注解处理和反射机制。它会遍历指定包及其子包下的所有类,寻找带有特定注解(如@Service、@Component、@Repository、@Controller等)的类,并将这些类实例化为Spring容器中的bean。...
Spring 注解是 Spring 框架中的一种强大功能,它允许开发者使用注解来配置和管理 Bean 对象。本文将详细讲解 Spring 注解的含义、类型、使用方法和相关配置。 注册注解处理器 在 Spring 中,需要注册注解处理器来...
在实际开发中,除了上述解决方法,还可以考虑使用其他策略,例如使用`@Import`注解导入特定的配置类,或者在主配置中显式声明Bean,以确保即使在自动扫描失败的情况下也能正常加载必要的组件。同时,持续关注Spring...
#### 二、Spring注解图示与分类 ##### 2.1 Spring-Context 模块的注解图 - **@Component**: 用于标记任何Java类作为Spring中的一个组件。该注解通常配合`<context:component-scan>`使用,以便Spring能够自动检测和...
Spring Bean 管理注解方式代码实例 Spring Bean 管理注解方式代码实例是 Spring 框架中的一种常见的 Bean 管理方式。通过使用注解的方式,可以简化 Bean 的配置和管理过程。这篇文章将详细介绍 Spring Bean 管理...
1. `@Component`, `@Service`, `@Repository`, `@Controller` 这些注解用于标记组件类,它们分别对应不同的服务层角色,Spring 会自动扫描并管理这些类。 2. `@Autowired` 用于自动装配 Bean,可以根据类型或属性...
### Spring MVC 注解详解 #### 一、Spring MVC 注解简介 Spring MVC 是 Spring 框架的一个模块,专门用于构建 Web 应用程序。它实现了 MVC(Model-View-Controller)设计模式,并且提供了丰富的注解支持,使得...
自动扫描是Spring框架的一个强大功能,它允许开发者指定一个或多个包,Spring会自动查找这些包及其子包下的所有带有特定注解(如@Component、@Service、@Repository、@Controller等)的类,然后将这些类注册为bean。...
在Spring MVC中,注解的支持需要通过特定的HandlerMapping和HandlerAdapter组件来实现。`@RequestMapping`注解的处理依赖于`DefaultAnnotationHandlerMapping`和`AnnotationMethodHandlerAdapter`。默认情况下,...