`
cats_tiger
  • 浏览: 276878 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

一劳永逸——让Spring自动加载Hibernate Annotated Classes

阅读更多
通常,spring整合Hibernate的SessionFactory是这样做的:
<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。
分享到:
评论
21 楼 IceWee 2012-07-02  
照猫画虎? 你说Spring是猫,而你的是虎,嗯? 哈哈
20 楼 cats_tiger 2011-07-25  
nggno1 写道
孤风浪客 写道
cats_tiger 写道
spring2.5.6终于发布,这个可以退役了

楼上不知道测试否?
我测试了一下,还是有BUG。
如果只是写JAVA文件单独测试,那么可以使用,如果放在server端启动,启动就会出错。我的配置如下:
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="hibernateProperties">
			<props>
				<!-- 省略 -->
			</props>
		</property>
		<property name="packagesToScan">
			<list>
				<value>com.test.*.model</value>
			</list>
		</property>
    </bean>

而网上有人说是少了个“.”号,也就是要写成com.test.*.model.
这种写法也是JAVA单元测试通过server端启动不通过,
如果采用com.test.*.model*.*
连单元测试都没办法通过,不知道是我的配置有问题还是BUG还没有解决!
异常主要是:
引用

Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'packagesToScan' of bean class [org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean]: Bean property 'packagesToScan' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?


这样写就OK
                <property name="packagesToScan">
			<list>
				<value>com/test/*/model</value>
			</list>
		</property>

应该这样写,连续两个*
<property name="packagesToScan">
		    <list>
		        <value>com.googlecode.jtiger.**.model</value>
		    </list>
		</property>
19 楼 nggno1 2011-07-22  
孤风浪客 写道
cats_tiger 写道
spring2.5.6终于发布,这个可以退役了

楼上不知道测试否?
我测试了一下,还是有BUG。
如果只是写JAVA文件单独测试,那么可以使用,如果放在server端启动,启动就会出错。我的配置如下:
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="hibernateProperties">
			<props>
				<!-- 省略 -->
			</props>
		</property>
		<property name="packagesToScan">
			<list>
				<value>com.test.*.model</value>
			</list>
		</property>
    </bean>

而网上有人说是少了个“.”号,也就是要写成com.test.*.model.
这种写法也是JAVA单元测试通过server端启动不通过,
如果采用com.test.*.model*.*
连单元测试都没办法通过,不知道是我的配置有问题还是BUG还没有解决!
异常主要是:
引用

Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'packagesToScan' of bean class [org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean]: Bean property 'packagesToScan' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?


这样写就OK
                <property name="packagesToScan">
			<list>
				<value>com/test/*/model</value>
			</list>
		</property>
18 楼 孤风浪客 2008-12-12  
TO:downpour,谢谢提醒。
现在这个问题已经解决了,和eclipse工程默认的编译文件输入问题有关,默认是:"/build/classes"改为"WebContent/WEB-INF/classes"就可以了。
17 楼 downpour 2008-12-12  
孤风浪客 写道
TO:myyate
你配置成功了么,如果成功能不能把你的配置截图给我看看?
我在spring的官方论坛下也发了个帖子
http://forum.springframework.org/showthread.php?t=64612
按照他们说的加*还有更新包都没有办法解决。


习惯不好,还是喜欢用点啊。

为什么不用/呢?

配置成:com/XX/model/不就好了。
16 楼 孤风浪客 2008-12-10  
TO:myyate
你配置成功了么,如果成功能不能把你的配置截图给我看看?
我在spring的官方论坛下也发了个帖子
http://forum.springframework.org/showthread.php?t=64612
按照他们说的加*还有更新包都没有办法解决。
15 楼 myyate 2008-12-09  
不要喷我,我记得这个方法以前有人写过的,不知道是不是你,而且代码很类似,在项目中我们也用了,你这个其实还是可以改造,如楼上的com.test.*.model*.*。
spring2.5已经支持正则包扫描了。
14 楼 孤风浪客 2008-12-09  
cats_tiger 写道
spring2.5.6终于发布,这个可以退役了

楼上不知道测试否?
我测试了一下,还是有BUG。
如果只是写JAVA文件单独测试,那么可以使用,如果放在server端启动,启动就会出错。我的配置如下:
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="hibernateProperties">
			<props>
				<!-- 省略 -->
			</props>
		</property>
		<property name="packagesToScan">
			<list>
				<value>com.test.*.model</value>
			</list>
		</property>
    </bean>

而网上有人说是少了个“.”号,也就是要写成com.test.*.model.
这种写法也是JAVA单元测试通过server端启动不通过,
如果采用com.test.*.model*.*
连单元测试都没办法通过,不知道是我的配置有问题还是BUG还没有解决!
异常主要是:
引用

Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'packagesToScan' of bean class [org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean]: Bean property 'packagesToScan' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
13 楼 cats_tiger 2008-11-06  
spring2.5.6终于发布,这个可以退役了
12 楼 supercrsky 2008-11-05  
最新的spring2.5.6已经实现此功能:



<!-- 自动搜索注入实体Bean -->
<property name="packagesToScan" value="org.eline.entity*.*" />
11 楼 cats_tiger 2008-10-29  
Else 写道
很棒的工作!
如果是原生的hibernate支持就好了,hibernate的配置文件也不用一个个写了

原生的本来就不用一个一个写,用mappingLocations就可以了。
10 楼 redez 2008-10-13  
import java.io.IOException;
import java.util.Iterator;

import org.hibernate.HibernateException;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.PersistentClass;
import org.lc.bean.CustomClassEditor;
import org.lc.bean.CustomClassEditorManager;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;

/**
 * 自动寻找所有以.hbm.xml结尾的文件,加入到Hibernate配置中
 * 并为Hibernate中定义的每一个类添加HibernateClassEditor,保证对象保存时可以通过id直接建立对象关联
 */
@SuppressWarnings("unchecked")
public class SessionFactoryBean extends LocalSessionFactoryBean implements BeanFactoryAware,BeanFactoryPostProcessor{
	
	private DefaultListableBeanFactory beanFactory;
		
	/**
	 * Spring会自动进行注入
	 * 记录BeanFactory,以在postProcessConfiguration中自动加入所有的Hibernate映射
	 */
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException{
		this.beanFactory = (DefaultListableBeanFactory) beanFactory;	
	}

	/** 
	 * @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#postProcessConfiguration(org.hibernate.cfg.Configuration)
	 * 
	 * 自动加入类路径中所有匹配的Hibernate映射文件
	 */
	@Override
	protected void postProcessConfiguration(Configuration config) throws HibernateException {
		ResourcePatternResolver rp = new PathMatchingResourcePatternResolver(beanFactory.getBeanClassLoader());
		
		Resource[] resources;
		try {
			resources = rp.getResources("/**/*.hbm.xml");
			for(int i=0;i<resources.length;i++){
				Resource resource = resources[i];
				
				//将找到的配置文件加入
				if(resource.exists())
					config.addInputStream(resource.getInputStream());
				
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		super.postProcessConfiguration(config);		
	}

	
	
	/* 
	 * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory)
	 * 在CustomClassEditorManager中加入Editor定义 
	 */
	
	
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		Iterator cIter = this.getConfiguration().getClassMappings();
		
		while(cIter.hasNext()){
			PersistentClass pc = (PersistentClass) cIter.next();
			
			String className = pc.getClassName();
			//为Hibernate中定义的每一个类加入自定义的编辑器
			try {				
				CustomClassEditorManager.registerCustomEditor(Class.forName(className), new CustomClassEditor(className));
			} catch (ClassNotFoundException e) {			
				e.printStackTrace();
			}			
		}
	}
}


9 楼 超级潜水员 2008-10-10  
onlydo 写道
都用annotation了。为什么不直接用jpa。不是更简洁。


JPA现在大家使用的实现就是Hibernate,而且JPA的规范更新的太慢了,有很多Hibernate支持的特性JPA不支持.
8 楼 onlydo 2008-10-10  
都用annotation了。为什么不直接用jpa。不是更简洁。
7 楼 Else 2008-10-09  
很棒的工作!
如果是原生的hibernate支持就好了,hibernate的配置文件也不用一个个写了
6 楼 badqiu 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> 


5 楼 cats_tiger 2008-10-08  
badqiu 写道

未发布的spring2.5.6中已经实现该功能,好像是增加一个packageToScan属性.
而rapid-framework也早实现该功能,与楼主不同的是使用package类型作为参数

packageToScan和白衣的entityPackages好像更加简洁呀。
packageToScan最好提供一个模糊匹配的例子,因为不能模糊匹配就没有意义了。
期待spring2.5.6...
4 楼 cats_tiger 2008-10-08  
江南白衣 写道

好方法,我将它加到springside(www.springside.org.cn)里可以吗?

当然,很荣幸,经常参考springside,很高兴能提供一点帮助。
3 楼 badqiu 2008-10-07  
<p>未发布的spring2.5.6中已经实现该功能,好像是增加一个packageToScan属性. <br/>而rapid-framework也早实现该功能,与楼主不同的是使用package类型作为参数</p>
<p> </p>
<pre name='code' class='xml'> &lt;bean id="sessionFactory" class="cn.org.rapid_framework.spring.hibernate3.annotation.ScanAnnotationSessionFactoryBean"&gt;
&lt;property name="dataSource" ref="dataSource"/&gt;

&lt;!-- annotatedScanPackages可以自动搜索某个package的全部标记@Entity class --&gt;
&lt;property name="annotatedScanPackages"&gt;
&lt;list&gt;
&lt;value&gt;com.company.demo.model&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/bean&gt; </pre>
<p> </p>
<p>spring2.5.5中还对Ibatis也实现的类似的功能自动搜索并加载SqlMap.xml</p>
<pre name='code' class='xml'>    &lt;bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"&gt;
        &lt;property name="mappingLocations"&gt;
        &lt;value&gt;classpath:/org/company/demo/model/**/*SqlMap.xml&lt;/value&gt;
        &lt;/property&gt;
        &lt;property name="dataSource" ref="dataSource"/&gt;
    &lt;/bean&gt;</pre>
<p> </p>
<p>等待spring2.5.6发布时rapid-framework下一版本会删除ScanAnnotationSessionFactoryBean类,使用官方实现.</p>
<p> </p>
2 楼 江南白衣 2008-10-07  
好方法,我将它加到springside(www.springside.org.cn)里可以吗?

相关推荐

    Spring+SpringMVC+JPA+Hibernate搭建

    10. 实现业务接口以及在applicationContext.xml中配置相应的bean,以让Spring容器管理这些业务对象。 11. 为项目添加JUnit测试支持,通过编写测试类和测试方法来验证业务逻辑的正确性。 12. 将SpringMVC整合到已经...

    配置hibernate数据源

    - 映射文件或注解(Annotated Classes):指定与数据库表对应的实体类。 4. 创建实体类。这些实体类将代表数据库中的表。可以通过注解或者XML映射文件来指定类与数据库表的映射关系。 5. 编写操作数据库的代码。...

    人脸数据库 Annotated Database

    这个特定的"Annotated Database"包含了240幅图像,每幅图像都经过了专业的标注,这意味着图像上的关键特征如眼睛、鼻子、嘴巴等都有精确的位置标记,这极大地促进了算法的训练和性能评估。 在人脸识别领域,数据库...

    eclipse 配置 hibernate tools 反向生成 pojo类 注解

    3. 在“Mapping Strategy”部分,选择“Annotated Java Classes”,这将生成带有注解的POJO类。 4. “Generation Options”中,你可以决定是否生成equals()、hashCode()和toString()方法,以及其他自定义选项。 5. ...

    Learning Spring Application Development

    Following this, you will explore how to implement the request handling layer using Spring annotated controllers. Other highlights include learning how to build the Java DAO implementation layer by ...

    spring-0.9-annotate:Spring0.9版本原始资料阅读-spring源码阅读

    源码中可以看到Spring如何封装数据库连接和事务管理,提供了一种声明式的方式,让开发者无需直接处理低级别的数据库操作。 在系统开源的标签下,Spring 0.9的源码阅读也为我们展示了开源软件的设计和开发流程。开源...

    IMM Annotated Database 人脸数据库

    IMM Annotated Database是一个专为人脸识别研究而设计的数据库,包含240幅精心标注的人脸图像。这个数据库在人脸识别领域具有重要的价值,因为它提供了一组标准化、有标记的图像,可供研究人员进行算法开发、测试和...

    Manning.Spring.in.Action.4th.Edition.2014.11.epub

    11.1. Integrating Hibernate with Spring 11.1.1. Declaring a Hibernate session factory 11.1.2. Building Spring-free Hibernate 11.2. Spring and the Java Persistence API 11.2.1. Configuring an entity ...

    the.annotated.turing

    《The Annotated Turing》是由Charles Petzold撰写的一本经典计算机科学著作,它深入解析了艾伦·图灵在1936年提出的图灵机模型,并以此为基础探讨了计算理论的基础。这本书对于理解计算机科学的基本原理至关重要,...

    The Annotated C++ Reference Manual

    隐式调度是指由μC++内核自动管理的任务调度,适用于那些对响应时间和确定性有较高要求的场景。 ##### 2.9.2 外部调度 外部调度允许开发者直接控制任务的执行时机,通常用于需要更高层次控制的应用场景。 #### ...

    The Annotated C++ Reference Manual part3

    The Annotated C++ Reference Manual 一共四个压缩包

    使用Hibernate逆向生成实体类的方法(注解和hbm.xml映射文件)

    ### 使用Hibernate逆向生成实体类的方法 在Java开发领域中,Hibernate作为一种流行的ORM(对象关系映射)框架,被广泛应用于将对象模型映射到基于SQL的关系型数据库上。通过Hibernate,开发者能够更加高效地处理...

    redis-3.0-annotated-unstable.zip

    redis-3.0-annotated-unstable.zipredis-3.0-annotated-unstable.zipredis-3.0-annotated-unstable.zipredis-3.0-annotated-unstable.zip

    spring2.5 jar

    5. **更多数据访问支持**:Spring 2.5增强了对JDBC、Hibernate、JPA等持久化技术的支持,提供了更简便的数据访问抽象,如JdbcTemplate和HibernateTemplate。 6. **国际化(Internationalization,i18n)**:Spring ...

    Thinking in Java 4th Edition Annotated Solutions Guide

    根据提供的文件信息,以下是对文件《Thinking in Java 4th Edition Annotated Solutions Guide》中所包含知识点的详细解释: 首先,文件标题《Thinking in Java 4th Edition Annotated Solutions Guide》指出了这是...

    The Annotated C++ Reference Manual.part2

    The Annotated C++ Reference Manual.part2

    The Common Language Infrastructure Annotated Standard

    The Common Language Infrastructure Annotated Standard

    The Annotated C++ Reference Manual(ARM)-Ch09

    本节将基于给定的文件信息——标题、描述、标签以及部分内容来深入探讨与第九章“ARM Classes”相关的知识点。 ### 一、ARM及其意义 《The Annotated C++ Reference Manual》这本书由Bjarne Stroustrup撰写,旨在...

Global site tag (gtag.js) - Google Analytics