31、最简单的mvc框架tiny,增加Ioc,jdbc工具类(1个类),连接池(1个类)

Stella981
• 阅读 475

Ioc

按照mvc,我们需要把tiny分成3层,其中视图(Renderer抽象类)和Action我们已经在前面实现了,这次我们用最少的代码实现Model。

    model沿用Action的想法,用户自定义类,类名必须以Model结尾,同Action一样在初始化时放入Container容器内。model就是数据模型,我们这里充血模型,model的类名默认是同数据库的表名做关联的,即类名去掉Model后(转为小写)为表明,这样一对一映射,有时会简单很多,如保存和查询单个表时,当然了你可以传入复杂sql,返回的结果有基本类型、map、list等。

    model里需要访问数据库时,我们设计了DbUtil工具类,建议在model里使用,当然了这个还是看你,DbUtil本身没有限制。model是通过ioc注入进来的,在你访问这个action前。下面代码中的Container.inject(o);为容器向action中注入model实例。

Map<String,String> args = this.converter(req.getParameterMap());
            
            String key = UUID.randomUUID().toString();
            
            Container.inject(o);
            
            this.before(routes,args,key);
            
            Object result = o.getClass().getMethod(routes[1],Map.class).invoke(o,args);
            
            this.after(args,key);
            
            Container.clearReqAops(key);

Container.inject代码如下:

public static void inject(Object o) throws IllegalArgumentException, IllegalAccessException, InstantiationException{
        Field filedArr[] = o.getClass().getDeclaredFields();
        for (Field field:filedArr) {
            String ftn = field.getType().getSimpleName();
             if (ftn.endsWith("Model")) {
                 Object m = Container.getCls(ftn);
                 if(m != null){
                     field.set(o,((Class)m).newInstance());
                 }
             }
        }
    }

我们从容器中取出这个model类,然后实例化,注入到这个action的属性。

model的初始化,代码如下:

if (className.endsWith("Action.class")) {
                    packPath=packPath.replace(".class.", "");
                    Object o = Class.forName(packPath).newInstance();
                    String clsName = o.getClass().getSimpleName().substring(0,o.getClass().getSimpleName().lastIndexOf("Action"));
                    if(clsMap.get(clsName) != null){
                        new IllegalAccessException(clsName+" class 重复");
                    }else{
                        clsMap.put(clsName, o);
                    }
                }else if (className.endsWith("Model.class")) {
                    className=className.replace(".class", "");
                    packPath=packPath.replace(".class.", "");
                    Class o = Class.forName(packPath);
                    if(clsMap.get(className) != null){
                        new IllegalAccessException(className+" class 重复");
                    }else{
                        clsMap.put(className, o);
                    }
                }

同action几乎一样,这里不细说。我们看到增加model模型,我们没有增加一个类,只是增加1个方法,和10几行代码。

模型的使用(数据库连接池还未测试)

testAction

package web;

import java.util.Map;

import tiny.ContextUtil;
import tiny.JspRenderer;
import tiny.Renderer;

public class TinyTestAction {
    public UserModel user;
    public void hello(Map<String,String> args){
        
        System.out.println("aa:"+args.get("aa"));
        System.out.println("访问时间1:"+System.currentTimeMillis());
        //ContextUtil.getContext().getXXX;
    }
    
    public String hello2(Map<String,String> args){
        return "/index.jsp";
    }
    
    public Renderer hello3(Map<String,String> args){
        //数据库
        Map<String,Object> data = user.outStr(args);
        return new JspRenderer("/index.jsp",data);
    }
}

model类

package web;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import tiny.DbUtil;

public class UserModel {
    public Map<String,Object> outStr(Map<String,String> params){
        try {
            //验证,参数转换
            Map<String,Object> args = new HashMap();
            
            //访问数据库
            Map<String,Object> data = DbUtil.dao.load(this, args);
            
            //业务逻辑处理
            return data;
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
}

jdbc工具类

这个还没有开发全,model和表的自动映射只做了一个查询。(以后补上),其他的都已经实现。代码如下:

package tiny;

import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DbUtil {
    DataBase instance;
    //SqlLoader loader;
    public static final DbUtil dao = new DbUtil();
    public DbUtil(){
        instance = DataBase.instance();
        //loader = SqlLoader.instance();
    }
    
    public Map<String, Object> load(Object o, Map<String, Object> params)
            throws SQLException {
        Connection conn = instance.getConnection();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        Map<String, Object> result = null;

        try {
            stmt = this.prepareStatement(conn, dao.modelConverterSql(o, params));
            rs = stmt.executeQuery();
            result = this.mapConverter(rs);
        } finally {
            try {
                close(rs);
            } finally {
                close(stmt);
            }
            instance.release(conn);
        }
        return result;
    }
    
    public Map<String, Object> load(String sql, Object[] params)
            throws SQLException {
        Connection conn = instance.getConnection();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        Map<String, Object> result = null;

        try {
            stmt = this.prepareStatement(conn, sql);
            if (params != null) {
                this.setParams(stmt, params);
            }
            rs = stmt.executeQuery();
            result = this.mapConverter(rs);
        } finally {
            try {
                close(rs);
            } finally {
                close(stmt);
            }
            instance.release(conn);
        }
        return result;
    }

    public List<Map<String, Object>> query(String sql, Object[] params)
            throws SQLException {
        Connection conn = instance.getConnection();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        List<Map<String, Object>> result = null;

        try {
            stmt = this.prepareStatement(conn, sql);
            if (params != null) {
                this.setParams(stmt, params);
            }
            rs = stmt.executeQuery();
            result = this.listConverter(rs);
        } finally {
            try {
                close(rs);
            } finally {
                close(stmt);
            }
            instance.release(conn);
        }
        return result;
    }

    public int update(String sql, Object[] params) throws SQLException {
        Connection conn = instance.getConnection();
        PreparedStatement stmt = null;
        int rows = 0;
        try {
            stmt = this.prepareStatement(conn, sql);
            this.setParams(stmt, params);
            rows = stmt.executeUpdate();
        } finally {
            close(stmt);
            instance.release(conn);
        }

        return rows;
    }
    public void setParams(PreparedStatement stmt, Object... params)
            throws SQLException {
        if (params == null) {
            return;
        }

        ParameterMetaData pmd = null;
        pmd = stmt.getParameterMetaData();
        if (pmd.getParameterCount() < params.length) {
            throw new SQLException("Too many parameters: expected "
                    + pmd.getParameterCount() + ", was given " + params.length);
        }
        for (int i = 0; i < params.length; i++) {
            if (params[i] != null) {
                stmt.setObject(i + 1, params[i]);
            } else {
                int sqlType = Types.VARCHAR;
                try {
                    sqlType = pmd.getParameterType(i + 1);
                } catch (SQLException e) {
                }
                stmt.setNull(i + 1, sqlType);
            }
        }
    }

    private Map<String, Object> mapConverter(ResultSet rs) throws SQLException {
        Map<String, Object> result = null;
        if (rs.next()) {
            result = new HashMap<String, Object>();
            ResultSetMetaData metaData = rs.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String filed = metaData.getColumnName(i);
                result.put(filed, rs.getObject(filed));
            }
        }
        return result;
    }

    private List<Map<String, Object>> listConverter(ResultSet rs)
            throws SQLException {
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
        while (rs.next()) {
            ResultSetMetaData metaData = rs.getMetaData();
            Map<String, Object> rowData = null;
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                rowData = new HashMap<String, Object>();
                String filed = metaData.getColumnName(i);
                rowData.put(filed, rs.getObject(filed));
                result.add(rowData);
            }
        }
        if (result.size() > 0) {
            return result;
        } else {
            return null;
        }

    }

    private PreparedStatement prepareStatement(Connection conn, String sql)
            throws SQLException {
        return conn.prepareStatement(sql);
    }

    protected void close(Statement stmt) throws SQLException {
        if (stmt != null) {
            stmt.close();
        }
    }

    protected void close(ResultSet rs) throws SQLException {
        if (rs != null) {
            rs.close();
        }
    }
    

    private String modelConverterSql(Object o,Map<String, Object> params){
        String table = o.getClass().getSimpleName().replace("Model.", "").toLowerCase();
        StringBuffer sql = new StringBuffer();
        sql.append("select * from "+table+" where 1=1 ");
        if(params != null){
            for(String key : params.keySet()){
                Object v = params.get(key);
                if(v instanceof Integer){
                    sql.append(" and " + key +"="+ v);
                }else if(v instanceof String){
                    sql.append(" and " + key +"='"+v+"'");
                }else if(v instanceof String){
                    //其他未实现
                }
            }
        }
        return sql.toString();
    }
}

我把查询的结果自动转化为map或list,其中modelConverterSql为“ model和表的自动映射只做了一个查询”,很简单的,其他的增删改查我会补上。其中注释掉的“SqlLoader”,想把sql保存到文件里,然后用这个来读取。最下面有这个类的代码,也没有开发完就是思路雏形。(这个我不想在完善了,本来tiny就像想简单,感觉分开sql了,反而会和tiny不协调,再说吧)。

连接池

我写了简单的连接池,可以默认初始化连接、不够是自增等,还未测试。(等测试后,我们把代码放到oschina的git上)

这个等我完善后,在细说下。

package tiny;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Vector;

public class DataBase {
    private Vector<Connection> pool;
    private static int init_active = 10;
    private static int curr_active = 10;
    private static int max_active = 50;
    private static DataBase instance = null;
    private DataBase(){
        pool = new Vector<Connection>();
        InputStream propStream = DataBase.class.getResourceAsStream("/database.properties");
        Properties props = new Properties();
        if (propStream != null) {
            try {
                props.load(propStream);
                init_active = Integer.parseInt(props.getProperty("initial.active"));
                max_active = Integer.parseInt(props.getProperty("max.active"));
                //for(int c=0;c<init_active;c++){
                for(int c=0;c<max_active;c++){
                    Class.forName(props.getProperty("jdbc.driver"));
                    Connection conn = DriverManager.getConnection(props.getProperty("jdbc.url"),props.getProperty("jdbc.username"),props.getProperty("jdbc.password"));
                    pool.add(conn);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    propStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public synchronized void release(Connection conn){
        pool.add(conn);
    }
    public synchronized void closePool(){
        for(int c=0;c<pool.size();c++){
            try {
                pool.get(c).close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            pool.remove(c);
        }
    }
    public static DataBase instance(){
        if(instance == null){
            instance = new DataBase();
        }
        return instance;
    }
    public synchronized Connection getConnection(){
        if(pool.size()>0){
            Connection conn = pool.get(0);
            pool.remove(conn);
            return conn;
        }else{
            return null;
        }
    }
}

**

连接池访问的属性文件配置**

#jdbc.driver=oracle.jdbc.driver.OracleDriver
#jdbc.url=jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))(LOAD_BALANCE = yes)(CONNECT_DATA =    (SERVER = DEDICATED)(SERVICE_NAME = xe)))
#jdbc.username=oneteam
#jdbc.password=1q2w3e 

jdbc.driver=org.h2.Driver
jdbc.url=jdbc:h2:./h2db/eternal
jdbc.username=oneteam
jdbc.password=1q2w3e 
initial.active=10
max.active=50

**

测试action 的index.jsp**

<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
    <base href="<%=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/"%>">
    
    <title>tiny-瘦成一道隐形闪电</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

  </head>
  <body>
  <p align="center">
      tiny-瘦成一道隐形的闪电
  </p>
  <p align="center">
      <%
          String dd = (String)request.getAttribute("name");
        if(dd != null){
            out.print(dd);
        }
      %>
  </p>
  </body>
</html> 

**

SqlLoader类

package tiny;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class SqlLoader {
    private static SqlLoader instance = null;
    private Map<String,String> sqls = null;
    public static SqlLoader instance() {
        if(instance == null){
            instance = new SqlLoader();
        }
        return instance;
    }

    private SqlLoader() {
        InputStream propStream = SqlLoader.class.getResourceAsStream("/sqls.properties");
        Properties props = new Properties();
        if (propStream != null) {
            try {
                props.load(propStream);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    propStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            sqls = (HashMap<String,String>)(new HashMap(props));
        }
    }

    @SuppressWarnings("unchecked")
    protected String get(String key) throws IOException {
        if(sqls == null){
            return null;
        }
        return sqls.get(key);
    }
    public synchronized void unload(){
        this.sqls = null;
    }

}

总结**

    tiny的开发就是突然的想法,虽然前前后后加一起开发的时间也就1天,但是我们还是开发出了很多东西的,实际使用时问题肯定是有的,tiny的开发主要想实现我当时的想法“瘦成一道隐形的闪电”,就是代码少的不能在少了,彻底0配置,不说你都不知道有action、model啥的(娱乐因素比较多,呵呵),最后的闪电就是tiny虽小,但是功能还是很多的action、多视图支持、aop、ioc、model充血模型、连接池、jdbc的dao封装等,也算一道小闪电。呵呵。

    tiny还有一个就是一直吵吵要增加的java调用前台js的,这个一直没实现,这个在酝酿,不过第一次写时就预留了,看西面代码:

//@WebFilter(urlPatterns = { "/demoAsyncLink" }, asyncSupported = true)
@WebFilter(urlPatterns = { "/ty/*" })
public class FrontControl implements Filter{

上面注释掉的注解,就是用来实现这个的。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
2年前
java常用类(2)
三、时间处理相关类Date类:计算机世界把1970年1月1号定为基准时间,每个度量单位是毫秒(1秒的千分之一),用long类型的变量表示时间。Date分配Date对象并初始化对象,以表示自从标准基准时间(称为“历元”(epoch),即1970年1月1日08:00:00GMT)以来的指定毫秒数。示例:packagecn.tanjian
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这