纯手写Springmvc框架 注释详解篇
程序员文章站
2022-07-09 23:26:17
项目场景:DispatcherServlet继承HttpServlet实现纯手写springmvc框架前言:提示:大神请绕道,此文章分享于一起共同奋斗的我们理解springmvc框架 在此感谢尚学堂雷老师!!! 进入主题:创建注解: Controller,RequestMapping,RequestParampackage com.yuanjw.annotation;import java.lang.annotation.*;/** * @Target(ElementType....
项目场景:
DispatcherServlet继承HttpServlet实现纯手写springmvc框架
前言:
提示:大神请绕道,此文章分享于一起共同奋斗的我们理解springmvc框架 在此感谢尚学堂雷老师!!!
进入主题:
创建注解: Controller,RequestMapping,RequestParam
package com.yuanjw.annotation;
import java.lang.annotation.*;
/**
* @Target(ElementType.TYPE):元注解(类上起作用)
* @Retention(RetentionPolicy.RUNTIME):元注解(运行时有效)
* @Documented被Javadoc记录
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
/**
* 使用注解之后里面可以传一个值 @Conntroller
* @return
*/
String value() default "";
}
package com.yuanjw.annotation;
import java.lang.annotation.*;
/**
* @Target({ElementType.TYPE,ElementType.METHOD})作用在类上 方法上
* @Retention(RetentionPolicy.RUNTIME) 运行时有效
* @Documented doc记录
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
/**
* @RequestMapping("addUser")
*/
String value() default "";
}
package com.yuanjw.annotation;
import java.lang.annotation.*;
/**
* @Target(ElementType.PARAMETER)作用于参数
* @Retention(RetentionPolicy.RUNTIME)运行时有效
* @Documented 文档记录
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
/**
* 表示参数的别名 必填
*
* @return
*/
String value();
}
创建前端控制器:DispatcherServlet
package com.yuanjw.core;
import com.yuanjw.annotation.Controller;
import com.yuanjw.annotation.RequestMapping;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
/**
* 前端控制器
*/
public class DispatcherServlet extends HttpServlet {
//存放配置文件
private Properties properties=new Properties();
//存放类的完全限定名
private List<String> classNames = new ArrayList<>();
//创建一个ioc容器
private Map<String,Object> ioc = new HashMap<>();
//控制器映射器
private Map<String,Method> handlerMapping = new HashMap<>();
//控制器对象集合
private Map<String,Object> controllerMap = new HashMap<>();
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
//1.加载application.properties
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2.初始化所有相关的类 扫描用户设定的包下面的所有类
doScanner(properties.getProperty("basePackageScan"));
//3.拿到扫描类利用反射机制实例化 创建对象 并放到ioc容器中 beanName默认是首字母小写
doInstance();
//4.初始化Urk和method(HandlerMapping)
initHandlerMapping();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理请求
Object doDispatch = doDispatch(req,resp);
//此刻处理请求转发
if(null!=doDispatch){
String path ="/"+doDispatch.toString();
req.getRequestDispatcher(path).forward(req,resp);
}
}
/**
* 请求分发
* @param req
* @param resp
* @return
*/
private Object doDispatch(HttpServletRequest req, HttpServletResponse resp) {
if(handlerMapping.isEmpty()) {
throw new RuntimeException("url not found");
}
//得到请求Url
String uri = req.getRequestURI();
//得到发布项目的路径
String contextPath = req.getContextPath();
uri=uri.replace(contextPath+"/","");
uri = uri.substring(0,uri.lastIndexOf("."));
//根据Uri从handlerMapping取方法
Method method = handlerMapping.get(uri);
//获取方法的所有参数列表
Class<?>[] parameterTypes = method.getParameterTypes();
//获取请求的参数
Map<String,String[]> parameterMap=req.getParameterMap();
//保存参数值
Object[] paramValues = new Object[parameterTypes.length];
//方法参数列表
for (int i = 0; i < parameterTypes.length; i++) {
//根据参数名称,做某些处理
String simpleName = parameterTypes[i].getSimpleName();
//类型已明确 这边强制转换类型
if(simpleName.equals("HttpServletRequest")){
paramValues[i]=req;
continue;
}
if(simpleName.equals("HttpServletResponse")){
paramValues[i]=resp;
continue;
}
if(simpleName.equals("String")){
for (Map.Entry<String,String[]> param: parameterMap.entrySet()) {
String value =Arrays.toString(param.getValue()).replaceAll("\\[|\\]","");
paramValues[i]=value;
}
}
}
//利用反射机制来调用
Object obj = null;
try {
obj= method.invoke(this.controllerMap.get(uri),paramValues);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return obj;
}
/**
* 初始化Urk和method(HandlerMapping)
*/
private void initHandlerMapping() {
//从ioc容器里面取创建好的对象,循环判断对象里面的类的和方法上面是否添加了注解
if(ioc.isEmpty())return;
for (Object o: ioc.values()) {
Class<?> aClass = o.getClass();
String baseUrl = "";
//判断这个类里是否有@RequestMapping注解
if(aClass.isAnnotationPresent(RequestMapping.class)){
//得到这个注解对象,并取出里面的值
RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);
baseUrl = annotation.value();
}else{
continue;
}
//解析对象里面的方法
Method[] methods = aClass.getMethods();
if(methods!=null&&methods.length!=0){
for (Method method:methods){
//判断方法有没有注解
if (method.isAnnotationPresent(RequestMapping.class)){
RequestMapping annotation = method.getAnnotation(RequestMapping.class);
String url = annotation.value();
url=baseUrl+"/"+url;
handlerMapping.put(url,method);
try {
controllerMap.put(url,aClass.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* 拿到扫描类利用反射机制实例化 创建对象 并放到ioc容器中 beanName默认是首字母小写
*/
private void doInstance() {
//判断classNames不为空
if(classNames.isEmpty())return;
for (String className:classNames ) {
Class<?> clazz = null;
try {
clazz = Class.forName(className);
//此处判断是否有注解
if(!clazz.isAnnotationPresent(Controller.class)){
continue;
}else{
Object instance = clazz.newInstance();
String key =firstbeanNameToLowerCase(instance.getClass().getSimpleName());
//放到ioc容器
ioc.put(key,instance);
}
} catch (Exception e) {
e.printStackTrace();
//如果一个类出现异常要保证其他类也可以解析
continue;
}
}
}
/**
* 初始化所有相关联的类
* @param basePackageScan com.yuanjw.controller
* com.yuanjw.controller要通过File找 需要替换为 com/yuanjw/controller
*/
private void doScanner(String basePackageScan) {
String packagePath=basePackageScan.replace(".","/");
URL url = this.getClass().getResource("/"+packagePath);
//得到类文件夹的路径
String path= url.getFile();
//构造File文件夹
File file = new File(path);
File[] files = file.listFiles();
if(files!=null&&files.length>0){
for (File f:files){
if (f.isDirectory()){
doScanner(basePackageScan+"."+f.getName());
}else{
//UserController.class className=com.yuanjw.controller.UserController
String className = basePackageScan+"."+f.getName().replace(".class","");
classNames.add(className);
}
}
}
}
/**
* 加载application.properties并解析
* @param contextConfigLocation
*/
private void doLoadConfig(String contextConfigLocation) {
String path = contextConfigLocation.substring(10, contextConfigLocation.length());
InputStream is = this.getClass().getResourceAsStream("/"+path);
//读取到properties中
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 把字符串的首字母小写
*/
private String firstbeanNameToLowerCase(String beanName){
char[] charArray = beanName.toCharArray();
charArray[0] +=32;
return String.valueOf(charArray);
}
}
web.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!--配置前端控制器-->
<servlet>
<servlet-name>mySpringMvc</servlet-name>
<servlet-class>com.yuanjw.core.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mySpringMvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
配置文件(properties):
#类似于springmvc.xml
basePackageScan=com.yuanjw.controller
测试:创建controller , jsp
package com.yuanjw.controller;
import com.yuanjw.annotation.Controller;
import com.yuanjw.annotation.RequestMapping;
import com.yuanjw.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
@RequestMapping("user")
public class UserController {
@RequestMapping("addUser")
public String addUser(HttpServletRequest request, HttpServletResponse response){
System.out.println("addUser");
return "adduser.jsp";
}
@RequestMapping("updateUser")
public String updateUser(HttpServletRequest request, HttpServletResponse response, @RequestParam("name")String name){
System.out.println("addUser");
return "adduser.jsp";
}
}
<%--
Created by IntelliJ IDEA.
User: yuanjw1
Date: 2020/12/28
Time: 21:24
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
success
</body>
</html>
结构:
感谢查阅!
本文地址:https://blog.csdn.net/yuanjiangwei255/article/details/111875461