原文地址:http://my.oschina.net/devleon/blog/530803?fromerr=9Z8ZFdbf

摘要

  自Spring3.0开 始,Spring正式将JavaConfig引入了Spring框架,我们可以基于纯Java代码来配置Spring容器和Web容器,不再需要任何 XML文件。摒弃XML文件而采用全Java配置的模式正逐渐变成主流。当然我们也不否认现阶段的一些配置还依然需要依托XML,Java应用彻底抛弃 XML配置文件还有很长的一段路要走。在本文中,我们将基于纯Java代码来配置一个Spring Web项目。

  回忆一下,我们通常需要在applicationContext.xml和xxx-servlet.xml中配置哪些东西?一般来讲,我们会配置组 件扫描、数据源、事务管理器、自定义的一些工具类、切面、消息转换器、静态资源处理以及视图等。为了有条理的组织我们的配置类,我们将这些东西分为几个部 分来配置。

  我们首先来配置数据源和事务管理器。既然我们是基于纯Java代码来配置,我们就需要考虑配置的可扩展性,我们希望提供的配置能够最大可能的适用于 不同的项目,而不是只能用于一个项目。所以,对于数据源和事务管理,我们首先提供一个抽象类DBConfig,这个类提供了事务管理器和一些自定义的数据 库访问工具,然后提供一个实现数据源抽象方法。源码如下:

package com.kiiwow.framework.config.context.spring.rest.dbconfig;import javax.sql.DataSource;import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;import com.kiiwow.framework.database.jdbc.JDBCAccess;
import com.kiiwow.framework.database.jdbc.JDBCAccessContext;/*** 基本的数据库配置* 统一配置了数据库访问工具和事务管理器  * 具体的数据库配置类需要实现数据源创建的抽象方法  * 这个类为实现不同的数据源提供了接口  *  * @author leon.gan  *  */ public abstract class DBConfig {     /**      * 数据源,由子类实现      */     public abstract DataSource dataSource();          /**      * 事务管理器      */     @Bean     public DataSourceTransactionManager dataSourceTransactionManager() {         DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();         dataSourceTransactionManager.setDataSource(dataSource());         return dataSourceTransactionManager;     }          /**      * SpringJDBC Template      */     @Bean     public JdbcTemplate jdbcTemplate() {         JdbcTemplate jdbcTemplate = new JdbcTemplate();         jdbcTemplate.setDataSource(dataSource());         return jdbcTemplate;     }          /**      * JDBC访问接口      */     @Bean     public JDBCAccess jdbcAccess() {         JDBCAccess jdbcAccess = new JDBCAccess();         jdbcAccess.setJdbcTemplate(jdbcTemplate());         return jdbcAccess;     }          /**      * 实际用于执行SQL的工具      */     @Bean     public JDBCAccessContext jdbcAccessContext() {         JDBCAccessContext jdbcAccessContext = new JDBCAccessContext();         jdbcAccessContext.setJdbcAccess(jdbcAccess());         return jdbcAccessContext;     }      }

  我们需要将DBConfig中的事务管理器和数据库访问工具注册成为被Spring管理的Bean,在方法上添加@Bean注解即可,默认是单例。 这个类并没有指定具体的数据源,而是由子类去实现dataSource()方法来指定,这样就做到了可扩展。(对于以上代码中Jdbc相关的工具类,请参考博文《对JdbcTemplate进行简易封装以使其更加易用》)接下来,我们提供一个BasicDataSourceDBConfig,这个类继承自DBConfig,并实现dataSource()方法提供了一个BasicDataSource数据源。源码如下:

package com.kiiwow.framework.config.context.spring.rest.dbconfig;import javax.inject.Inject;
import javax.sql.DataSource;import org.apache.commons.dbcp.BasicDataSource; import org.springframework.context.annotation.Bean; import com.kiiwow.framework.config.context.spring.KiiwowEnvironment; /**  * Apache BasicDataSource 数据源配置  *  * @author leon.gan  *  */ public class BasicDataSourceDBConfig extends DBConfig {     @Inject     KiiwowEnvironment environment;          @Bean     public DataSource dataSource() {         BasicDataSource ds = new BasicDataSource();         ds.setDriverClassName(environment.getProperty("jdbc.driver"));         ds.setUrl(environment.getProperty("kiiwow_jdbc.url"));         ds.setUsername(environment.getProperty("kiiwow_jdbc.username"));         ds.setPassword(environment.getProperty("kiiwow_jdbc.password"));         ds.setTestOnBorrow(true);         ds.setValidationQuery("select 1 from dual");         return ds;     }      }

我们在实际使用时,只要使用BasicDataSourceDBConfig这个类即可。同理,我们也可以提供一个C3p0DataSourceDBConfig类来实现C3P0数据源,源码如下:

package com.kiiwow.framework.config.context.spring.rest.dbconfig;import javax.inject.Inject;
import javax.sql.DataSource;import org.springframework.context.annotation.Bean; import com.kiiwow.framework.config.context.spring.KiiwowEnvironment; import com.mchange.v2.c3p0.ComboPooledDataSource; /**  * C3P0数据源配置  *  * @author leon.gan  *  */ public class C3p0DataSourceDBConfig extends DBConfig {     @Inject     KiiwowEnvironment environment;          @Bean     public DataSource dataSource() {         try {             ComboPooledDataSource dataSource = new ComboPooledDataSource();             dataSource.setDriverClass(environment.getProperty("db.c3p0.driverClassName"));             dataSource.setJdbcUrl(environment.getProperty("kiiwow_db.c3p0.url"));             dataSource.setUser(environment.getProperty("kiiwow_db.c3p0.username"));             dataSource.setPassword(environment.getProperty("kiiwow_db.c3p0.password"));             //当连接池在没有可用空闲连接时每次可以新增的连接数             dataSource.setAcquireIncrement(environment.getRequiredProperty("c3p0.acquireIncrement", int.class, 5));             //连接池初始连接数             dataSource.setInitialPoolSize(environment.getRequiredProperty("c3p0.initialPoolSize", int.class, 5));             //连接池可持有的最大连接数             dataSource.setMaxPoolSize(environment.getRequiredProperty("c3p0.maxPoolSize", int.class, 200));             //连接池可持有的最小连接数             dataSource.setMinPoolSize(environment.getRequiredProperty("c3p0.minPoolSize", int.class, 5));             //连接池中的连接失效的阀值(即最大未被使用时长)             dataSource.setMaxIdleTime(environment.getRequiredProperty("c3p0.maxIdleSize", int.class, 1800));             //与MaxIdleTime配合使用,必须小于MaxIdleTime的值,用于减少连接池中的连接             dataSource.setMaxIdleTimeExcessConnections(environment.getRequiredProperty("c3p0.maxIdleTimeExcessConnections", int.class, 1200));             //连接最大存活时间,超过这个时间将被断开,正在使用的连接在使用完毕后被断开             dataSource.setMaxConnectionAge(environment.getRequiredProperty("c3p0.maxConnectionAge", int.class, 1000));             //进行空闲连接测试的SQL             dataSource.setPreferredTestQuery(environment.getProperty("c3p0.preferredTestQuery", "select 1 from dual"));             //进行空闲连接测试的时间间隔             dataSource.setIdleConnectionTestPeriod(environment.getRequiredProperty("c3p0.idleConnectionTestPeriod", int.class, 120));             return dataSource;         } catch (Exception e) {             throw new RuntimeException(e);         }     }      }

  在上面的代码中,出现了一个KiiwowEnvironment类,这个类是对 org.springframework.core.env.Environment的封装,以实现编码解码功能。为了说明这个类的作用,我们首先来看看 Environment这个接口的作用。这个接口是与@PropertySources注解配合工作的,我们在@PropertySources注解中指 定的配置文件会自动被加载到Environment的实现中以key-value形式存储,我们可以通过Environment提供的 getProperty(String key)等方法通过属性名获取属性值。

  在我们的设计中有一个特殊的需求,我们希望配置在properties文件中的数据库连接以及账号密码都是经过加密的,这样可以一定程度上防止数据库信息泄 露。所以我们定义在properties文件中所有以"kiiwow_"开头的属性,其对应的值都是经过Base64编码的结果,所以在我们通过 Environment取出这些值的时候也要进行解码。不幸的是,Environment并不具备编码解码功能,我们需要自己实现,所以有了 KiiwowEnvironment这个类。源码如下:

package com.kiiwow.framework.config.context.spring;import javax.inject.Inject;import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component; import com.kiiwow.framework.util.DigestUtils; /**  * 自定义环境类  * 基于JavaConfig配置Spring的情况下,使用@PropertySource注解将配置属性注入Environment中后  * Environment不能支持解码已编码的属性值。所以提供这个类对数据获取进行解码操作。  *  * @author leon.gan  *  */ @Component public class KiiwowEnvironment {     @Inject     Environment env;          /**      * 所有以kiiwow_开头的属性,其值都需要经过Base64解码后使用      */     public String getProperty(String propertyName) {         if (propertyName.startsWith("kiiwow_")) {             String originalValue = env.getProperty(propertyName);             return DigestUtils.decodeBase64(originalValue);         } else {             return env.getProperty(propertyName);         }     }          /**      * 没有配置属性时采用默认值      */     public String getProperty(String propertyName, String defaultValue) {         if (propertyName.startsWith("kiiwow_")) {             String originalValue = env.getProperty(propertyName);             return originalValue == null ? null : DigestUtils.decodeBase64(originalValue);         } else {             return env.getProperty(propertyName) == null ? defaultValue : env.getProperty(propertyName);         }     }          /**      * 将值转换为指定类型      */     public <T> T getRequiredProperty(String propertyName, Class<T> targetType) {         return env.getRequiredProperty(propertyName, targetType);     }          /**      * 属性不存在则返回默认值      */     public <T> T getRequiredProperty(String propertyName, Class<T> targetType, T defaultValue) {         try {             return env.getRequiredProperty(propertyName, targetType);         } catch (Exception e) {             return defaultValue;         }     }      }

  从上面的代码可以看到,我们只是将org.springframework.core.env.Environment 注入,然后在调用他的getProperty(String key)等方法前增加了判断属性名是否以"kiiwow_"开头的逻辑。同时提供了处理属性名不存在于配置文件则返回默认值的方法。特别注意,我们需要将 这个类注册成为Spring容器的一个组件,即在类上使用@Component注解,否则org.springframework.core.env.Environment是无法注入的。(这个解决方法特别感谢@YouKnowNothing 的帮助)。

  至此,我们对于数据源和事务管理的配置就结束了。我们可以自由继承DBConfig类来扩展我们需要的数据源,也能对配置文件中的敏感信息进行编码 防止信息泄露。不过需要指出的是,这种处理敏感信息的方法依然不是最优的。由于我们要进行解码,所以我们只能使用双向加密策略,如果别人知道了你的编码策 略,也能够轻松破解。我们将在下一篇文章中讲解对于静态资源、视图解析器、消息转换器的配置以及web.xml的Java代码化。

感谢您的阅览,劳烦点个赞。

转载于:https://www.cnblogs.com/SummerinShire/articles/5583767.html

springMVC纯java代码配置(一)- 数据源与事务管理(转载的哦)相关推荐

  1. 基于纯Java代码的Spring容器和Web容器零配置的思考和实现(3) - 使用配置

    经过<基于纯Java代码的Spring容器和Web容器零配置的思考和实现(1) - 数据源与事务管理>和<基于纯Java代码的Spring容器和Web容器零配置的思考和实现(2) - ...

  2. Android --- 使用纯java代码实现相对布局(通俗易懂)

    java布局 java代码布局和xml布局的区别 1.Java纯布局更加的灵活,比如自定义控件或一些特殊要求时,使用java代码布局 2.常用的xml布局是所见即所得的编写方式,以及xml本身拥有一些 ...

  3. java编写流星_纯Java代码实现流星划过天空

    废话不多说了,直接给大家贴java代码了. import java.awt.Color; import java.awt.Graphics; import java.awt.image.Buffere ...

  4. 【iText5 生成PDF】纯Java代码实现生成PDF(自定义表格、文本水印、单元格样式)

    工作中遇到需要生成PDF.最终选择了iText.其他也有通过html再生成.感觉不太适合就用了代码实现. 使用iText 5.5.13.1版本.纯Java代码实现 1.自定义表格合并指定行列完成数据填 ...

  5. 用纯Java代码根据IP获取windows和linux的MAC物理地址

    一.环境介绍 1.最近在使用struts2和JSP开发web网页时,碰到一个问题,我需要在后台根据ip获取访问服务器的物理地址,然后进行其他业务处理,但是我发现我在后台没有对应的接口或函数能够满足我. ...

  6. 在Android中用纯Java代码布局

    感谢大佬:https://www.jianshu.com/p/7aedea560f16 在Android中用纯Java代码布局 本文的完成了参考了一篇国外的教程,在此表示感谢. Android中的界面 ...

  7. springmvc atomikos mysql数据源_Spring多数据源分布式事务管理/springmvc+spring+atomikos[jta]+druid+mybatis...

    项目进行读写分离及分库分表,在一个业务中,在一个事务中处理时候将切换多个数据源,需要保证同一事务多个数据源数据的一致性.此处使用atomikos来实现:最后附源码: 1:spring3.0之后不再支持 ...

  8. Android Studio编写运行测试纯java代码可带main()函数

    问题 小伙伴们在做安卓项目的时候,是不是有时候会忘记某些api的使用方法,不太确定他们的结果是怎样的,需要写一些测试代码,验证看看我们的写法是否正确.刚开始的时候我是在页面上写一个Button,添加点 ...

  9. java中android_在Android中用纯Java代码布局

    本文的完成了参考了一篇国外的教程,在此表示感谢. Android中的界面布局主要有两种方式,一种是xml文件和Java代码结合的布局方式,一种是完全依靠Java代码布局. 两种布局方式的比较 对于第一 ...

最新文章

  1. 【Git】搭建Git 服务器
  2. VF01-billing拆分逻辑详解
  3. Moment.js常见用法总结
  4. c# 修饰词public, protected, private,internal的区别
  5. html5技术英文论文参考文献,英文论文的参考文献范例(精选8篇)
  6. Vue.js 学习笔记 八 v-for
  7. grep -i pattern files :不区分大小写地搜索。默认情况区分大小写,
  8. idea启动vue项目
  9. C语言求三角形斜边长
  10. 顺时针、逆时针两种方式减小的倒计时动画
  11. TypeError: Class constructor ServeCommand cannot be invoked without ‘new‘
  12. 如何安装或卸载Google Chrome浏览器
  13. unity3d画布切换_画布内3D折叠导航
  14. Tomcat 如何查看端口
  15. 豆瓣电影评分分析(数据分析)
  16. 洛谷9月月赛 康娜的线段树
  17. python rot13,Python文本加密:rot13
  18. 在nuc972上实现I2C接口数字电位器isl95311的驱动
  19. 深聊全链路压测之:第二十一讲 | 如何搭建GoReplay压测平台。
  20. 操作系统精髓-进程相关知识总结

热门文章

  1. mapreduce 文件可以切分吗_MapReduce的任务流程
  2. python写接口自动化需要rsa加密_RSA加密,请问如何用Python实现该加密过程
  3. ipv6 端口号_计算机网络之IP、MAC、端口号、子网掩码、默认网关、DNS
  4. html5 提交from,刨根问底:HTML中form的隐式提交
  5. Western Digital使用5400 rpm级硬盘使所有人感到困惑
  6. 信息系统项目管理师论文评分标准
  7. 前端数据可视化可绘制地图等插件:Highcharts、Echarts和D3
  8. SpringBoot项目中对mysql数据库进行定时备份为sql文件的实现思路
  9. DevExpress的图形按钮菜单栏控件WindowsUIButtonPanel的布局、使用和设置按钮的点击事件
  10. BJUI接受TabID实现添加或编辑后自动刷新页面