学习SSM源码分析的一次实践,自己实现SSM框架

首先介绍下这次的总体思路:
下载地址:https://download.csdn.net/download/drsbbbl/12264912
github(用的码云):https://gitee.com/mibaowei/ssm.git
在spring中最核心的就是IOC容器,默认采用的是通过单例的模式来进行管理我们注入到Ioc中的bean(当然我们也是可以修改成其他的模式,暂且不讨论),在spring中单例模式是采用注册的方式来实现的单例模式,所以我也是采用map注册的方式实现bean的实例化。关于spring中的注解,采用和spring一样的注解名字,在springMVC方面,采用统一的前端控制器dispatcherServlet,处理统一请求进行分发。
对应 mybatis 是采用代理的方式 对接口进行生成对应的代理对象,加载对应的xml 中的 数据和 对应的 执行占位符的情况 对占位符数据进行set 对应的数值。并且通过动态注入的方式进行 接口的调用,并且实现对应的调用对应xml中的sql片段。
说这么多 下面发下代码:

首先是 实现 对应的IOC容器:

采用最简单的 map 的方式 来实现 ioc容器
对应的key 就是类的名字的简写
实际在spring中。是通过 beanFactory的方式,进行生成对应的bean对象 采用 type 和name 的两种方式,我这里只是简写,只采用了一种方式进行 使用 就是类的全名 不像 spring中的是 类名 首字母小写 我没有采用这种方式,

/**
 * bean 工厂
 * 
 * @author mbb
 *
 */
public class BeanFactory {
	private static final Map<String, Object> IOC = new HashMap<>();

	/**
	 * 得到 ioc 容器
	 * 
	 * @return
	 */
	public static Map<String, Object> getIOC() {
		return IOC;
	}

	//
	public static Object getBean(String beanName) {
		return IOC.get(beanName);
	}

	public static void doInstance() {
		Set<Class<?>> classSet = ClassNames.getClassSet();
		if (classSet.isEmpty()) {
			return;
		}
		for (Class<?> clazz : classSet) {
			try {
				Object instance = clazz.newInstance();
				IOC.put(clazz.getName(), instance);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

简单说明下对应的注解:
除了 DAO注解 应该其他的都见过,
对应的主句
说明下DAO注解 是注在 对应的 Mapper接口上的。
简单截图下 使用方式:
和spring用法类型 不在说明
在这里插入图片描述
关于注解不懂的 可以翻翻我之前的文章

关于扫包的 类 进行说明:

public class GetPaceOverClass {

	public static void getAllPackOverClass(Map<String, Object> ioc, String packageName) {
		// List<Class<?>> list=new ArrayList<Class<?>>();
		// 1. 首先判断 包的写法 并且 对包 进行 修改 修改为 类的加载器能够加载的模式:
		// 类的加载器 加载 为 反斜杠的形式
		// packageName = packageName.replace(".", System.lineSeparator());

		// 2.创建 类的加载器 对 资源 进行加载 并且 启动 加载
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		// packageName=packageName.replace(System.lineSeparator(),".");
		try {
			// 3 获取到 此包下所有的资源
			Enumeration<URL> resources = contextClassLoader.getResources(packageName);
			while (resources.hasMoreElements()) {

				// 4.获取到 当前资源
				URL nextElement = resources.nextElement();
				// 获取 此 url 的类型 在网络传输中代表 协议名称
				// String type = nextElement.getProtocol();
				// 获取当前路劲名字
				String file = nextElement.getFile();
				File typeFile = new File(file);
				if (typeFile.isDirectory()) {
					// 此时代表 还是个文件 不是 对应的 jar文件
					// 递归调用 自己下一层
					File[] listFiles = typeFile.listFiles();
					for (File file2 : listFiles) {
						getAllPackOverClass(ioc, packageName + "/" + file2.getName());
					}
				}
				// 判断是否 是文件 并且 以 .class 结尾
				else if (typeFile.getName().endsWith(".class")) {
					// 读到对应类文件了 开始解析是否有注解
					try {
						System.out.println(packageName);

						String temp = packageName.replace("/", ".");
						System.out.println(temp);
						Class<?> clzz = Class.forName(temp.substring(0, packageName.lastIndexOf(".")));
						if (clzz.isInterface()) {
							if (clzz.isAnnotationPresent(Dao.class)) {

							}
						}
						isAnnotation(ioc, clzz);
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// return list;
	}

	private static void isAnnotation(Map<String, Object> ioc, Class<?> clzz) {
		ClassNames.addClass(clzz);
		// 得到需要注入到ioc容器中的类
		if (clzz.isAnnotationPresent(Dao.class)) {
			// 对mydao接口特殊处理 生成他的代理类
			//此处 处理了 特殊的接口 Mapper 接口  要生成对应的 特殊的 代理类
			Object SqlSessionFactoryBean = ioc.get("SqlSessionFactoryBean");
			if (SqlSessionFactoryBean == null) {
				Object newInstance = null;
				try {
					newInstance = edu.mbb.springmybatis.SqlSessionFactoryBean.class.newInstance();
				} catch (InstantiationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				ioc.put("SqlSessionFactoryBean", newInstance);
			}
			SqlSessionFactoryBean sqlsession = (edu.mbb.springmybatis.SqlSessionFactoryBean) ioc
					.get("SqlSessionFactoryBean");
			Object myDaoAnnotionProxy = sqlsession.getMyDaoAnnotionProxy(clzz);
			ioc.put(clzz.getSimpleName(), myDaoAnnotionProxy);
		}
		if (clzz.isAnnotationPresent(Controller.class) || clzz.isAnnotationPresent(Service.class)) {
			try {
			//把 注释了 Controller 和 service 注释的类  加载进入 对应的 IOC容器中去。并 new 对应的实体类 实现单例  spring 的单例体现 
			//真正spring中单例 是采用注册登记的方式 进行注册生成的 此处 是简写的方式
				ioc.put(clzz.getSimpleName(), clzz.newInstance());
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				System.out.println("bean注入失败");
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		// 存在了 进行 注解的操作

	}

}

对应的 DI操作:

package edu.mbb.ioc;

import java.lang.reflect.Field;
import java.util.Set;

import edu.mbb.annocation.Autowried;

public class DIClass {
	public static void Di() {
		Set<Class<?>> classSet = ClassNames.getClassSet();
		if (classSet == null || classSet.size() == 0) {
			return;
		}
		for (Class<?> class1 : classSet) {
			Field[] declaredFields = class1.getDeclaredFields();
			for (Field field : declaredFields) {
			//判断是否存在 对应的 注解 
				if (field.isAnnotationPresent(Autowried.class)) {
				//从ioc容器中 取出对应的 实体类
					Object bean = BeanFactory.getBean(class1.getSimpleName());
					if (bean == null) {
						System.out.println("注入失败");
						return;
					}
					field.setAccessible(true);
					Class<?> type = field.getType();
					//得到对应DI的 类型名字 用于查找对应的 IOC中的对象
					String simpleName = type.getSimpleName();
					try {
					//调用set给对应属性赋值
						field.set(bean, BeanFactory.getBean(simpleName));
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
						System.out.println("注入失败");
					} catch (IllegalAccessException e) {
						e.printStackTrace();
						System.out.println("注入失败");
					}
				}
			}
		}
	}
}

取出 所有的类 ,并且 把 对应的所有的类 取出来 看看当前的类 中 是否存在对应的autowire 注解
当存在对应的autowire注解的时候 从ioc容器中 取出对应的 单例 对象
通过 set 的方式 给当前 对象中 属性进行赋值

对应的 DispatcherServlet 对应的实现
原理很简单 先上代码:

package edu.mbb.core;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import edu.mbb.annocation.Controller;
import edu.mbb.annocation.RequestMapping;
import edu.mbb.ioc.BeanFactory;

public class DispatcherServlet extends HttpServlet {
	// mvc的核心控制类
	// 解析对应的 requestMapping
	private Map<String, Method> handlerMapping = new HashMap<>();
	// 处理controller 的 类 特殊处理
	private Map<String, Object> controllerMap = new HashMap<>();

	@Override
	public void init(ServletConfig config) throws ServletException {
		// 初始化handler映射
		initHandlerMapping();
	}

	private void initHandlerMapping() {
		// 得到ioc容器
		Map<String, Object> ioc = BeanFactory.getIOC();
		if (ioc.isEmpty()) {
			return;
		}
		try {
			Set<String> keySet = ioc.keySet();
			for (String key : keySet) {
				Class<? extends Object> clazz = ioc.get(key).getClass();
				// 只处理controller 可以放开处理所有的注解情况
				if (!clazz.isAnnotationPresent(Controller.class))
					continue;
				// 类上的requestmapping
				String baseUrl = "";
				if (clazz.isAnnotationPresent(RequestMapping.class)) {
					RequestMapping annotation = clazz.getAnnotation(RequestMapping.class);
					baseUrl = annotation.value();
				}
				Method[] methods = clazz.getMethods();
				for (Method method : methods) {
					if (!method.isAnnotationPresent(RequestMapping.class))
						continue;
					RequestMapping annotation = method.getAnnotation(RequestMapping.class);
					String url = annotation.value();
					url = (baseUrl + "/" + url).replaceAll("/+", "/");
					handlerMapping.put(url, method);
					controllerMap.put(url, ioc.get(key));
				}
				System.out.println(handlerMapping);
				System.out.println(controllerMap);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			doDispatch(req, resp);
		} catch (Exception e) {
			resp.getWriter().write("500!!!Server Exception");
		}
	}

	private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (handlerMapping.isEmpty())
			return;
		String uri = request.getRequestURI();
		String contentPath = request.getContextPath();
		uri = uri.replace(contentPath, "").replaceAll("/+", "/");
		if (!handlerMapping.containsKey(uri)) {
			response.getWriter().write("404 Not Found");
			return;
		}
		Method method = handlerMapping.get(uri);
		// 获取方法参数列表类型
		Class<?>[] parameterTypes = method.getParameterTypes();
		// 获取请求的参数
		Map<String, String[]> parameterMap = request.getParameterMap();
		// 保存参数值
		Object[] paramValues = new Object[parameterTypes.length];
		for (int i = 0; i < parameterTypes.length; i++) {
			// 根据参数名,进行处理
			String requestParam = parameterTypes[i].getSimpleName();
			if (requestParam.equals("HttpServletRequest")) {
				paramValues[i] = request;
				continue;
			}
			if (requestParam.equals("HttpServletResponse")) {
				paramValues[i] = response;
				continue;
			}
			if (requestParam.equals("String")) {
				for (Map.Entry<String, String[]> param : parameterMap.entrySet()) {
					String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
					paramValues[i] = value;
				}
			}
		}
		// 利用反射调用方法
		try {
			method.invoke(controllerMap.get(uri), paramValues);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

我们配置对应的 所有的请求 都进这个 servlet
对应的wb.xml配置;

  <servlet>
    <servlet-name>myDispatcherServlet</servlet-name>
    <servlet-class>edu.mbb.core.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>myDispatcherServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

对应的 加载对应的映射关系。
也就是把所有 加上 RequestMapper的注解的类中的方法。
对应的 url 也就是 类上面的 RequestMapping 加上对应的 方法上的RequestMapper 中的值 为 key 对应的method为 value 进行 一一对应存储,在初始化中方法进行调用。
通过request 可以获取到对应的请求地址,就可以找到对应的map中的需要调用的方法。通过 invoke 就可以 调用对应的方法并且通过 方法的参数列表就可以 设置对应的参数列表

String uri = request.getRequestURI();
		String contentPath = request.getContextPath();
		uri = uri.replace(contentPath, "").replaceAll("/+", "/");

我这里 只设置了 String类型和 request response 的处理 其他的可以自行添加。

关于spring 的启动类:

public class ContextLoaderListener implements ServletContextListener {

	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void contextInitialized(ServletContextEvent arg0) {
		// TODO Auto-generated method stub
		ServletContext servletContext = arg0.getServletContext();
		// 取出对应的初始化参数
		String initParameter = servletContext.getInitParameter("contextPath");
		if (initParameter == null || initParameter.length() > 0) {
			initParameter = "classpath:application.properties";
		}
		// 加载配置文件开始
		String[] split = initParameter.split(":");
		// 读到对应的配置文件
		String path = split[1];
		String basePackage = null;
		String xmlPath = null;
		InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(path);
		Properties pro = new Properties();
		try {
			pro.load(resourceAsStream);
			// 得到扫描包的路径
			basePackage = pro.getProperty("scanPackage");
			xmlPath = pro.getProperty("xmlPath");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("配置文件路径不存在");
			e.printStackTrace();
		}
		// 开始解析对应的注解和ioc解析
		// 开始 扫包开始 把注解全扫出来 ioc 注入完成
		GetPaceOverClass.getAllPackOverClass(BeanFactory.getIOC(), basePackage);
		// 进行依赖注入 扫描注解 autowite
		DIClass.Di();
		// 初始化 mybatis配置文件信息
		// 解析xml配置文件 进行 开始解析
		// xmlPath
		GetXMLConfiguration.init(xmlPath);
		/// EmpMapper employeeMapper = (EmpMapper)
		/// BeanFactory.getIOC().get("EmpMapper");
		// Emp findById = employeeMapper.findById("100");
		// System.out.println(findById);
	}

}

监听 tomcat容器的启动。也就是 监听Application对象的生成 对应的数据 监听 在xml进行配置。;

   <context-param>
    	<param-name>contextPath</param-name>
    	<param-value>classpath:application.properties</param-value>
    </context-param>
         <listener>
         	<listener-class>edu.mbb.core.ContextLoaderListener</listener-class>
         </listener>

mybatis中解析 对应的xml
简单说下 数据结构:

在这里插入图片描述

public class GetXMLConfiguration {

	public static void init(String xmlPath) {
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		try {
			Enumeration<URL> resource = contextClassLoader.getResources(xmlPath);
			while (resource.hasMoreElements()) {
				URL nextElement = resource.nextElement();
				// 获取当前路径的名字
				String file = nextElement.getFile();
				File tem = new File(file);
				File[] list = tem.listFiles();
				for (File fileName : list) {

					if (fileName.exists()) {
						if (fileName.getName().endsWith(".xml")) {
							// 开始解析xml
							SAXReader sax = new SAXReader();
							Document read = sax.read(fileName);
							Element rootElement = read.getRootElement();
							// rootElement.get
							// System.out.println(name);
							// nameSpace 名字
							String namespace = rootElement.attributeValue("namespace");
							List<Element> elements = rootElement.elements();
							for (Element element : elements) {
								String id = element.attributeValue("id");
								String text = element.getText();
								Configuration.insertXmlToInFaceMethodSql(id, text);
							}
							Configuration.insertMapperInFaceToXml(namespace, Configuration.getXmlToInFaceMethodSql());
							System.out.println(Configuration.getmapperInFaceToXml());
						}
					}
				}

			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		init("mapper");
	}
}
@Service
public class MyDefalutSqlSession implements MySqlSession {

	private MyExecutor executor = new MyBaseExecutor();

	@Override
	public <T> T selectOne(String sql, Class<?> returnType, String methodName) throws SQLException {
		return executor.query(sql, returnType, methodName);
	}

	@Override
	public <T> T getMapper(Class<T> clazz) {
		return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, new MyMapperProxy(this));
	}
}

生成代理对象

package edu.mbb.mybatis;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import edu.mbb.core.Configuration;

public class MyMapperProxy implements InvocationHandler {
	private MySqlSession sqlSession;

	public MyMapperProxy() {
	}

	public MyMapperProxy(MySqlSession sqlSession) {
		this.sqlSession = sqlSession;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("--------");
		// 类名字
		String mapperClass = method.getDeclaringClass().getName();
		// 方法的名字
		String name = method.getName();
		Class<?> returnType = method.getReturnType();
		Map<String, Object> xmlByInfaceName = (Map<String, Object>) Configuration.getXmlByInfaceName(mapperClass);
		// 得到这个方法的sql
		String sql = xmlByInfaceName.get(name).toString();
		// 判断sql中的 变量值

		/*
		 * SELECT ar.id, ar.aut_id, ar.title, ar.type, ar.content,
		 * ar.create_time, au.name, au.age, au.sex, au.email FROM article ar,
		 * author au WHERE ar.aut_id = au.id AND ar.id =
		 */
		// #{id}
		// Parameter[] parameters = method.getParameters();
		sql = replaceSql(sql, args, mapperClass + name);
		Object selectOne = sqlSession.selectOne(sql, returnType, mapperClass + name);
		System.out.println(sql);
		return selectOne;
	}

	public String replaceSql(String sql, Object[] args, String mapperClass) {
		// String temp = "SELECT ar.id, ar.aut_id, ar.title, ar.type,
		// ar.content,ar.create_time, au.name, au.age, au.sex, au.email
		// FROMarticle ar, author auWHERe ar.aut_id = au.id AND ar.id = #{id}
		// and id=15";
		// 带?的sql
		String parse = parse(sql, "#{", "}", Configuration.getXmlArgs(mapperClass), Configuration.getArgsValues(),
				args);
		return parse;
	}

	public static String parse(String text, String openToken, String closeToken, List<String> args,
			Map<String, Object> argsValue, Object[] temp) {
		if (text == null || text.isEmpty()) {
			return "";
		}
		char[] src = text.toCharArray();
		int offset = 0;
		// search open token
		int start = text.indexOf(openToken, offset);
		if (start == -1) {
			return text;
		}
		final StringBuilder builder = new StringBuilder();
		StringBuilder expression = null;
		int i = 0;
		while (start > -1) {
			if (start > 0 && src[start - 1] == '\\') {
				// this open token is escaped. remove the backslash and
				// continue.
				builder.append(src, offset, start - offset - 1).append(openToken);
				offset = start + openToken.length();
			} else {
				// found open token. let's search close token.
				if (expression == null) {
					expression = new StringBuilder();
				} else {
					expression.setLength(0);
				}
				builder.append(src, offset, start - offset);
				builder.append("?");

				System.out.println(builder.toString());
				offset = start + openToken.length();
				System.out.println(offset);
				int end = text.indexOf(closeToken, offset);
				System.out.println(end);
				while (end > -1) {
					if (end > offset && src[end - 1] == '\\') {
						// this close token is escaped. remove the backslash and
						// continue.
						expression.append(src, offset, end - offset - 1).append(closeToken);

						offset = end + closeToken.length();
						System.out.println(offset);
						end = text.indexOf(closeToken, offset);
					} else {
						expression.append(src, offset, end - offset);
						args.add(expression.toString());
						System.out.println(expression.toString());

						argsValue.put(expression.toString(), temp[i]);
						i++;
						System.out.println(expression.toString());
						offset = end + closeToken.length();
						System.out.println(offset);
						break;
					}
				}
				if (end == -1) {
					// close token was not found.
					builder.append(src, start, src.length - start);
					offset = src.length;
				} else {
					// builder.append(handler.handleToken(expression.toString()));
					offset = end + closeToken.length();
				}
			}
			start = text.indexOf(openToken, offset);
		}
		if (offset < src.length) {
			builder.append(src, offset, src.length - offset);
		}
		System.out.println(argsValue);
		System.err.println(args);
		return builder.toString();
	}

	public static void main(String[] args) {
		String temp = "SELECT ar.id, ar.aut_id, ar.title, ar.type, ar.content,ar.create_time, au.name, au.age, au.sex, au.email FROMarticle ar, author auWHERe ar.aut_id = #{test} AND ar.id = #{id} and id=18";
		String parse = parse(temp, "#{", "}", Lists.newArrayList(), Maps.newHashMap(), new Object[] { "100", "200" });
		System.out.println(parse);
	}
}

解析 XML 并生成对应的sql 在此处说明 解析XML 并把对应的 值 替换为 实际参数 参考 mybatis 的源码 parse 这个是 mybatis 源码。
至此 解析完毕
代码 我上传上去

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值