相关资料,网上的资料很多,但是文章看不懂,看别人写好的代码比较好理解

ssm-example

mysssm

整个流程和原理

  • 一个入口类,入口类需要在tomcat启动的时候执行
  • 通过扫描文件加把文件取出来,存进classList里
  • 把mapper的注解扫描,创建一个叫mapper的map
  • 收集注解,存进各自的Map里【controllerMap,serviceMap,mapperMap】
  • 生成mybatis代理函数
  • 把serviceMap里的值赋值到controller层的Autowired上
  • 把mapperMap里的值赋值到service层的Autowired上
  • 把RequestMapping拼接成key,value是对应的类方法,存进handlerMap里
  • 重写HttpServlet的doGet和doPost方法

前提,下载maven依赖

  • servlet,tomocat依赖
  • dom4j,转化xml文件
  • mysql,连接数据库插件
  • lang3,工具包
  • fastjson,json工具

文件目录

创建入口文件

// core/springmvc/DispatcherServlet.Java

public class DispatcherServlet extends HttpServlet {
//收集文件路径的List
List<String> classList = new ArrayList();
Map<String, Object> controllerMap = new HashMap<String, Object>();
Map<String, Object> serviceMap = new HashMap<String, Object>();
Map<String, Object> mapperMap = new HashMap<String, Object>();
// key是拼接的全路径,value是方法
Map<String, Object> handlerMap = new HashMap<String, Object>(); // 核心的入口方法
// 上面的流程都在这个方法里
@Override
public void init() throws ServletException {
// 扫描文件
scanPackage("com.pdt.ssm");
// 收集注解,存进对应的map里
doInstance();
// 生成proxy函数
mybatisDo();
// 把controller层的Autowired赋值上serviceMap的对应类
controllerIoc();
// 把service层的Autowired赋值上mapperMap的对应proxy函数
serviceIoc();
// 把RequestMapping拼接好作为key存进map里
buildUrlMapping();
}
}

web设置

// webapp/web-inf/web.xml
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.pdt.core.springmvc.DispatcherServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

创建注解

  • springMvc注解【RequestMapping,RequestParam】
  • spring注解【Controller,Service,Autowired】
  • mybatis注解【Mapper,Select】

难点是Mybatis

使用过Mybatis可以知道,不过是xml方式还是注解方式,那个接口的方法并不存在,所以需要自己创建相对应的方法,需要两个用于储存的Bean

  • Function,用于存Select注解的数据
  • MapperBean,用于存Mapeer注解接口里的所有方法
private void mybatisDo() {
// mapperMap是在上一个方法里搜集到的mapper注解的类
for (Map.Entry<String, Object> entry : mapperMap.entrySet()) {
Class clazz = (Class) entry.getValue();
MapperBean mapper = new MapperBean();
// 用来存储方法的list
List<Function> list = new ArrayList<Function>();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
int annsLen = method.getAnnotations().length;
if(annsLen==0){
System.out.println("没有mybatis注解");
}else{
// 用来存储一条方法的记录
Function function = new Function();
// 如果是select注解
if (method.isAnnotationPresent(Select.class)) {
function.setSqltype("select");
function.setSql(method.getAnnotation(Select.class).value());
function.setFuncName(method.getName());
function.setResultType(method.getReturnType().getName());
// 这个是获取泛型,为了自动注入
if(method.getGenericReturnType().getTypeName().contains("<")){
function.setSetGenericReturnType(method.getGenericReturnType().getTypeName().split("<")[1].split(">")[0]);
}
}else{
// 其他注解
}
list.add(function);
mapper.setInterfaceName(entry.getKey());
mapper.setList(list);
}
}
// 核心内容,通过Proxy创建一个新的方法
Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] {clazz},
new MapperProxy(mapper));
// 把创建出来的方法替代原本的key的内容
entry.setValue(obj);
}
}

核心中的核心,MapperProxy类

public class MapperProxy<T> implements InvocationHandler{
private MapperBean mapperBean;
public MapperProxy(MapperBean mapperBean) {
this.mapperBean = mapperBean;
} @Override
public T invoke(Object proxy, Method method, Object[] args) throws Exception{
List<Function> list = mapperBean.getList();
if(null != list || 0 != list.size()){
for(Function function : list) {
// 看id是否和接口的方法名一样
// 应该判断参数数量和格式是不是对应的
if(method.getName().equals(function.getFuncName())){
String ann = function.getSqltype();
// 如果是select注解
if(ann.equals("select")){
// 判断返回类型,决定执行什么sql方法,具体查看代码
}
}
return null;
}
}

给service的Autowired注解赋值,controller同理

private void serviceIoc() {
for (Map.Entry<String, Object> entry : serviceMap.entrySet()) {
Object instance = entry.getValue();
Class<?> clazz = instance.getClass();
if (clazz.isAnnotationPresent(Service.class)) {
//获得所有声明的参数 得到参数数组
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
Autowired autowired = field.getAnnotation(Autowired.class);
String key = autowired.value();
//打开私有属性的权限修改
field.setAccessible(true);
try {
//给变量重新设值,这个mapperMap取出的就是上面的Proxy生成的方法
field.set(instance, mapperMap.get(key));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
continue;
}
}
} else {
continue;
}
}
}

自动注入,这里实现了两处

  • 前端请求自动注入,查看下面的代码
  • 查询数据自动注入,同理
public static Object handleRequest(String typeStr, HttpServletRequest req) throws Exception{
// typeStr是全路径的bean位置字符串,例如com.pdt.bean.User
Object res = Class.forName(typeStr).newInstance();
Field[] fields = Class.forName(typeStr).getDeclaredFields();
for (Field field : fields) {
String fieldType = field.getType().getName();
String fieldName = field.getName();
//打开私有属性的权限修改
field.setAccessible(true);
field.set(res,req.getParameter(fieldName));
}
// 这个返回的值就是已经存好了值的
return res;
}

重写响应方法

// DispatcherServlet.Java

@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 {
//获取请求路径
String url = req.getRequestURI();
String context = req.getContextPath();
String path = url.replace(context, "");
Method method= (Method) handlerMap.get(path);
// 从controllerMap取出对应key的类方法
Object controller= null;
//根据key去拿实例
if(path.equals("/")){
controller= controllerMap.get("/");
}else{
controller= controllerMap.get("/"+path.split("/")[1]);
}
try {
if(controller==null || method==null){
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().println("<h1>404</h1>");
}else{
Class<?> returnType = method.getReturnType();
if(returnType == null){
System.out.println("Controller层不能无返回");
}else{
// 参数处理
String returnTypeSimpleName = returnType.getSimpleName();
List parameList = RequsetParameter.handle(req,resp,method.getParameters());
// 最后返回处理
ReturnParameter.handle(returnTypeSimpleName,parameList,method,controller,resp);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

最后返回处理,根据返回类型给前端不同的响应

public class ReturnParameter {
public static void handle(String returnTypeSimpleName, List parameList, Method method, Object controller, HttpServletResponse resp)throws Exception{
if(returnTypeSimpleName.equals("String")){
// 这里应该判断有没有body注解的,没有就返回页面,有返回字符串
String res = (String) method.invoke(controller,parameList.toArray());
resp.setContentType("text/plain;charset=UTF-8");
resp.getWriter().println(res);
}else if(returnTypeSimpleName.equals("List")){
List list = (List) method.invoke(controller,parameList.toArray());
String res = JSON.toJSONString(list);
resp.setContentType("application/json;charset=UTF-8");
resp.getWriter().println(res);
}else if(returnTypeSimpleName.equals("Map")){
Map map = (Map) method.invoke(controller,parameList.toArray());
String res = JSON.toJSONString(map);
resp.setContentType("application/json;charset=UTF-8");
resp.getWriter().println(res);
}
}
}

到这里,接受请求,处理请求,返回响应整个流程都完成了,代码上传到github上

手搓SSM的更多相关文章

  1. 手搓一个“七夕限定”,用3D Engine 5分钟实现烟花绽放效果

    七夕来咯!又到了给重要的人送惊喜的时刻. 今年,除了将心意融入花和礼物,作为程序员,用自己的代码本事手搓一个技术感十足"七夕限定"惊喜,我觉得,这是不亚于车马慢时代手写信的古典主义 ...

  2. 手搓一个兔子问题(分享一个C语言问题,持续更新...)

    大家好,我是小七夜,今天就不分享C语言的基础知识了,分享一个比较好玩的C语言经典例题:兔子问题 题目是这样的:说有一个穷苦人这天捉到了一只公兔子,为了能繁衍后代他又买了一只母兔子,后来兔子开始生小兔子 ...

  3. 手搓一个C语言简单计算器。

    #include <stdio.h> void xing(int shu); void biaoti(int kong,char * title); void zhuyemian(char ...

  4. SSM框架练习之Jsp页面使用taglib标签报错500的问题

    最近在练手一个SSM的基于AdminLET框架模板的后台管理系统,使用的环境是tomcat9,使用Maven构建并通过添加Web模板框架的项目,在添加完所有的配置文件后启动tomcat运行,出现了一个 ...

  5. HNOI2015滚粗记

    HNOI2015滚粗记 经过两天的苦战,艰难的HNOI终于结束了.感觉这次HNOI自己还是收获了许多. \(Day1\)打的很是艰难,题目一下就有种晕头转向的感觉.开场\(20min\)自己还在读题时 ...

  6. CTSC2015&APIO2015滚粗记

    CTSC 这次CTSC的考试,觉得还是考出了自己该有的水平.虽然自己最后还是没有得到金牌,但是我觉得自己尽力了,也没有什么太大的遗憾.比起省选,自己在应试的方面又有了很大的进步. Day1是我主要捞分 ...

  7. 洛谷 P1177 【模板】快速排序【13种排序模版】

    P1177 [模板]快速排序 题目描述 利用快速排序算法将读入的N个数从小到大排序后输出. 快速排序是信息学竞赛的必备算法之一.对于快速排序不是很了解的同学可以自行上网查询相关资料,掌握后独立完成.( ...

  8. LeetCode题解 | 215. 数组中的第K个最大元素

    在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6,4] 和 k = 2 输出: 5 ...

  9. PAT甲级1123 Is It a Complete AVL Tree【AVL树】

    题目:https://pintia.cn/problem-sets/994805342720868352/problems/994805351302414336 题意: 给定n个树,依次插入一棵AVL ...

随机推荐

  1. CRT中国剩余定理 & Lucas卢卡斯定理

    数论_CRT(中国剩余定理)& Lucas (卢卡斯定理) 前言 又是一脸懵逼的一天. 正文 按照道理来说,我们应该先做一个介绍. 中国剩余定理 中国剩余定理,Chinese Remainde ...

  2. mysql测试点

    前言 性能测试过程中,数据库相关指标的监控是不可忽视的,在这里我们就MySQL的监控配置及重点涉及性能的一些参数进行说明. 在笔者的日常性能测试过程中,重点关注了这些参数,但不代表仅仅只有这些参数对性 ...

  3. [C/C++] _tprintf() 输出不了汉字

    在前面加一个 setlocale(LC_ALL, ""); //必须得有这行 否则不能输出中文 注意得加locale头文件 #include<stdio.h> #inc ...

  4. 原生js登录创建cookie

    原生js创建cookie,功能:点击登录按钮时,将用户名.密码存为cookie:页面再次加载时,自动读取cookie中的用户名.密码. <html><head><titl ...

  5. Springboot项目搭建(1)-创建,整合mysql/oracle,druid配置,简单的CRUD

    源码地址:https://github.com/VioletSY/article-base 1:创建一个基本项目:https://blog.csdn.net/mousede/article/detai ...

  6. 传奇定时器OnTimer功能详解(泡点、时间触发、任务活动)

    传奇定时器OnTimer功能详解(泡点.时间触发.任务活动) 定时器功能,是传奇服务端中非常常见的一种功能,常见如:泡点脚本.赌博脚本,任务活动指定时间刷怪,时间触发一些都需要用到OnTimer功能, ...

  7. leetCode练题——27. Remove Element

    1.题目 27. Remove Element——Easy Given an array nums and a value val, remove all instances of that valu ...

  8. 红黑树java代码实现

    红黑树 思想源于:https://www.cnblogs.com/nananana/p/10434549.html有解释有图,很清晰(删除时需考虑根节点和兄弟节点的子节点是否存在) package t ...

  9. Windows配置本地Hadoop运行环境

    很多人喜欢用Windows本地开发Hadoop程序,这里是一个在Windows下配置Hadoop的教程. 首先去官网下载hadoop,这里需要下载一个工具winutils,这个工具是编译hadoop用 ...

  10. C#中equals和==的区别有哪些

    本文导读:C# 中==是用来判断变量的值是否相等,相等返回true,不相等返回false.Equals是用来判断两个对象(除string类型外)是否相等,相等的 条件是:值,地址,引用全相等,因为St ...