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
连接池
我写了简单的连接池,可以默认初始化连接、不够是自增等,还未测试。(等测试后,我们把代码放到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{
上面注释掉的注解,就是用来实现这个的。