package com.mvc.myServlet;
import com.mvc.annotation.MyAutowired;
import com.mvc.annotation.MyController;
import com.mvc.annotation.MyRequertMapping;
import com.mvc.annotation.MyService;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;
/**
-
@ Author :gtj
-
@ Date :Created in 12:58 2019/2/2
-
@ Description:自己写一个SpringMVc核心代码
*/
public class MyDispathcherServlet extends HttpServlet {//配置文件属性
private Properties contextConfig=new Properties();//扫描类的路径
List classNames=new ArrayList();//创建一个map作为IOC容器
private Map<String ,Object> ioc=new HashMap<String ,Object>();//声明handlerMapping映射容器
private Map<String ,Object> handlerMapping=new HashMap<String ,Object>();@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//7.调用方法
doDispatch(req,resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write(“500 Exception”+Arrays.toString(e.getStackTrace()));
}
}/**
-
通过反射传参到controller 完成业务调用
-
@param req HttpServletRequest对象
-
@param resp HttpServletResponse对象
-
@throws Exception
*/
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {if (this.handlerMapping.isEmpty()){return;}
//把URL改为绝对路径 即去除前面的路径
String url=req.getRequestURI();
String contextPath = req.getContextPath();
url=url.replace(contextPath,"").replaceAll("/+","/");//如果不存在对应的路径,直接返回404
if (!this.handlerMapping.containsKey(url)){
resp.getWriter().write(“404 Not Found!!!”);
return;
}//根据url找到对应的方法
Method method = (Method) this.handlerMapping.get(url);//定义values 为数组可以后续添加一些数组类型的参数
Map<String,String[]> params = req.getParameterMap();String beanName=lowerFistrCase(method.getDeclaringClass().getSimpleName());
//调用方法的参数列表
Parameter[] parameters = method.getParameters();//用户给method反射调用方法的参数数组
Object[] paramValues=new Object[parameters.length];//给paramValues进行赋值
for (int i = 0; i <parameters.length ; i++) {//如果参数类型为HttpServletRequest或者HttpServletResponse进行自动注入 后续可以添加更多的参数如Model(可以提取出来) if (parameters[i].getParameterizedType().getTypeName().endsWith("HttpServletRequest")){ paramValues[i]=req; continue; } if (parameters[i].getParameterizedType().getTypeName().endsWith("HttpServletResponse")){ paramValues[i]=resp; continue; } //给前端传递的参数赋值到paramValues数组中 for (Map.Entry<String, String[]> entry : params.entrySet()) { if(parameters[i].getName().equals(entry.getKey())){ paramValues[i]=entry.getValue()[0].trim(); } }
}
//反射调用对用的业务方法
method.invoke(ioc.get(beanName),paramValues);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}@Override
public void init(ServletConfig config) throws ServletException {//1.加载配置文件 doLoadConfig(config.getInitParameter("contextConfigLocation")); //2.解析配置文件,扫描类 doScanner(contextConfig.getProperty("scanPackage")); //3.初始化所有的类,保存到IOC容器中 doInstance(); //4.完成依赖注入 DI doAutoWired(); //5.创建HandlerMapping与url和method建立联系 initHandlerMapping(); System.out.println("My SpringMVC is init complete");
}
/**
-
@param contextConfigLocation 配置文件的参数 在web.xml配置
*/
private void doLoadConfig(String contextConfigLocation) {//从类路径下取得文件路径转化为文件流
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);try {
contextConfig.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭流 if (null!=inputStream){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } }
}
}
/**
-
//2.解析配置文件,扫描类
-
@param scanPackage 配置文件的值(简化版)
*/
private void doScanner(String scanPackage) {//获取扫描包的url路径
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\.", “/”));
File classDir = new File(url.getFile());
for (File file : classDir.listFiles()) {
//递归扫描 找到对应的类
if (file.isDirectory()){doScanner(scanPackage+"."+file.getName()); }else { //如果后缀为.class 则把路径存入classNames中 if (!file.getName().endsWith(".class")){continue;} String className=(scanPackage+"."+file.getName().replace(".class","")).trim(); classNames.add(className); }
}
}
/**
-
//3.初始化所有相关的类,保存到IOC容器中
*/
private void doInstance() {//如果扫描类为空直接结束
if (classNames.isEmpty()){return; }try {
for (String className : classNames) {
Class<?> clazz = Class.forName(className);//带注解的类才被实例化 if (clazz.isAnnotationPresent(MyController.class)){ //获取类名并且把首字母转为小写作为key String key=lowerFistrCase( clazz.getSimpleName()); ioc.put(key,clazz.newInstance()); }else if(clazz.isAnnotationPresent(MyService.class)){ //类名首字母小写 自定义命名优先 MyService service = clazz.getAnnotation(MyService.class); //得到自定义命名 String beanName = service.value(); //如果没有自定义命名则使用默认命名(类名首字母小写) if ("".equals(beanName.trim())) { beanName=lowerFistrCase(clazz.getSimpleName()); } Object instance = clazz.newInstance(); //存入IOC容器中 ioc.put(beanName,instance); //用接口的全称名做为key,这里简化一点,只能有一个实现类 后续可以再完善多个类的 Class<?>[] interfaces = clazz.getInterfaces(); for (Class<?> i : interfaces) { if (ioc.containsKey(i.getName())){ throw new Exception("The beanName is exists"); } ioc.put(i.getName(),instance); } }else { continue; } }
}catch (Exception e){
}
}
/**
-
//4.完成依赖注入 DI
*/
private void doAutoWired() {//如果IOC容器为空 结束
if (ioc.isEmpty()){return;}for (Map.Entry<String, Object> entry : ioc.entrySet()) {
//获取IOC容器类中的字段 Field[] fields = entry.getValue().getClass().getDeclaredFields(); for (Field field : fields) { //进行注入,这里就只考虑Antowired注入 if (!field.isAnnotationPresent(MyAutowired.class)){continue;} //得到注解的实例 MyAutowired autowired = field.getAnnotation(MyAutowired.class); //给到Autowired的属性值 String beanName = autowired.value(); if ("".equals(beanName)){ beanName=field.getType().getName(); } //强制授权,否则不能注入到私有的属性 field.setAccessible(true); try { //把IOC容器的实例化对象 放入属性中 field.set(entry.getValue(),ioc.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } }
}
}
/**
-
//5.创建HandlerMapping与url和method建立联系
*/
private void initHandlerMapping() {//如果IOC为空,结束
if (ioc.isEmpty()){return; }for (Map.Entry<String,Object> entry : ioc.entrySet()) {
//得到IOC容器的实例化类 Class<?> clazz = entry.getValue().getClass(); //只有controller的类才能与前端进行映射 if (!clazz.isAnnotationPresent(MyController.class)){continue;} //得到MyRequertMapping的地址 String baseUrl= ""; if (clazz.isAnnotationPresent(MyRequertMapping.class)){ MyRequertMapping requertMapping = clazz.getAnnotation(MyRequertMapping.class); baseUrl=requertMapping.value(); } //获取所有的公共方法(私有方法不考虑) Method[] methods = clazz.getMethods(); for (Method method : methods) { //如果没有路径,则跳过 if (!method.isAnnotationPresent(MyRequertMapping.class)){continue;} //得到对应的MyRequertMapping对象 MyRequertMapping requertMapping = method.getAnnotation(MyRequertMapping.class); //组装访问地址的绝对路径 String url=("/"+baseUrl+"/"+requertMapping.value()).replaceAll("/+","/"); //存入handlerMapping handlerMapping.put(url,method); System.out.println("Mapping: " +url+","+method); }
}
}
/**
*把类名的首字母改为小写- @param simpleName 类名
- @return 返回类名首字母小写
*/
private String lowerFistrCase(String simpleName) {
char[] chars=simpleName.toCharArray();
chars[0]+=32;
return String.valueOf(chars);
}
-
}