package cn.jesseyang.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JesseController {
String value() default "";
}
package cn.jesseyang.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JesseMapping {
String value() default "";
}
package cn.jesseyang.annotation;
import java.lang.annotation.*;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JesseParam {
String value() default "";
}
package cn.jesseyang.servlet;
import cn.jesseyang.annotation.JesseController;
import cn.jesseyang.annotation.JesseMapping;
import cn.jesseyang.annotation.JesseParam;
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.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class MyServlet extends HttpServlet {
private static final String packageNames="cn.jesseyang.controller";
// 根据类名找controller
private Map iocMap = new HashMap<>(64);
//根据url找处理的方法
private Map handlerMapping = new HashMap<>(64);
//根据url找到处理的类 反射调用方法的时候需要用到
private Map controllerMap = new HashMap<>(64);
@Override
public void init() throws ServletException {
try {
//扫描所有的包,将带有controller注解的放到ioc容器里待用
scanPackages();
//把ioc容器里的controller类遍历找到JesseMapping注解的方法,拼url,初始化handlerMapping和controllerMap
//这一步就可以把访问的路径和处理方法对应起来
dealHandlerMapping();
}catch (Exception e){
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPut(req,resp);
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
dispatch(req,resp);
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private void dispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
String url =req.getRequestURI();
String contextPath = req.getContextPath();
url=url.replace(contextPath, "").replaceAll("/+", "/");
if(!handlerMapping.containsKey(url)){
resp.getWriter().write("404 !!! 服务器没有这个页面哦");
return;
}
Method method = handlerMapping.get(url);
//方法的参数类型
Class[] parameterTypes = method.getParameterTypes();
Parameter[] parameters = method.getParameters();
//从请求中获取参数列表 key是参数名,value是对象
Map parameterMap = req.getParameterMap();
//方法反射调用需要的参数 Object[] 里面放从请求过来的参数
Object[] methodParameter = new Object[parameterTypes.length];
for (int i=0;i0){
//简单的将数组转换为String然后加到反射用的方法参数数组里
methodParameter[i]= Arrays.toString(requestParam).replaceAll("[\\[\\]]","")
.replaceAll(",","");
}
}
}
method.invoke(controllerMap.get(url),methodParameter);
}
private void dealHandlerMapping() throws IllegalAccessException, InstantiationException {
if(iocMap.isEmpty())return;
for (Map.Entry entry :iocMap.entrySet()){
Class controllerClazz = entry.getValue();
//接下来拼url
String requestUrl = "";
if (controllerClazz.isAnnotationPresent(JesseMapping.class)){
JesseMapping jesseMapping = (JesseMapping) controllerClazz.getAnnotation(JesseMapping.class);
requestUrl = jesseMapping.value();
}
Method[] controllerMethods = controllerClazz.getMethods();
for (Method method : controllerMethods){
if (method.isAnnotationPresent(JesseMapping.class)){
requestUrl += method.getAnnotation(JesseMapping.class).value();
handlerMapping.put(requestUrl,method);
controllerMap.put(requestUrl,controllerClazz.newInstance());
}
}
}
}
private void scanPackages() throws IllegalAccessException, ClassNotFoundException, InstantiationException {
String packagePath = this.getClass().getResource("/"+packageNames.replaceAll("\\.","/")).getPath();
System.out.println("packagePath"+packagePath);
findClassWithAnnotation(packagePath);
}
private void findClassWithAnnotation(String filePath) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
File file = new File(filePath);
File[] files = file.listFiles();
for(File f:files){
if (f.isDirectory()){
findClassWithAnnotation(f.getPath());
}else{
//通过反射实例化类,放到map里面,key是类名
System.out.println("fileName"+f.getPath());
putClass2IOCMap(f);
}
}
}
private void putClass2IOCMap(File f) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class clazz = Class.forName(transferPath2PackagePath(f));
if (clazz!=null && clazz.isAnnotationPresent(JesseController.class)){
System.out.println("clazz.getSimpleName():"+clazz.getSimpleName());
iocMap.put(clazz.getSimpleName(),clazz);
}
}
private String transferPath2PackagePath(File f ){
return packageNames+"."+f.getName().replace(".class","");
}
}
MyMVC
MyMVC
cn.jesseyang.servlet.MyServlet
1
TestServlet
TestServlet
cn.jesseyang.servlet.TestServlet
MyMVC
/*
TestServlet
/TestServlet
package cn.jesseyang.controller;
import cn.jesseyang.annotation.JesseController;
import cn.jesseyang.annotation.JesseMapping;
import cn.jesseyang.annotation.JesseParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@JesseController
@JesseMapping("/jesse")
public class TestController {
@JesseMapping("/login")
public void index(
HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
@JesseParam("user") String user, @JesseParam("password")String password ){
try {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("user:");
stringBuilder.append(user);
stringBuilder.append("
");
stringBuilder.append("password:");
stringBuilder.append(password);
httpServletResponse.getWriter().write(stringBuilder.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
首先理一下思路,特定路径的的HTTP请求交给Servlet去处理(这里拦截所有的请求),Servlet里重写doGet doPost方法根据url去分发给特定的方法去处理。这里采用注解的方式标识类和方法,让servlet能找到。那么类和方法需要提前实例化好放入容器里面以便调用。
确定一下需要几个注解。标识类是处理分发的Controller的注解 JesseController。这里加一个我的名字和Spring的区分开。servlet根据url分发处理,需要一个JesseMapping来标识类和方法是处理哪一个url请求的(Spring的RequestMapping)。以及标识参数名的JesseParam。这里的这个Param注解原本不想要,因为是极简版。但是写的过程中发现,方法的参数名没有办法通过反射获取。虽然Java8改JVM启动参数的方式能获取到。于是加了个Param注解,区分一下1个以上的请求参数。
接收到HTTP请求后,servlet根据url找到方法,需要一个 map,key是url,value是方法。方法调用需要指定类,那么需要一个map,key是url,value是方法所在的类对象。启动时扫描指定的包,获取所有jesseController注解的类,需要一个iocMap去装所有的controller。
如果一个url没有方法去处理,那么返回404提示。