架构探险——第二章(为web应用添加业务功能)
第二章不使用框架完成了自己的Web应用。
重点:
服务层的完善优化过程,思路
在看这一段的时候引起了无数次的共鸣。相信大家在开始接触Java Web的时候,都做过类似的封装和优化。
第一版
在Service的静态代码块中获取config.properties配置文件中与JDBC相关的配置项。在service的业务方法中获取数据库的连接,并进行数据库的操作,finally中关闭数据库。
/**
* 提供客户数据服务
*/
public class CustomerServiceVersion {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class);
private static final String DRIVER;
private static final String URL;
private static final String USERNAME;
private static final String PASSWORD;
private static final String URL;
static {
Properties conf = PropsUtil.loadProps("config.properties");
DRIVER = conf.getProperty("jdbc.driver");
URL = conf.getProperty("jdbc.url");
USERNAME = conf.getProperty("jdbc.username");
PASSWORD = conf.getProperty("jdbc.password");
try {
Class.forName(DRIVER);
}catch(ClassNotFoundException e){
LOGGER.error("can not load jdbc driver",e);
}
}
/**
* 获取客户列表
* @return
*/
public List<Customer> getCustomerList(){
Connection conn = null;
try {
List<Customer> customerList = new ArrayList<Customer>();
String sql = "select * from customer";
conn = DriverManager.getConnection(URL,USERNAME,PASSWORD);
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
while (rs.next()){
Customer customer = new Customer();
customer.setId(rs.getLong("id"));
customer.setName(rs.getString("name"));
customer.setContact(rs.getString("contact"));
customer.setTelephone(rs.getString("telephone"));
customer.setEmail(rs.getString("email"));
customer.setRemark(rs.getString("remark"));
customerList.add(customer);
}
return customerList;
}catch (SQLException e){
LOGGER.error("excute sql failure",e);
}finally {
if(conn != null){
try{
conn.close();
}catch (SQLException e){
LOGGER.error("close connection failure",e);
}
}
}
}
}
存在的问题:
1.在service中读取config.properties文件,不合理,其他service使用的时候反复进行io。
2.执行一条SQL语句需要编写一大堆代码,并且使用try...catch...finally结构,开发效率不高)
第二版
开发DatabaseHelper来读取配置文件,获取数据库连接,关闭数据库连接。(解决了第一个问题)
使用Apache Common项目中的DbUtils类库,在DatabaseHelper中写SQL公共类,通过传参的形式,利用反射,执行对应的SQL。(解决了第二个问题)
/**
* 数据库助手类
*/
public final class DatabaseHelper1 {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelperV.class);
private static final QueryRunner QUERY_RUNNER = new QueryRunner();
private static final String DRIVER;
private static final String URL;
private static final String USERNAME;
private static final String PASSWORD;
private static final String URL;
static {
Properties conf = PropsUtil.loadProps("config.properties");
DRIVER = conf.getProperty("jdbc.driver");
URL = conf.getProperty("jdbc.url");
USERNAME = conf.getProperty("jdbc.username");
PASSWORD = conf.getProperty("jdbc.password");
try {
Class.forName(DRIVER);
}catch(ClassNotFoundException e){
LOGGER.error("can not load jdbc driver",e);
}
}
/**
* 获取数据库连接
*/
public static Connection getConnection() {
Connection conn = null;
try {
conn = DriverManager.getConnection(URl,USERNAME,PASSWORD);
} catch (SQLException e) {
LOGGER.error("get connection failure", e);
}
return conn;
}
/**
* 关闭数据库连接
*/
public static void closeConnection(Connection conn){
if(conn != null){
try{
conn.close();
}catch (SQLException e){
LOGGER.error("close connection failure",e);
}
}
}
/**
* 查询实体列表
*/
public static <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
List<T> entityList;
try {
Connection conn = getConnection();
entityList = QUERY_RUNNER.query(conn, sql, new BeanListHandler<T>(entityClass), params);
} catch (SQLException e) {
LOGGER.error("query entity list failure", e);
throw new RuntimeException(e);
}
return entityList;
}
}
存在的问题:
1.由于Connection是static变量,(静态对象可以节省频繁访问引起的频繁分配内存、释放内存、占用内存的性能开销)但是这样是线程不安全的。
第三版
使用ThreadLocal来存放本地线程变量。在每次获取Connection的时候,在ThreadLocal中查找,不存在,则新创建一个连接。
/**
* 数据库助手类
*/
public final class DatabaseHelper2 {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelperV.class);
private static final ThreadLocal<Connection> CONNECTION_HOLDER;
private static final QueryRunner QUERY_RUNNER = new QueryRunner();
private static final String DRIVER;
private static final String URL;
private static final String USERNAME;
private static final String PASSWORD;
private static final String URL;
static {
CONNECTION_HOLDER = new ThreadLocal<Connection>();
Properties conf = PropsUtil.loadProps("config.properties");
DRIVER = conf.getProperty("jdbc.driver");
URL = conf.getProperty("jdbc.url");
USERNAME = conf.getProperty("jdbc.username");
PASSWORD = conf.getProperty("jdbc.password");
try {
Class.forName(DRIVER);
}catch(ClassNotFoundException e){
LOGGER.error("can not load jdbc driver",e);
}
}
/**
* 获取数据库连接
*/
public static Connection getConnection() {
Connection conn = CONNECTION_HOLDER.get();
if (conn == null) {
try {
conn = DATA_SOURCE.getConnection();
} catch (SQLException e) {
LOGGER.error("get connection failure", e);
throw new RuntimeException(e);
} finally {
CONNECTION_HOLDER.set(conn);
}
}
return conn;
}
/**
* 关闭数据库连接
*/
public static void closeConnection(){
Connection conn = CONNECTION_HOLDER.get();
if(conn != null){
try {
conn.close();
}catch (SQLException e){
LOGGER.error("cose connection failure",e);
throw new RuntimeException(e);
}finally {
CONNECTION_HOLDER.remove();
}
}
}
/**
* 查询实体列表
*/
public static <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
List<T> entityList;
try {
Connection conn = getConnection();
entityList = QUERY_RUNNER.query(conn, sql, new BeanListHandler<T>(entityClass), params);
} catch (SQLException e) {
LOGGER.error("query entity list failure", e);
throw new RuntimeException(e);
}
return entityList;
}
}
存在的问题:每次都需要创建一个Connection,然后进行数据库操作,然后关闭。
第四版
引入了DBCP数据库连接池
/**
* 数据库助手类
*/
public final class DatabaseHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class);
private static final ThreadLocal<Connection> CONNECTION_HOLDER;
private static final QueryRunner QUERY_RUNNER;
private static final BasicDataSource DATA_SOURCE;
static {
CONNECTION_HOLDER = new ThreadLocal<Connection>();
QUERY_RUNNER = new QueryRunner();
Properties conf = PropsUtil.loadProps("config.properties");
String driver = conf.getProperty("jdbc.driver");
String url = conf.getProperty("jdbc.url");
String username = conf.getProperty("jdbc.username");
String password = conf.getProperty("jdbc.password");
DATA_SOURCE = new BasicDataSource();
DATA_SOURCE.setDriverClassName(driver);
DATA_SOURCE.setUrl(url);
DATA_SOURCE.setUsername(username);
DATA_SOURCE.setPassword(password);
}
/**
* 获取数据库连接
*/
public static Connection getConnection() {
Connection conn = CONNECTION_HOLDER.get();
if (conn == null) {
try {
conn = DATA_SOURCE.getConnection();
} catch (SQLException e) {
LOGGER.error("get connection failure", e);
throw new RuntimeException(e);
} finally {
CONNECTION_HOLDER.set(conn);
}
}
return conn;
}
/**
* 执行查询语句
*/
public List<Map<String, Object>> executeQuery(String sql, Object... params) {
List<Map<String, Object>> result;
try {
Connection conn = getConnection();
result = QUERY_RUNNER.query(conn, sql, new MapListHandler(), params);
} catch (Exception e) {
LOGGER.error("execute query failure", e);
throw new RuntimeException(e);
}
return result;
}
}
在这个过程中,黄老师已经教我们开发了一个轻量级的JDBC框架。
最终代码
干货分享:
读取配置文件——Java读取Properties配置文件
ThreadLocal的用法——传送门-ThreadLocal那点事儿
架构探险——第二章(为web应用添加业务功能)的更多相关文章
- 读《架构探险——从零开始写Java Web框架》
内容提要 <架构探险--从零开始写Java Web框架>首先从一个简单的 Web 应用开始,让读者学会如何使用 IDEA.Maven.Git 等开发工具搭建 Java Web 应用:接着通 ...
- 【EatBook】-NO.3.EatBook.3.JavaArchitecture.2.001-《架构探险:从零开始写Java Web框架》-
1.0.0 Summary Tittle:[EatBook]-NO.3.EatBook.3.JavaArchitecture.2.001-<架构探险:从零开始写Java Web框架>- S ...
- 2017.12.12 架构探险-第一章-从一个简单的web应用开始
参考来自:<架构探险>黄勇 著 1 使用IDEA搭建MAVEN项目 1.1 搭建java项目 (1)创建java项目 为了整个书籍的项目,我创建了一个工程,在这个工程里创建了每个章节的mo ...
- 架构探险——从零开始写Java Web框架》第二章照作
沉下来慢慢看实现了. 越来越觉得可以和DJANGO作对比. package org.smart4j.chapter2.model; /** * Created by sahara on 2016/3/ ...
- 第二章、Web中使用shiro(实现登陆)
虽然Apache Shiro的核心设计目标允许它用于保护任何基于JVM的应用程序(如命令行应用程序,服务器守护程序,Web应用程序等),但本指南将着重讨论最常见的用例:确保运行的Web应用程序一个Se ...
- 《架构探险——从零开始写Java Web框架》这书不错,能看懂的入门书
这书适合我. 哈哈,结合 以前的知识点,勉强能看懂. 讲得细,还可以参照着弄出来. 希望能坚持 完成啦... 原来,JSTL就类似于DJANGO中的模板. 而servlet类中的res,req,玩了D ...
- 002-读书笔记-企业IT架构转型之道-阿里巴巴中台战略思想与架构实战-第二章 构建业务中台的基础-共享服务体系简介
2.1.回归SOA的本质-服务重用 SOA理念的核心价值:松耦合的服务带来业务的复用,通过服务的编排助力业务的快速响应和创新. 现有模式多是烟囱式结合 ESB 企业总线打通不同系统间的交互. 2.2. ...
- 《深入理解linux内核架构》第二章 进程管理和调度
2.1进程优先级 进程优先级 硬实时进程 软实时进程 抢占式多任务处理 2.2进程生命周期 用户太切换到核心态的办法 系统调用 中断 抢占调度模型优先级普通进程<系统调用<中断 普通进程可 ...
- 网络安全从入门到精通 (第二章-1) Web安全前端基础
本文内容: 前端是什么? 前端代码 HTML CSS JS !!!醋成酒的小墨,促成就的小墨,小墨促成就,!!! 1,前端是什么? 网站一般用两部分组成,前端负责展示,后端负责处理请求. 2,前端代码 ...
随机推荐
- <测试用例设计>用户及权限管理功能常规测试方法
1) 赋予一个人员相应的权限后,在界面上看此人员是否具有此权限,并以此人员身份登陆,验证权限设置是否正确(能否超出所给予的权限): 2) 删除或修改已经登陆系统并正在进行操作的人员的权限,程序能否 ...
- [转]linux之nl命令
转自:http://www.cnblogs.com/peida/archive/2012/11/01/2749048.html nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内容 ...
- asp.net mvc源码分析-Action篇 IModelBinder
我们首先还是看看ReflectedParameterBindingInfo的Binder属性吧: public override IModelBinder Binder { ge ...
- sphinx下的max_matches取值对SetLimits的影响
使用PHP在客户端执行 $s -> SetLimits(0, 15, 1200); 传递的第三个参数,是服务器端设定当前查询的结果集大小为1200,但是运行结果,确实$s最终查询得到的结果为空值 ...
- c# webbrowser获取滚动条最大值
int HeightMax = 0; int WidthMax =0; HeightMax = webBrowser1.Document.Body.ScrollRectangle.Height-web ...
- iOS OpenCV 缺少64位解决方法
- js运动 运动效果留言本
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...
- reds pub/sub官方文档翻译
Publish / Subscribe发布/订阅 redis-py includes a PubSub object that subscribes to channels and listens f ...
- Ubuntu下实用命令收集 --新增 删除 环境变量
1. 关闭防火墙: sudo ufw disable 2. 设置IPV4地址和网关: ifconfig eth0 up %s netmask %s route del default gw 192.1 ...
- 读取proc信息的可扩展实现
需求 1. 将内存.线程数等信息注册到zk上进行监控 2. 统计信息,为下一步做负载均衡做准备. 实现 本文只解决问题1. 从网上查询了下,这些信息可以从proc文件系统中获取,如果不知道proc的, ...