手写SpringMCV框架

本文详细介绍了如何手写SpringMVC框架的两个核心模块:通用数据访问模块,包括DatabaseUtil、TypeConstant和CommonDao类,实现了数据库连接的管理、类型映射和SQL操作;以及请求分发模块,涉及到DispatcherServlet、ControllerMapping和配置文件的处理。通过这些组件,简化了项目中的数据访问和请求处理流程。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

 

目录

前言

一、SpringMVC是什么?

二、代码实现

1.通用数据访问模块

2.请求分发模块

总结


 

前言

这是一个简单的手写SpringMVC框架


提示:以下是本篇文章正文内容,下面案例可供参考

一、SpringMVC是什么?

 

二、代码实现

1.通用数据访问模块

·DatabaseUtil

数据库连接管理类,包含了连接、销毁连接等方法。

package my.framework.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 数据库连接与关闭工具类。
 */
public class DatabaseUtil {
   private static String driver = ConfigManagers.getProperty("driver");// 数据库驱动字符串
   private static String url = ConfigManagers.getProperty("url");// 连接URL字符串
   private static String user = ConfigManagers.getProperty("user"); // 数据库用户名
   private static String password = ConfigManagers.getProperty("password"); // 用户密码

   static {
      try {
         Class.forName(driver);
      } catch (ClassNotFoundException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }

   private static final ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

   /**
    * 获取数据库连接对象。
    * 
    * @throws SQLException
    */
   public static Connection getConnection() throws SQLException {
      // 获取连接并捕获异常
      Connection connection = threadLocal.get();
      if (connection == null || connection.isClosed())
         try {
            connection = DriverManager.getConnection(url, user, password);
            if (connection.getAutoCommit())
               connection.setAutoCommit(false);
            threadLocal.set(connection);
         } catch (SQLException e) {
            e.printStackTrace();
            throw e;
         }
      return connection;// 返回连接对象
   }

   /**
    * 关闭语句容器。
    * 
    * @param stmt
    *            Statement对象
    * @param rs
    *            结果集
    */
   public static void closeStatement(Statement stmt, ResultSet rs) {
      // 若结果集对象不为空,则关闭
      try {
         if (rs != null && !rs.isClosed())
            rs.close();
      } catch (Exception e) {
         e.printStackTrace();
      }
      // 若Statement对象不为空,则关闭
      try {
         if (stmt != null && !stmt.isClosed())
            stmt.close();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   /**
    * 关闭数据库连接。
    */
   public static void closeConnection() {
      Connection connection = (Connection) threadLocal.get();
      threadLocal.set(null);

      try {
         if (connection != null && !connection.isClosed())
            connection.close();
      } catch (SQLException e) {
         e.printStackTrace();
      }
   }

}

1.初始化一个ThreadLocal对象,有get()、set()方法。

2.get()方法返回当前线程的线程内部变量,即Connection对象

3.如果当前线程是初次访问数据库,Connection对象为NULL,则创建一个Connection对象

4.把创建的Connection对象保存到ThreadLocal中,即和当前线程绑定。则当该线程在同一次请求中再次访问数据库时,从ThreadLocal中get()就可以获取刚才使用过的Connection对象。

5.业务流程的最后,关闭Connection时,首先调用ThreadLocal的get()方法获取和当前线程绑定的Connection对象,接着调用set()方法,把ThreadLocal管理的Connection对象置为NULL,从而执行了对当前线程的清理工作,为使用该线程的清理工作,为使用该线程处理另一个用户请求做好准备。

 

·TypeConstant

定义Java数据类型与数据库类型的对应关系,以便CommonDao中进行自动的数据处理

package my.framework.dao;

import java.util.HashMap;
import java.util.Map;

/**
 * 实现数据库类型与Java类型的映射
 */
public class TypeConstant {
	/***
	 * 数据库类型和Java类型映射,key为数据库类型,value为Java类型
	 */
	private static Map<String, Class<?>> typeMap = new HashMap<String, Class<?>>();

	static {
		typeMap.put("BIGINT", Long.class);
		typeMap.put("INT", Integer.class);
		typeMap.put("VARCHAR", String.class);
		typeMap.put("TEXT", String.class);
		typeMap.put("DATETIME", java.util.Date.class);
		typeMap.put("DECIMAL", Double.class);
		typeMap.put("TINYINT", Integer.class);
		typeMap.put("BIT", Boolean.class);
		typeMap.put("TIMESTAMP", java.util.Date.class);
	}

	public static void addType(String columnType, Class<?> javaType) {
		typeMap.put(columnType, javaType);
	}
	public static Class<?> getJavaType(String columnType) {
		return typeMap.get(columnType);
	}
}

·CommonDao

定义了通用的数据库访问方法,并对查询条件和查询结果的处理进行封装。

package my.framework.dao;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import my.framework.dao.DatabaseUtil;

/**
 * 执行数据库操作的工具类。
 */
public class CommonDao {

	/**
	 * 增、删、改操作
	 * 
	 * @param sql
	 *            sql语句
	 * @param prams
	 *            参数数组
	 * @return 执行结果
	 * @throws SQLException
	 */
	public int executeUpdate(String sql, Object[] params) throws SQLException {
		int result = 0;
		PreparedStatement pstmt = null;
		try {
			pstmt = DatabaseUtil.getConnection().prepareStatement(sql);
			if (!(params == null || params.length == 0))
				for (int i = 0; i < params.length; i++) {
					pstmt.setObject(i + 1, params[i]);
				}
			result = pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			throw e;
		} finally {
			DatabaseUtil.closeStatement(pstmt, null);
		}
		return result;
	}

	public int executeUpdate(String sql, Object entity) throws SQLException {
		Map<String, Object[]> mappingResult = handleParameterMapping(sql, entity);
		Entry<String, Object[]> entry = mappingResult.entrySet().iterator().next();
		return executeUpdate(entry.getKey(), entry.getValue());
	}

	protected Map<String, Object[]> handleParameterMapping(String sql, Object entity) throws SQLException {
		Map<String, Object[]> mappingResult = new HashMap<String, Object[]>();
		List<String> placeholders = new ArrayList<String>();
		int offset = 0;
		while (true) {
			int start = sql.indexOf("#{", offset);
			if (start >= offset) {
				int end = sql.indexOf("}", start);
				placeholders.add(sql.substring(start + 2, end));
				offset = end + 1;
			} else {
				break;
			}
		}
		if (placeholders.size() > 0) {
			Object[] params = new Object[placeholders.size()];
			for (int i = 0; i < placeholders.size(); ++i) {
				String placeholder = placeholders.get(i);
				sql = sql.replaceFirst("\\#\\{" + placeholder + "\\}", "?");
				try {
					Method getter = entity.getClass()
							.getMethod("get" + placeholder.substring(0, 1).toUpperCase() + placeholder.substring(1));
					Object param = getter.invoke(entity);
					params[i] = param;
				} catch (NullPointerException | NoSuchMethodException | SecurityException | IllegalAccessException
						| IllegalArgumentException | InvocationTargetException e) {
					throw new RuntimeException("无法为占位符 #{" + placeholder + "} 赋值!", e);
				}
			}
			System.out.println("===================================SQL: " + sql); // log
			String ps = "[\n";
			for (Object p : params) ps += "\t" + p + "\n";
			ps += "]";
			System.out.println("===================================Params: \n" + ps); // log
			mappingResult.put(sql, params);
		} else {
			mappingResult.put(sql, new Object[] {});
		}
		return mappingResult;
	}

	/**
	 * 查询操作
	 * 
	 * @param sql
	 *            sql语句
	 * @param params
	 *            参数数组
	 * @return 查询结果
	 * @throws SQLException
	 */
	public <T> List<T> executeQuery(Class<T> clz, String sql, Object[] params) throws SQLException {
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			pstmt = DatabaseUtil.getConnection().prepareStatement(sql);
			if (!(params == null || params.length == 0))
				for (int i = 0; i < params.length; i++) {
					pstmt.setObject(i + 1, params[i]);
				}
			rs = pstmt.executeQuery();

			return handleResultMapping(clz, rs);
		} catch (SQLException e) {
			e.printStackTrace();
			throw e;
		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
				| InvocationTargetException e) {
			throw new RuntimeException("封装查询结果时出现错误!", e);
		} finally {
			DatabaseUtil.closeStatement(pstmt, rs);
		}
	}

	public <T> List<T> executeQuery(Class<T> clz, String sql, Object entity) throws SQLException {
		Map<String, Object[]> mappingResult = handleParameterMapping(sql, entity);
		Entry<String, Object[]> entry = mappingResult.entrySet().iterator().next();
		return executeQuery(clz, entry.getKey(), entry.getValue());
	}

	@SuppressWarnings("unchecked")
	protected <T> List<T> handleResultMapping(Class<T> clz, ResultSet rs) throws SQLException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		if (clz.equals(Integer.class)) {
			List<Integer> result = new ArrayList<Integer>();
			while (rs.next())
				result.add(rs.getInt(1));
			return (List<T>) result;
		}
		ResultSetMetaData metaData = rs.getMetaData();
		int count = metaData.getColumnCount();
		if (clz.equals(Object[].class)) {
			List<Object[]> result = new ArrayList<Object[]>();
			Object[] row = null;
			while (rs.next()) {
				row = new Object[count];
				for (int i = 0; i < count; ++i) {
					Class<?> targetClz = TypeConstant.getJavaType(metaData.getColumnTypeName(i + 1));
					if (targetClz.equals(java.util.Date.class))
						row[i] = new java.util.Date(rs.getTimestamp(i + 1).getTime());
					else
						row[i] = rs.getObject(i + 1, targetClz);
				}
				result.add(row);
			}
			return (List<T>) result;
		}
		Method[] setters = new Method[count];
		Class<?>[] types = new Class[count];
		for (int i = 0, j = 1; i < count; ++i, ++j) {
			String lable = metaData.getColumnLabel(j);
			String type = metaData.getColumnTypeName(j);
			try {
				setters[i] = clz.getMethod("set" + lable.substring(0, 1).toUpperCase() + lable.substring(1),
						TypeConstant.getJavaType(type));
				types[i] = TypeConstant.getJavaType(type);
			} catch (NoSuchMethodException | SecurityException e) {
				e.printStackTrace(); // log
				setters[i] = null;
				types[i] = null;
			}
		}
		List<T> result = new ArrayList<T>();
		T instance = null;
		while (rs.next()) {
			instance = clz.newInstance();
			for (int k = 0; k < count; ++k) {
				if (setters[k] == null)
					continue;
				else {
					System.out.println("===========================setter: " + setters[k].getName() + "( " + types[k].getTypeName() + " )");
					System.out.println("===========================param: " + (rs.getObject(k+1)==null?null:rs.getObject(k+1).getClass()));
					if (types[k].equals(java.util.Date.class))
						setters[k].invoke(instance, new java.util.Date(rs.getTimestamp(k + 1).getTime()));
					else
						setters[k].invoke(instance, rs.getObject(k + 1, types[k]));
				}
			}
			result.add(instance);
		}
		return result;
	}
}

·ConfigManager

负责读取保存有效数据库连接参数的配置文件,配置文件默认为存储在classpath根目录下的database.properties文件。

package my.framework.dao;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class ConfigManagers {
    private static Properties props = null;

    static {
        InputStream is = null;
        is = ConfigManagers.class.getClassLoader().getResourceAsStream(
                "database.properties");
        if (is == null)
            throw new RuntimeException("找不到数据库参数配置文件!");
        props = new Properties();
        try {
            props.load(is);
        } catch (IOException e) {
            throw new RuntimeException("数据库配置参数加载错误!", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static String getProperty(String key) {
        return props.getProperty(key);
    }
}

开发完毕测试,将以上Java类打包成jar文件,即可在其他项目中作为通用数据访问组件引入并使用,从而简化项目中数据访问模块的编码。

2.请求分发模块

在IDEA中新建一个独立的Java Web项目以开发请求分发模块,以对请求处理模式和响应处理模式进行规范。

·DispatcherServlet 类根据请求URL包含的映射信息调用对应功能模块处理请求。

·ControllerMapping 类封装功能模块类型信息,包括类名和方法名两部分信息。

·ControllerMappingManager 类加载映射配置文件,提取URL映射和模块方法的对应关系。

·PrintUtil 类负责输出JSON格式的响应。

·ConfigLoadingException 异常表示加载映射配置文件错误。

·ConfigException 异常表示映射配置文件存在问题,如未提供类名、方法名。

·PathException 异常表示请求URL包含的映射路径存在问题,找不到对应处理方法。

·ViewPathException 异常表示导航路径存在问题,如没有以"/"开头。

 

 


总结

项目暂未开发完善,如有想要全部代码,请一键三连,可能不在就是饿死了。

### 回答1: Spring Framework是一个开源的Java平台,它提供了一组全面的工具来支持Java应用程序开发。它主要包括IoC容器、AOP框架、事务管理、MVC框架、DAO框架、连接池等。它可以帮助程序员更好地组织代码,减少重复性工作,提高代码质量。手写Spring框架需要对Java编程和设计模式有较深的了解,并对Spring框架的源码有着深入的研究。 ### 回答2: Spring框架是一个开源的Java平台,用于构建企业级应用程序。它提供了一种全面的编程和配置模型,用于开发基于Java的应用程序和服务。手写Spring框架意味着从头开始实现Spring的核心功能。 手写Spring框架的基本步骤包括: 1. 创建一个核心容器类,用于管理应用程序中的Bean对象。这个容器类需要提供注册、获取和销毁Bean的功能。 2. 定义一个Bean注解,用于标识哪些类应该被容器所管理。 3. 创建一个Bean定义类,用于存储每个Bean的元数据信息,包括类名、作用域和依赖关系等。 4. 实现依赖注入功能,通过读取Bean定义中的依赖关系,将相关的Bean对象注入到目标Bean中。 5. 提供AOP(面向切面编程)功能,允许开发者在应用程序的特定点进行横切关注点的处理。 6. 实现声明式事务管理功能,使用注解或XML配置方式来处理数据库事务。 7. 提供对不同数据访问技术(如JDBC、ORM框架、NoSQL等)的支持,通过集成相应的库来简化数据访问代码。 8. 增加对Web开发的支持,如处理请求、渲染视图等。 手写Spring框架需要具备对Java语言的深入了解,熟悉反射、代理、设计模式等概念和技术。还需要对依赖注入、AOP、事务管理、Web开发等方面有一定的理解。实现一个完整的Spring框架是一个庞大而复杂的任务,需要经过反复的设计、开发和测试。通过手写Spring框架,可以更深入地理解Spring的原理和内部机制,提高对框架的使用和调试能力。 ### 回答3: 手写Spring框架是一个相当复杂的任务,但我可以简要介绍手写Spring框架的一些关键步骤和组成部分。 1. 依赖注入:Spring框架的核心概念之一是依赖注入。我们需要编写一个容器,负责管理和维护各个对象之间的依赖关系。可以使用反射机制来实现依赖注入。 2. 控制反转:Spring框架通过控制反转(IoC)来管理对象的创建和生命周期。我们需要编写一个BeanFactory,负责加载和实例化对象,并将依赖注入到相应的对象中。 3. 配置文件:手写Spring框架也需要一个配置文件,用于定义对象的依赖关系和属性。我们可以使用XML、注解或者JavaConfig等方式来定义配置文件。 4. AOP支持:Spring框架提供了面向切面编程(AOP)的支持,可以通过编写切面和通知来实现横切关注点的统一处理。我们需要实现一个AOP框架,用于拦截和处理切面逻辑。 5. MVC模式:Spring框架也提供了一个MVC框架,用于处理Web应用程序的请求和响应。我们需要编写一个DispatcherServlet,负责将请求分发给相应的控制器,并处理模型和视图的交互。 6. 整合其他技术:Spring框架还可以与其他技术进行整合,例如数据库访问、事务管理、安全性控制等。我们需要编写相应的模块,将这些技术与Spring框架集成起来。 虽然这只是一个简要的介绍,手写Spring框架是一个非常庞大的工程,需要深入理解Spring的原理和设计思想,并具备扎实的Java编程能力。但通过手写Spring框架,我们可以更好地掌握Spring的核心概念和原理,并加深对框架的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值