介绍
github代码:https://github.com/erlieStar/servlet-learning
v3分支
用的是servlet 3.0所以就不用web.xml了,全程注解
定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
String value() default "";
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value() default "";
}
定义中央控制器
基本上所有的逻辑都在这个类中,主要流程如下
- 创建DispatcherServlet的时候,tomcat会调用init()方法,在里面初始化url和对应的处理方法的映射关系
- 当有请求来的时候,从uriInvokeInfoMap中拿对应的方法,如果有对应的方法,反射调用方法,拿到页面名字,拼接页面地址,转发到相应的页面,否则返回404
@WebServlet(urlPatterns="/", loadOnStartup = 1)
public class DispatcherServlet extends HttpServlet {
// 保存所有的handler
private List<Object> beanList = new ArrayList<>();
// 保存 uri 和 handler 的映射关系
private Map<String, InvokeInfo> uriInvokeInfoMap = new HashMap<>();
private static final String SLASH = "/";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getRequestURI();
String contextPath = req.getContextPath();
// 去掉项目路径
uri = uri.replace(contextPath, "");
System.out.println(uri);
if (uri == null) {
return;
}
InvokeInfo invokeInfo = uriInvokeInfoMap.get(uri);
if (invokeInfo == null) {
resp.getWriter().write("404");
return;
}
String pageName = (String)methodInvoke(invokeInfo.getBean(), invokeInfo.getMethod());
viewResolver(pageName, req, resp);
}
// 视图解析器
public void viewResolver(String pageName, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String prefix = "/";
String suffix = ".jsp";
req.getRequestDispatcher(prefix + pageName + suffix).forward(req, resp);
}
// 反射执行方法
private Object methodInvoke(Object object, Method method) {
try {
return method.invoke(object);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public void init() throws ServletException {
// 获取指定包下的Class对象
List<Class<?>> classList = ClassUtil.getAllClassByPackageName("com.javashitang.controller");
// 找到所有标注了@Controller的类
findAllConrollerClass(classList);
// 初始化 uri 和 handler 的映射关系
handlerMapping();
}
public void findAllConrollerClass(List<Class<?>> list) {
list.forEach(bean -> {
// 将被@Controller注解修饰的类放到beanList
if (bean.isAnnotationPresent(Controller.class)) {
try {
beanList.add(bean.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
// 根据url找到相应的处理类
public void handlerMapping() {
for (Object bean : beanList) {
Class<? extends Object> classInfo = bean.getClass();
// 获取类上的@RequestMapping信息
RequestMapping beanRequestMapping = classInfo.getDeclaredAnnotation(RequestMapping.class);
String baseUrl = beanRequestMapping != null ? beanRequestMapping.value() : "";
Method[] methods = classInfo.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
// 获取方法上的@RequestMapping信息
RequestMapping methodRequestMapping = method.getDeclaredAnnotation(RequestMapping.class);
if (methodRequestMapping != null) {
String requestUrl = SLASH + baseUrl + SLASH + methodRequestMapping.value();
// 为了处理@Controller和@RequestMapping value 中加了 / 前缀的情况
requestUrl = requestUrl.replaceAll("/+", "/");
InvokeInfo invokeInfo = new InvokeInfo(bean, method);
uriInvokeInfoMap.put(requestUrl, invokeInfo);
}
}
}
}
}
将方法及其对应的类做了一下封装
public class InvokeInfo {
private Object bean;
private Method method;
public InvokeInfo(Object bean, Method method) {
this.bean = bean;
this.method = method;
}
}
开始测试
@Controller
@RequestMapping("index")
public class IndexController {
@RequestMapping("user")
public String user() {
return "user";
}
}
访问如下连接,页面正常显示
http://localhost:8080/show/index/user
欢迎关注
参考博客
[1]https://my.oschina.net/liughDevelop/blog/1622646
[2]https://www.malaoshi.top/show_1EFutw6imbI.html