`

基于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

分享到:
评论

相关推荐

    spring3零配置注解实现Bean定义

    标题《spring3零配置注解实现Bean定义》中蕴含的知识点主要包括Spring框架中的Bean定义配置方法的演进,特别是从Spring 2.5到Spring 3版本的过渡过程中,对于注解方式实现Bean定义的支持如何被引入和优化。...

    spring自动扫描和管理Bean的示例

    组件扫描是指Spring容器会遍历指定的包及其子包,寻找带有特定注解的类,这些注解表明该类是一个Spring Bean。常见的注解有@Component、@Service、@Repository和@Controller,它们分别代表不同层次的组件角色。 1. ...

    Spring学习笔记(9)----让Spring自动扫描和管理Bean

    `@ComponentScan`注解用于指定需要进行Bean扫描的包路径,可以包含多个包,并且可以过滤特定的注解。 ### **XML配置的自动扫描** 在XML配置中,`&lt;context:component-scan&gt;`标签用于启用自动扫描。你可以指定base-...

    Spring 自定义注解的解析

    在Spring框架中,自定义注解的解析是一个强大的特性,允许开发者根据业务需求创建特定的注解,并在Spring容器启动时自动处理这些注解。本文将深入探讨如何在Spring环境中通过`component-scan`配置来处理自定义Java...

    spring3零配置注解实现Bean定义(包括JSR-250、JSR-330)

    - **JSR-250**:这是Java标准组织提出的一套注解规范,其中包含了`@ManagedBean`注解,用于指示一个类作为JavaBean管理,虽然Spring并不直接支持`@ManagedBean`,但在某些场景下可能会用到。 - **JSR-330**:又称为...

    Spring学习之(四)基于注解的组件扫描.pdf

    **组件扫描**的核心思想是:Spring会在启动时自动搜索指定包及其子包下带有特定注解的类,并将这些类作为Spring Bean注册到Spring IoC容器中。 #### 二、组件扫描的配置与使用 1. **指定扫描包路径** 在Spring的...

    Spring2.5_注解介绍(3.0通用)

    - `&lt;context:component-scan&gt;`:这个命名空间元素用于开启组件扫描,Spring会搜索指定包及其子包中的类,寻找带有特定注解的bean定义。`base-package`属性指定了扫描的起始位置。 - `过滤器`:可以使用四种过滤...

    使用 Spring 2.5 基于注解驱动的 Spring MVC

    被此注解标记的类会被Spring容器识别为Bean,并且默认会被扫描到并实例化。 2. **@RequestMapping**:这是一个非常重要的注解,用于定义Controller类和其内部的方法如何映射到URL路径上。它可以作用于类级别,也...

    day38 16-Spring的Bean的装配:注解的方式

    2. **注解Bean定义**:在需要管理的类上添加`@Component`或其派生注解,如`@Service`。 ```java @Service public class UserService { // ... } ``` 3. **依赖注入**:通过`@Autowired`注解来注入依赖。如果依赖...

    Spring定义bean的三种方式和自动注入

    `@Autowired`注解则更加智能,它会根据属性类型找到合适的Bean进行注入,如果存在多个匹配的Bean,可以通过`@Qualifier`注解指定特定的Bean。在上述例子中,`Student`类中的`teacher`字段使用`@Autowired`注解,...

    spring注解笔记

    总结来说,Spring注解提供了声明和注册Bean的简洁方式,通过这些注解,我们能够方便地进行依赖注入和组件扫描,极大地简化了Spring应用的配置工作。同时,理解每个注解背后的原理及使用场景对于开发高效且可维护的...

    Spring扫描器—spring组件扫描使用详解

    Spring组件扫描的原理基于Java的注解处理和反射机制。它会遍历指定包及其子包下的所有类,寻找带有特定注解(如@Service、@Component、@Repository、@Controller等)的类,并将这些类实例化为Spring容器中的bean。...

    spring注解

    Spring 注解是 Spring 框架中的一种强大功能,它允许开发者使用注解来配置和管理 Bean 对象。本文将详细讲解 Spring 注解的含义、类型、使用方法和相关配置。 注册注解处理器 在 Spring 中,需要注册注解处理器来...

    Spring自动扫描无法扫描jar包中bean的解决方法

    在实际开发中,除了上述解决方法,还可以考虑使用其他策略,例如使用`@Import`注解导入特定的配置类,或者在主配置中显式声明Bean,以确保即使在自动扫描失败的情况下也能正常加载必要的组件。同时,持续关注Spring...

    spring4注解

    #### 二、Spring注解图示与分类 ##### 2.1 Spring-Context 模块的注解图 - **@Component**: 用于标记任何Java类作为Spring中的一个组件。该注解通常配合`&lt;context:component-scan&gt;`使用,以便Spring能够自动检测和...

    Spring Bean管理注解方式代码实例

    Spring Bean 管理注解方式代码实例 Spring Bean 管理注解方式代码实例是 Spring 框架中的一种常见的 Bean 管理方式。通过使用注解的方式,可以简化 Bean 的配置和管理过程。这篇文章将详细介绍 Spring Bean 管理...

    Struts2+spring注解配置

    1. `@Component`, `@Service`, `@Repository`, `@Controller` 这些注解用于标记组件类,它们分别对应不同的服务层角色,Spring 会自动扫描并管理这些类。 2. `@Autowired` 用于自动装配 Bean,可以根据类型或属性...

    Spring_mvc注解

    ### Spring MVC 注解详解 #### 一、Spring MVC 注解简介 Spring MVC 是 Spring 框架的一个模块,专门用于构建 Web 应用程序。它实现了 MVC(Model-View-Controller)设计模式,并且提供了丰富的注解支持,使得...

    spring 2.5 IOC 自动扫描,自动注入

    自动扫描是Spring框架的一个强大功能,它允许开发者指定一个或多个包,Spring会自动查找这些包及其子包下的所有带有特定注解(如@Component、@Service、@Repository、@Controller等)的类,然后将这些类注册为bean。...

    spring3.0MVC注解(附实例).

    在Spring MVC中,注解的支持需要通过特定的HandlerMapping和HandlerAdapter组件来实现。`@RequestMapping`注解的处理依赖于`DefaultAnnotationHandlerMapping`和`AnnotationMethodHandlerAdapter`。默认情况下,...

Global site tag (gtag.js) - Google Analytics