《Spring.3.x企业应用开发实战》--- 零碎笔记

来源:互联网 发布:足球数据统计软件 编辑:程序博客网 时间:2024/05/09 23:54
       Bean工厂(com.springframework.beans.factory.BeanFactory)是 Spring 框架最核心的接口,它提供了高级 IoC 的配置机制。 BeanFactory 使管理不同类型的 Java 对象成为可能,应用上下文( com.springframework.context.ApplicationContext)建立在 BeanFactory 基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。我们一般称 BeanFactory 为 IoC 容器,而称 ApplicationContext 为应用上下文。但有时为了行文方便,我们也将 ApplicationContext 称为 Spring 容器。
       对于两者的用途,我们可以进行简单划分: BeanFactory是Spring框架的基础设施,面向 Spring 本身; ApplicationContext面向使用 Spring 框架的开发者,几乎所有的应用场合我们都直接使用 ApplicationContext 而非底层的 BeanFactory。


       和 BeanFactory 初始化相似, ApplicationContext 的初始化也很简单,如果配置文件放置在类路径下,用户可以优先使用 ClassPathXmlApplicationContext 实现类:
ApplicationContext ctx =new ClassPathXmlApplicationContext("com/baobaotao/ context/beans.xml");
对于 ClassPathXmlApplicationContext 来说,“com/baobaotao/context/beans.xml”等同于“ classpath: com/baobaotao/context/beans.xml”。如果配置文件放置在文件系统的路径下,则可以优先考虑使用 FilySystemXmlApplicationContext 实现类:
ApplicationContext ctx =new FileSystemXmlApplicationContext("com/baobaotao/ context/beans.xml");
对于FileSystemXmlApplicationContext来说,“ com/baobaotao/context/beans.xml”等同于“ file: com/baobaotao/context/beans.xml”。
       还可以指定一组配置文件, Spring会自动将多个配置文件在内存中“整合”成一个配置文件,如下所示:
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"conf/beans1.xml","conf/beans2.xml"});


       ApplicationContext和BeanFactory另一个最大的不同之处在于:前者会利用 Java反射机制自动识别出配置文件中定义的BeanPostProcessor、 InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,并自动将它们注册到应用上下文中;而后者需要在代码中通过手工调用addBeanPostProcessor()方法进行注册。这也是为什么在应用开发时,我们普遍使用ApplicationContext 而很少使用 BeanFactory 的原因之一。


       使用<bean>进行配置时,可以通过init-method和destory-method属性指定Bean的初始化及容器销毁前执行的方法。Spring从2.5开始支持JSR-250中定义的@PostConstruct和@PreDestory注解,在Spring中它们相当于init-method和destory-method属性的功能,不过使用注解时,可以在一个Bean中定义多个@PostConstruct和@PreDestory方法。


       如果尝试通过一下配置为bean的brand属性注入一个null值,将会得到一个失望的结果:
<bean id=”car” class=”com.baobaotao.Car”>   <property name=”brand”>      <value></value>   </property></bean>
       Spring将<value></value>解析为空字符串,那如何为属性设置一个null的注入值呢?答案是必须使用专用的<null/>元素标签,通过它可以为Bean的字符串或其它对象类型的属性注入null值:
<bean id=”car” class=”com.baobaotao.Car”>   <property name=”brand”>      <null/>   </property></bean>


       可以使用@Component注解在UserDao类声明处对类进行标注,它可以被Spring容器识别。除了@Component以外,Spring提供了3个功能基本和@Component等效的注解:
        @Repository: 用于对DAO实现类进行标注;
        @Service: 用于对Service实现类进行标注;
        @Controller: 用于对Controller实现类进行标注。
       之所以要在@Component之外提供这三个特殊的注解,是为了让标注类本身的用途清晰化,完全可以用@Component替代这三个特殊的注解。但是,推荐使用特定的注解标注特定的Bean。


       Spring 分别提供了用于启动 WebApplicationContext 的 Servlet 和 Web 容器监听器:
        org.springframework.web.context.ContextLoaderServlet
        org.springframework.web.context.ContextLoaderListener


       如果采用DBCP的默认配置,由于testOnBorrow属性的默认值为true,数据源在将连接交给DAO前,会事先检测这个连接是否是好的,如果连接有问题(在数据库端被关闭),则会取一个其他的连接给DAO。如果每次将连接交给DAO时都检测连接有效性,在高并发的应用中将会带来性能的问题,因为它会需要更多的数据库访问请求。
       一种推荐的高效方式是:将testOnBorrow设置为false,而将testWhileIdle设置为true,再设置好timeBetweenEvictionRunsMillis值。这样,DBCP将通过一个后台线程定时对空闲连接进行检测,当发现无用的空闲连接(那些被数据库关闭的连接)时,就会将它们清除掉。只要将timeBetweenEvictionRunsMillis的值设置小于8小时,那些被MySQL关闭的空闲连接就可以被清除出去。
       当然,MySQL本身可以通过调整interactive-timeout(以秒为单位)配置参数,更改空闲连接的过期时间。所以,在设置timeBetweenEvictionRunsMillis值时,必须首先获知MySQL空闲连接的最大过期时间。


       Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播。
       PROPAGATION_REQUIRED
       如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
       PROPAGATION_SUPPORTS
       支持当前事务,如果当前没有事务,就以非事务方式执行。
       PROPAGATION_MANDATORY
       使用当前的事务,如果当前没有事务,就抛出异常。
       PROPAGATION_REQUIRES_NEW
       新建事务,如果当前存在事务,把当前事务挂起。
       PROPAGATION_NOT_SUPPORTED
       以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
       PROPAGATION_NEVER
       以非事务方式执行,如果当前存在事务,则抛出异常。
       PROPAGATION_NESTED
       如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
       Spring默认的事务传播行为是PROPAGATION_REQUIRED,它适合绝大多数的情况,如果多个ServiceX#methodX()均工作在事务环境下(即均被Spring事务增强),且程序中存在如下的调用链:Service1#method1() -> Service2#method2() -> Service3#method3(),那么这3个服务类的3个方法通过Spring的事务传播机制都工作在同一个事务中。


       一般情况下,都是在DAO类中使用JDBCTemplate,JDBCTemplate在XML配置文件中配置好,然后直接在DAO中注入即可:

@Repositorypublic class UserDao{   private JdbcTemplate jdbcTemplate;   public void setDataSource(DataSource dataSource){       jdbcTemplate = new JdbcTemplate(dataSource); // 用dataSource构造一个JdbcTemplate   }   ……}
       Spring也为JDBCTemplate提供了对应的支持类JdbcDaoSupport。JdbcDaoSupport内部定义了JDBCTemplate的成员变量,开发者可以通过扩展JdbcDaoSupport定义自己的Dao,但是推荐的做法是自己定义一个BaseDao,在BaseDao中定义JDBCTemplate成员变量,并打上@Autowire注解。在实际项目中,还可以在BaseDao中定义一些通用的功能,如分页查询等等。



       Spring提供了一个能从当前事务上下文中获取绑定的数据连接的工具类,那就是DataSourceUtils。Spring强调必须使用DataSourceUtils工具类获取数据连接,Spring的JdbcTemplate内部也是通过DataSourceUtils来获取连接的。
使用DataSourceUtils获取数据连接:Connection conn = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());显示使用DataSourceUtils释放连接:DataSourceUtils.releaseConnection(conn, jdbcTemplate.getDataSource());


       尽量使用可绑定参数的SQL语句,以便数据库可以复用SQL的执行计划,提高数据库的执行效率。此外,应尽量在DAO中使用类级别的静态常量(final static)定义SQL字符串,不应在方法内部声明SQL字符串变量,提高JVM的内存使用效率。


       使用RowCallbackHandler处理结果集。Spring提供了org.springframework.jdbc.core.RowCallbackHandler回调接口,通过该接口可以定义如何从结果集获取数据。Spring会遍历结果集,对结果集中每一行调用RowCallbackHandler回调接口public void process(ResultSet rs) throws SQLException处理数据。
       使用RowMapper<T>处理结果集。Spring还提供了一个和RowCallbackHandler功能上类似的RowMapper<T>接口,也可以使用RowMapper<T>定义结果集映射逻辑,在结果集为多行记录时,该接口更容易使用。RowMapper<T>也只有一个接口方法:T mapRow(ResultSet rs, int rowNum)。


       我们知道,通过JDBC查询返回一个ResultSet结果集时,JDBC并不会一次性将所有匹配的数据都加载到JVM中,而是只返回一批次的数据(由JDBC驱动程序决定),当通过ResultSet#next()游标滚动结果集超过数据范围时,JDBC再获取一批数据。这样以一种“批量化+串行化”的处理方式避免的结果集处理时JVM内存的过大开销。
       当处理大结果集时,如果使用RowMapper,则虽然获取数据的过程是串行化的,但是结果集中的所有数据最终都会映射并汇总成一个List<T>对象,占用大量的JVM内存,甚至可直接引发OutOfMemoryException异常。这时,应该使用RowCallbackHandler接口,在processRow()接口方法内部处理结果集数据。
       举例来说,如果程序要求给所有系统用户发送一封邮件,而系统用户数量为100万。第一种方案是采用RowMapper,返回一个List<User>的集合,再通过遍历这个List<User>,逐个发送邮件;而另一种方案是采用RowCallbackHandler接口,在processRow()接口方法内部逐行获取User数据后,马上就调用邮件服务发送邮件。虽然这两种方案都达到了相同的目的,但第一种方案将会在程序运行过程中,在JVM中产生一个大小为100万的List<User>,导致极低的系统性能和巨大的开销,甚至引起系统崩溃。


单值查询
     int queryForInt(String sql)
     int queryForInt(String sql, Object... args)
     int queryForInt(String sql, Object[] args, int[] argTypes)
     long queryForLong(String sql)
     ......
     <T> T queryForObject(String sql, Class<T> requiredType)
     ......
       需要注意的是,如果返回空结果集,将抛出EmptyResultDataAccessException异常,如果返回的结果多于一行,则会抛出IncorrectResultSizeDataAccessException异常。









0 0
原创粉丝点击