精华帖 (5) :: 良好帖 (15) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-05-30
<property name="annotatedClasses"> <list><value>com.systop.common.core.dao.testmodel.TestDept</value></list> </property> <property name="mappingLocations"> <list><value>classpath*:org/jbpm/**/*.hbm.xml</value></list> </property> Spring可以根据mappingLocations属性中定义的Path Pattern自动加载hbm文件,但是对于annotatedClasses则只能一个一个的苦恼的写上去。无论是Hibernate还是Spring,都不能自动的加载某个package下的Anntated Classes。这样,一旦项目需要重构或者增加/减少Tables就会带来一些麻烦。尤其是对于那些已经打包的应用来说更是如此。 能不能让Spring自动加载AnnotatedClasses呢,我想到了Spring2.5中component-scan,于是便照猫画虎的写了一个AnnotationSessionFactoryBean的子类: package com.systop.common.core.dao.hibernate; import java.io.IOException; import java.util.HashSet; import java.util.Set; import javax.persistence.Entity; import org.apache.oro.text.regex.MalformedPatternException; import org.apache.oro.text.regex.Pattern; import org.apache.oro.text.regex.PatternCompiler; import org.apache.oro.text.regex.PatternMatcher; import org.apache.oro.text.regex.Perl5Compiler; import org.apache.oro.text.regex.Perl5Matcher; import org.hibernate.HibernateException; import org.hibernate.cfg.AnnotationConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.orm.hibernate3.annotation.AnnotationSessionFactoryBean; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import com.systop.common.core.exception.ApplicationException; @SuppressWarnings("unchecked") public class AnnotationSessionFactoryBeanEx extends AnnotationSessionFactoryBean { private static final Logger logger = LoggerFactory .getLogger(AnnotationSessionFactoryBeanEx.class); /** * The locations of the hibernate enity class files. They are often some of the string with * Sping-style resource. A ".class" subfix can make the scaning more precise. * <p> example: * <pre> * classpath*:com/systop/** /model/*.class * </pre> */ private String[] annotatedClassesLocations; /** * Which classes are not included in the session. * They are some of the regular expression. */ private String[] excludedClassesRegexPatterns; /** * @param annotatedClassesLocations the annotatedClassesLocations to set */ public void setAnnotatedClassesLocations(String[] annotatedClassesLocations) { this.annotatedClassesLocations = annotatedClassesLocations; } /** * @see AnnotationSessionFactoryBean#postProcessAnnotationConfiguration(org.hibernate.cfg.AnnotationConfiguration) */ @Override protected void postProcessAnnotationConfiguration(AnnotationConfiguration config) throws HibernateException { Set<Class> annClasses = scanAnnotatedClasses(); //Scan enity classes. // Add entity classes to the configuration. if (!CollectionUtils.isEmpty(annClasses)) { for (Class annClass : annClasses) { config.addAnnotatedClass(annClass); } } } /** * Scan annotated hibernate classes in the locations. * @return Set of the annotated classes, if no matched class, return empty Set. */ private Set<Class> scanAnnotatedClasses() { ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory( resourcePatternResolver); Set<Class> annotatedClasses = new HashSet<Class>(); if (annotatedClassesLocations != null) { try { for (String annClassesLocation : annotatedClassesLocations) { //Resolve the resources Resource[] resources = resourcePatternResolver.getResources(annClassesLocation); for (Resource resource : resources) { MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource); String className = metadataReader.getClassMetadata().getClassName(); //If the class is hibernate enity class, and it does not match the excluded class patterns. if (isEntityClass(metadataReader) && !isExcludedClass(className)) { Class clazz = ClassUtils.forName(className); annotatedClasses.add(clazz); logger.debug("A entity class has been found. \n({})", clazz.getName()); } } } } catch (IOException e) { logger.error("I/O failure during classpath scanning, ({})", e.getMessage()); e.printStackTrace(); throw new ApplicationException(e); } catch (ClassNotFoundException e) { logger.error("Class not found, ({})", e.getMessage()); e.printStackTrace(); throw new ApplicationException(e); } catch (LinkageError e) { logger.error("LinkageError ({})", e.getMessage()); e.printStackTrace(); throw new ApplicationException(e); } } return annotatedClasses; } /** * @return True if the given MetadataReader shows * that the class is annotated by <code>javax.persistence.Enity</code> */ private boolean isEntityClass(MetadataReader metadataReader) { Set<String> annTypes = metadataReader.getAnnotationMetadata().getAnnotationTypes(); if (CollectionUtils.isEmpty(annTypes)) { return false; } return annTypes.contains(Entity.class.getName()); } /** * * @return True if the given class name match the excluded class patterns. */ private boolean isExcludedClass(String className) { if (excludedClassesRegexPatterns == null) { // All class is included. return false; } PatternCompiler compiler = new Perl5Compiler(); PatternMatcher matcher = new Perl5Matcher(); try { for (String regex : excludedClassesRegexPatterns) { //Test each patterns. logger.debug("Pattern is: {}", regex); Pattern pattern = compiler.compile(regex); if (matcher.matches(className, pattern)) { logger.debug("class [{}], matches [{}], so it is excluded.", className, pattern .getPattern()); return true; } } } catch (MalformedPatternException e) { logger.warn("Malformed pattern [{}]", e.getMessage()); } return false; } /** * @param exculdePatterns the exculdePatterns to set */ public void setExcludedClassesRegexPatterns(String[] excludedClassesRegexPatterns) { this.excludedClassesRegexPatterns = excludedClassesRegexPatterns; } } 在Spring的配置文件中这样写: <bean id="sessionFactory" class="com.systop.common.core.dao.hibernate.AnnotationSessionFactoryBeanEx"> <property name="dataSource" ref="dataSource"/> <property name="annotatedClassesLocations"> <list> <value>classpath*:com/systop/**/model/*.class</value> </list> </property> <!--用正则表达式匹配不被scan的类--> <property name="excludedClassesRegexPatterns"> <list> <value><![CDATA[^[\w\.]+Test[\w]+$]]></value> </list> </property> </bean> 好了,一劳永逸! 哦,对了,提醒一下,上述代码使用了Spring2.5中的一些API,另外还有apache oro的正则表达式API。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-06-02
多数时候,Domain类会在项目开始阶段得到确定,此时,annotatedClasses属性也就确定了,如果项目需求比较固定,那么上述改进没有什么明显的优势,但是,下列情况下就不同了。
1.如果项目需要重构。 2.如果需求模糊或者只确定了一部分需求,项目需要经常的添加一些Domain 3.如果公司有一个可复用模块库,里面包含权限、工作流等通用模块,这个复用库以jar包的形式提供给各个项目使用,spring的配置文件也在jar中。 |
|
返回顶楼 | |
发表时间:2008-10-07
好方法,我将它加到springside(www.springside.org.cn)里可以吗?
|
|
返回顶楼 | |
发表时间:2008-10-07
未发布的spring2.5.6中已经实现该功能,好像是增加一个packageToScan属性.
<bean id="sessionFactory" class="cn.org.rapid_framework.spring.hibernate3.annotation.ScanAnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- annotatedScanPackages可以自动搜索某个package的全部标记@Entity class --> <property name="annotatedScanPackages"> <list> <value>com.company.demo.model</value> </list> </property> </bean>
spring2.5.5中还对Ibatis也实现的类似的功能自动搜索并加载SqlMap.xml <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="mappingLocations"> <value>classpath:/org/company/demo/model/**/*SqlMap.xml</value> </property> <property name="dataSource" ref="dataSource"/> </bean>
等待spring2.5.6发布时rapid-framework下一版本会删除ScanAnnotationSessionFactoryBean类,使用官方实现.
|
|
返回顶楼 | |
发表时间:2008-10-08
江南白衣 写道 好方法,我将它加到springside(www.springside.org.cn)里可以吗? 当然,很荣幸,经常参考springside,很高兴能提供一点帮助。 |
|
返回顶楼 | |
发表时间:2008-10-08
badqiu 写道 未发布的spring2.5.6中已经实现该功能,好像是增加一个packageToScan属性. 而rapid-framework也早实现该功能,与楼主不同的是使用package类型作为参数 packageToScan和白衣的entityPackages好像更加简洁呀。 packageToScan最好提供一个模糊匹配的例子,因为不能模糊匹配就没有意义了。 期待spring2.5.6... |
|
返回顶楼 | |
发表时间:2008-10-09
cats_tiger 写道 badqiu 写道 未发布的spring2.5.6中已经实现该功能,好像是增加一个packageToScan属性. 而rapid-framework也早实现该功能,与楼主不同的是使用package类型作为参数 packageToScan和白衣的entityPackages好像更加简洁呀。 packageToScan最好提供一个模糊匹配的例子,因为不能模糊匹配就没有意义了。 期待spring2.5.6... 都是支持的,因为都是使用PathMatchingResourcePatternResolver这个类 引用 <bean id="sessionFactory" class="cn.org.rapid_framework.spring.hibernate3.annotation.ScanAnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- annotatedScanPackages可以自动搜索某个package的全部标记@Entity class --> <property name="annotatedScanPackages"> <list> <value>com.**.model</value> </list> </property> </bean> |
|
返回顶楼 | |
发表时间:2008-10-09
很棒的工作!
如果是原生的hibernate支持就好了,hibernate的配置文件也不用一个个写了 |
|
返回顶楼 | |
发表时间:2008-10-10
都用annotation了。为什么不直接用jpa。不是更简洁。
|
|
返回顶楼 | |
发表时间:2008-10-10
onlydo 写道 都用annotation了。为什么不直接用jpa。不是更简洁。
JPA现在大家使用的实现就是Hibernate,而且JPA的规范更新的太慢了,有很多Hibernate支持的特性JPA不支持. |
|
返回顶楼 | |