spring对于java程序员来说,无疑就是吃饭到筷子。在每次编程工作到时候,我们几乎都离不开它,相信无论过去,还是现在或是未来到一段时间,它仍会扮演着重要到角色。自己对spring有一定的自我见解,所以参考网上的视频和文章,整理出一套简单的SpirngMVC。

  项目地址先贴出来,接下来大概讲下流程。

  手写简单的SpringMvc框架。

  主要分为几个步骤:

  1. 扫描包下面的文件。

  2. 根据扫描到到文件,初始化bean工厂。

  3. 根据@Controller @RequestMapping 注解,处理映射关系。

  4.  启动tomcat。

1. 项目基于maven,framework模块是主要对SpringMVC框架,test只是为了方便测试,单独引用framework框架进行测试 。

2.接下来讲下主要framework模块。图如下:

(1)MiniApplication为框架的入口类。其主要有以下四个功能。

package com.chan.starter;

import com.chan.beans.BeanFactory;
import com.chan.core.ClassScanner;
import com.chan.web.handler.HandlerManage;
import com.chan.web.server.TomcatServer;
import org.apache.catalina.LifecycleException; import java.io.IOException;
import java.util.List; /**
* @description: 项目的入口
* @author: Chen
* @create: 2019-06-23 14:22
**/
public class MiniApplication { public static void run(Class<?> clz,String[] agrs){
try {
//根据传进来的clz所在的包取扫描包下的数据
List<Class<?>> classList = ClassScanner.scanClasses(clz.getPackage().getName());
try {
//根据扫描到到文件,初始化bean工厂。
BeanFactory.init(classList);
//根据@Controller @RequestMapping 注解,处理映射关系。
HandlerManage.resolveMappingHandleList(classList);
} catch (Exception e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
//启动tomcat
TomcatServer tomcatServer = new TomcatServer(agrs);
tomcatServer.startServer();
} catch (LifecycleException e) {
e.printStackTrace();
}
} }

(2)功能一  扫描包下的文件。

package com.chan.core;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile; /**
* @description:类扫描器
* @author: Chen
* @create: 2019-07-02 22:29
**/
public class ClassScanner { /**
* 扫描包下的文件并返回。
* 如果是jar包,则取jar的文件。如果是文件夹,这递归取
* @param packegeName
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static List<Class<?>> scanClasses(String packegeName) throws IOException, ClassNotFoundException { List<Class<?>> classList = new ArrayList<>();
String path = packegeName.replace(".","/");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources(path);
while (resources.hasMoreElements()){ URL resource = resources.nextElement();
//如果资源是jar包,那么遍历jar里面到文件
if (resource.getProtocol().contains("jar")){
JarURLConnection jarURLConnection = (JarURLConnection) resource.openConnection();
String jarFilePath = jarURLConnection.getJarFile().getName();
classList.addAll(getClassesFromJar(path,jarFilePath));
}
//是文件的话,递归取的文件
else if (resource.getProtocol().contains("file")){
File dir = new File(resource.getFile());
for (File file:dir.listFiles()){
if (file.isDirectory()){
classList.addAll(scanClasses(packegeName + "." + file.getName()));
}else {
String className =packegeName +"." +file.getName().replace(".class", "");
classList.add(Class.forName(className));
}
} } }
return classList; } private static List<Class<?>> getClassesFromJar(String path, String jarFilePath) throws IOException, ClassNotFoundException { List<Class<?>> classList = new ArrayList<>();
JarFile jarFile = new JarFile(jarFilePath);
Enumeration<JarEntry> jarEntrys = jarFile.entries();
while (jarEntrys.hasMoreElements()){
JarEntry jarEntry = jarEntrys.nextElement();
String name = jarEntry.getName();
if (name.startsWith(path)&&name.endsWith(".class")){
String classFullName = name.replace("/", ".").substring(0, name.length() - 6);
classList.add(Class.forName(classFullName));
}
} return classList; } }

(3) 根据扫描到到文件,初始化bean工厂。

package com.chan.beans;

import com.chan.web.mvc.Controller;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
* @description: bean 工厂
* @author: Chen
* @create: 2019-07-03 00:03
**/
public class BeanFactory { /**
* 配置bean容器 存放bean
*/
private static Map<Class<?>,Object> beanMap = new ConcurrentHashMap<>(); /**
* 根据类 获取bean
* @param clz
* @return
*/
public static Object getBean(Class<?> clz){return beanMap.get(clz);} /**
* 根据扫描到到类 依次按照规则注入到bean容器中
* @param classList
* @throws Exception
*/
public static void init(List<Class<?>> classList) throws Exception { List<Class<?>> toCreate = new ArrayList<>(classList); /**
* 将满足注入条件循环注入bean容器
*/
while (toCreate.size()!=0){ int remainSize = toCreate.size(); for (int i=0;i<toCreate.size();i++){
if (finishCreate(toCreate.get(i))){
toCreate.remove(i);
}
} if (remainSize==toCreate.size()){
throw new Exception("cycle dependency");
} } } /**
* 判断是否是需要注入到bean
* 如果不是 直接返回true 并且添加到bean容器
* 如果是,但是当时不满足注入条件 返回false 等到下次循环再调用
* 直至满足条件,添加到bean容器中
* @param clz
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
private static boolean finishCreate(Class<?> clz) throws IllegalAccessException, InstantiationException { if (!clz.isAnnotationPresent(Controller.class)&&!clz.isAnnotationPresent(Bean.class)){
return true;
} Object bean = clz.newInstance();
for (Field field : clz.getDeclaredFields()){
if (field.isAnnotationPresent(AutoWired.class)){
Class<?> fieldType = field.getType();
Object filedTypeObj = BeanFactory.getBean(fieldType);
if (filedTypeObj==null){
return false;
} field.setAccessible(true);
field.set(bean,filedTypeObj);
}
} beanMap.put(clz,bean);
return true;
} }

(4)根据@Controller @RequestMapping 注解,处理映射关系。

package com.chan.web.handler;

import com.chan.web.mvc.Controller;
import com.chan.web.mvc.RequestMapping;
import com.chan.web.mvc.RequestParam;
import com.sun.glass.events.mac.NpapiEvent; import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List; /**
* @description: 映射处理管理中心
* @author: Chen
* @create: 2019-07-03 00:34
**/
public class HandlerManage { /**
* 保存需要映射的列表
*/
public static List<MappingHandle> mappingHandleList = new ArrayList<>(); public static void resolveMappingHandleList(List<Class<?>> classList){ for (Class<?> clz :classList){ if (clz.isAnnotationPresent(Controller.class)){
parseHandlerFromController(clz);
} } } private static void parseHandlerFromController(Class<?> clz) { Method[] methods = clz.getMethods(); for (Method method:methods){ if (!method.isAnnotationPresent(RequestMapping.class)){
continue;
} String uri = method.getDeclaredAnnotation(RequestMapping.class).value();
List<String> paramNameList = new ArrayList<>(); for (Parameter parameter:method.getParameters()){
if (parameter.isAnnotationPresent(RequestParam.class)){
paramNameList.add(parameter.getDeclaredAnnotation(RequestParam.class).value());
}
}
String[] args = paramNameList.toArray(new String[paramNameList.size()]);
MappingHandle mappingHandle = new MappingHandle(uri,method,clz,args);
HandlerManage.mappingHandleList.add(mappingHandle);
} } }

(5)配置DispatcherServlet启动tomcat。

package com.chan.web.server;

import com.chan.util.YmlUtil;
import com.chan.web.servlet.DispatcherServlet;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat; import javax.servlet.Servlet; /**
* @description:根据 tomcat 的包进行处理
* @author: Chen
* @create: 2019-06-23 14:58
**/
public class TomcatServer { private Tomcat tomcat;
private String[] args; public TomcatServer(String[] args){
this.args = args;
} public void startServer() throws LifecycleException {
tomcat = new Tomcat();
tomcat.setPort(YmlUtil.get("server.port"));
tomcat.start(); Context context = new StandardContext();
context.setPath("");
context.addLifecycleListener(new Tomcat.FixContextListener()); DispatcherServlet servlet = new DispatcherServlet();
Tomcat.addServlet(context, "dispatcherServlet", servlet).setAsyncSupported(true);
context.addServletMappingDecoded("/", "dispatcherServlet");
tomcat.getHost().addChild(context); Thread awaitThread = new Thread("tomcar-await-thread"){
@Override
public void run() {
TomcatServer.this.tomcat.getServer().await();
}
};
awaitThread.setDaemon(false);
awaitThread.start();
} }
package com.chan.web.servlet;

import com.chan.web.handler.HandlerManage;
import com.chan.web.handler.MappingHandle; import javax.servlet.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException; /**
* @description: serlet适配器,所有的url都会进入到此处,
* 在此处根据@RequestMaping所映射到方法进行操作。
* @author: Chen
* @create: 2019-06-23 15:15
**/
public class DispatcherServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException { } @Override
public ServletConfig getServletConfig() {
return null;
} @Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { System.out.println("HandlerManage.mappingHandleList:"+HandlerManage.mappingHandleList.size()); for (MappingHandle mappingHandle : HandlerManage.mappingHandleList){
try {
if (mappingHandle.handle(req,res)){
return;
}else {
System.out.println("false");
}
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} } @Override
public String getServletInfo() {
return null;
} @Override
public void destroy() { }
}
package com.chan.web.handler;

import com.chan.beans.BeanFactory;
import org.apache.catalina.servlet4preview.http.HttpServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; /**
* @description: 映射处理类
* @author: Chen
* @create: 2019-07-03 00:35
**/
public class MappingHandle { String uri; Method method; Class<?> controller; String[] agrs; public MappingHandle(String uri,Method method,Class<?> controller,String[] agrs){
this.uri = uri;
this.method = method;
this.controller = controller;
this.agrs = agrs;
} /**
* 将扫描到到RequestMapping的路径 进行处理
* 通过反射调用 调用该方法
* @param req
* @param res
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws IOException
*/
public boolean handle(ServletRequest req, ServletResponse res) throws InvocationTargetException, IllegalAccessException, IOException { String requestURI = ((HttpServletRequest)req).getRequestURI(); System.out.println("uri:"+uri);
System.out.println("requestURI:"+requestURI.substring(1));
if (!uri.equals(requestURI.substring(1))){
return false;
} String[] params = new String[agrs.length];
for (int i =0;i< params.length;i++){
params[i] = req.getParameter(agrs[i]);
} Object obj = BeanFactory.getBean(controller);
System.out.println(obj);
if (obj==null){
return false;
} Object ret = method.invoke(obj,params);
System.out.println("ret:"+ret.toString());
res.getWriter().println(ret.toString());
return true;
} }

3.测试framework模块。

(1) application.yml 配置项目启动的端口号。

(2)Application项目入口。

(3)controller 控制层

(4)service 业务层,在这里注入类bean。

这里和springboot类似,这只是简单Spirng框架的大概思路流程。

在尾巴处再次贴上项目手写简单的SpringMvc框架。

最新文章

  1. Caffe Python MemoryDataLayer Segmentation Fault
  2. python 使用总结
  3. HTML标记语法之图片Img元素
  4. js 判断数据类型的方法及实现
  5. netmon,messageanalyzer
  6. 如何选择分类器?LR、SVM、Ensemble、Deep learning
  7. ASP.NET 导入excel 数据
  8. 视频和音频播放的演示最简单的例子6:OpenGL广播YUV420P(T经exture,采用Shader)
  9. chapter8_4 错误处理
  10. 安卓组件service
  11. .htaccess伪静态(URL重写)绑定域名到子目录实现子站点
  12. ajax提交到后台是中文乱码
  13. Gradle入门--基本配置
  14. webservice服务的提供及调用完整代码示例
  15. Ubuntu 16.04设置开机启动脚本的方法
  16. spring 之 BeanPostProcessor
  17. 白鹭引擎 - 事件机制 ( Event, addEventListener, dispatchEvent )
  18. [svc]NFS存储企业场景及nfs最佳实战探究
  19. css 文本和div垂直居中方法汇总
  20. c++ list erase()

热门文章

  1. Vue 页面 前进刷新 后退不刷新(keepAlive)
  2. Qt实现多国语言(即界面翻译)可实时进行切换
  3. vue等诸多概念记录
  4. python爬虫添加请求头
  5. Flex 布局是什么?
  6. BZOJ 4269: 再见Xor 线性基+贪心
  7. BZOJ 1778: [Usaco2010 Hol]Dotp 驱逐猪猡 概率与期望+高斯消元
  8. codeforces400C
  9. puppet自动化部署
  10. (转载)深入理解Java:内省(Introspector)