首页 技术 正文
技术 2022年11月9日
0 收藏 663 点赞 4,809 浏览 11419 个字

先来看我们的程序入口DispatcherServlet

主要核心处理流程如下:

1 扫描基础包下的带有controller 以及 service注解的class,并保存到list中

2 对第一步扫描到的class进行实例化操作

3 对第一步扫描到的class中带有AutoWired注解的属性值进入自动注入

4 建立url和方法,方法对应的controller的映射关系(使用map)

当以上准备工作完成以后,

用户提交请求,服务器获取url 并在第4步建立的映射关系中查找

找到了,就执行相关方法,找不到 就报404错误

以下是核心代码

几个自定义注解

@Target({ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Autowired {

String value() default “”;

}

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Controller {

String value() default “”;

}

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface RequestMapping {

String value() default “”;

}

@Target({ElementType.PARAMETER})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface RequestParam {

String value() default “”;

boolean required() default true;

}

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Service {

String value() default “”;

}

================================================================================

帮助类

需要自行引入

Pom位置

<dependency><groupId>org.objectweb.asm</groupId><artifactId>org.objectweb.asm</artifactId><version>3.3</version></dependency>

  

public class AsmUtil {
/**
* 获取指定类指定方法的参数名
*
* @param clazz
* 要获取参数名的class
* @param method
* 要获取参数名的方法
* @return 按参数顺序排列的参数名列表,如果没有参数,则返回null
*/
public static String[] getMethodParameterNamesByAsm4(final Class clazz, final Method method) {
final String methodName = method.getName();
final Class<?>[] methodParameterTypes = method.getParameterTypes();
final int methodParameterCount = methodParameterTypes.length;
String className = method.getDeclaringClass().getName();
final boolean isStatic = Modifier.isStatic(method.getModifiers());
final String[] methodParametersNames = new String[methodParameterCount];
int lastDotIndex = className.lastIndexOf(".");
className = className.substring(lastDotIndex + 1) + ".class";
InputStream is = clazz.getResourceAsStream(className);
try {
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cr.accept(new ClassAdapter(cw) {
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);final Type[] argTypes = Type.getArgumentTypes(desc);
// 参数类型不一致
if (!methodName.equals(name) || !matchTypes(argTypes, methodParameterTypes)) {
return mv;
}
return new MethodAdapter(mv) {
public void visitLocalVariable(String name, String desc, String signature, Label start,
Label end, int index) {
// 如果是静态方法,第一个参数就是方法参数,非静态方法,则第一个参数是 this ,然后才是方法的参数
int methodParameterIndex = isStatic ? index : index - 1;
if (0 <= methodParameterIndex && methodParameterIndex < methodParameterCount) {
methodParametersNames[methodParameterIndex] = name;
}
super.visitLocalVariable(name, desc, signature, start, end, index);
}
};
}
}, 0);
} catch (Exception e) {
e.printStackTrace();
}
return methodParametersNames;
}/**
* 比较参数是否一致
*/
private static boolean matchTypes(Type[] types, Class<?>[] parameterTypes) {
if (types.length != parameterTypes.length) {
return false;
}
for (int i = 0; i < types.length; i++) {
if (!Type.getType(parameterTypes[i]).equals(types[i])) {
return false;
}
}
return true;
}
}

  

核心控制器

public class DispatcherServlet extends HttpServlet {/**
*
*/
private static final long serialVersionUID = -8630585768835454263L;
// 保存含有controller或者service注解的class名字
private List<String> classNames = new ArrayList<>();
// 保存含有实例化对象
private Map<String, Object> instancesMap = new HashMap<>();
// 保存url和url对应的方法以及controller的映射关系
private Map<String, UrlMethodMappingModel> handlerMapping = new HashMap<>();public void init(ServletConfig config) throws ServletException {// 1 扫描基础包下的所有controller 以及 service
scanBasePackage(config.getInitParameter("basePackage"));try {
// 2 实例化list中的对象 并且保存到map中
instanceClass();
// 3 注入使用了autowired的注解的属性
doAutoWired();
// 4建立 url和方法的映射
urlAndMethodMapping();} catch (Exception e) {
e.printStackTrace();
}
}private void urlAndMethodMapping() {
for (Map.Entry<String, Object> entry : instancesMap.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
// 只处理Controller的,只有Controller有RequestMapping
if (!clazz.isAnnotationPresent(Controller.class)) {
continue;
}
// 定义url
StringBuffer urlBuffer = new StringBuffer();urlBuffer.append("/");
if (clazz.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
urlBuffer.append(requestMapping.value());}// 获取方法上的RequestMapping
Method[] methods = clazz.getMethods();
// 只处理带RequestMapping的方法
for (Method method : methods) {
if (!method.isAnnotationPresent(RequestMapping.class)) {
break;
}
RequestMapping methodMapping = method.getAnnotation(RequestMapping.class);
// requestMapping.value()即是在requestMapping上注解的请求地址,不管用户写不写"/",我们都给他补上
String realUrl = urlBuffer.append("/" + methodMapping.value()).toString();
// 替换掉多余的"/",因为有的用户在RequestMapping上写"/xxx/xx",有的不写,所以我们处理掉多余的"/"
realUrl = realUrl.replaceAll("/+", "/");
// -------获取访问url完整路径end// ---获取方法的请求参数开始
// 获取所有的参数的注解,有几个参数就有几个annotation[],空的话也会有一个数组
// 是数组的原因是,一个参数可以有多个注解……
Annotation[][] annotations = method.getParameterAnnotations();
// 由于后面的Method的invoke时,需要传入所有参数的值的数组,所以需要保存各参数的位置
Map<String/* 参数名 如 name */, Integer/* 参数下标位置 */> paramMap = new HashMap<>();// 获取方法里的所有参数的参数名(注意:此处使用了ASM.jar
// 如TestController 的test(String name, HttpServletResponse
// reponse),将得到如下数组["name","reponse"]
String[] paramNames = AsmUtil.getMethodParameterNamesByAsm4(clazz, method);
// 获取所有参数的类型
Class<?>[] paramTypes = method.getParameterTypes();for (int i = 0; i < annotations.length; i++) {
Annotation[] annotationArray = annotations[i];
if (annotationArray.length == 0) // 就是当前参数没有使用注解 (String
// name )
{
Class<?> type = paramTypes[i];if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
paramMap.put(type.getName(), i);
} else { // 普通属性
paramMap.put(paramNames[i], i);
}
}
// 有注解,就遍历每个参数上的所有注解
for (Annotation annotation : annotationArray) {
if (annotation.annotationType() == RequestParam.class) {
String paramValue = ((RequestParam) annotation).value();
if (!"".equals(paramValue)) {
paramMap.put(paramValue, i);
break;
}}
}
}UrlMethodMappingModel model = new UrlMethodMappingModel(method, entry.getValue(), paramMap);
handlerMapping.put(realUrl, model);
}
}
}private void doAutoWired() {// 遍历instancesMap中被托管的对象
for (Map.Entry<String, Object> entry : instancesMap.entrySet()) {
// 查找所有被Autowired注解的属性
Field[] fieldArray = entry.getValue().getClass().getDeclaredFields();
for (Field field : fieldArray) {
// 没有使用Autowired注解
if (!field.isAnnotationPresent(Autowired.class)) {
continue;
}
Autowired autowired = field.getAnnotation(Autowired.class);
if ("".equals(autowired.value())) {
field.setAccessible(true);
String beanName = lowerFirstChar(field.getType().getSimpleName());
if (instancesMap.get(beanName) != null) {
try {
field.set(entry.getValue(), instancesMap.get(beanName));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}private void instanceClass() throws Exception {
if (classNames.size() == 0) {
return;
}
for (String className : classNames) {
try {
Class<?> clazz = Class.forName(className);
// 如果是controller 或者 service 直接使用类名
if (clazz.isAnnotationPresent(Controller.class)) {
instancesMap.put(lowerFirstChar(clazz.getSimpleName()), clazz.newInstance());} else if (clazz.isAnnotationPresent(Service.class)) {
// 获取注解上的名字 如果有的话
// 获取注解上的值
Service service = clazz.getAnnotation(Service.class);
String servicename = service.value();
if (!"".equals(servicename)) {
instancesMap.put(lowerFirstChar(clazz.getSimpleName()), clazz.newInstance());
} else {
Class<?>[] interfaces = clazz.getInterfaces();
// 这里简单处理 假定ServiceImpl只实现了一个接口
for (Class<?> class1 : interfaces) {
instancesMap.put(lowerFirstChar(class1.getSimpleName()), clazz.newInstance());
break;
}}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}}private void scanBasePackage(String basePackage) {
URL baseUrl = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));File baseFile = new File(baseUrl.getFile());
File[] files = baseFile.listFiles();
for (File file : files) {
if (file.isDirectory()) { // 是目录
scanBasePackage(basePackage + "." + file.getName());
} else { // 是文件if (!file.getName().endsWith(".class")) {
continue;
}
String className = basePackage + "." + file.getName().replace(".class", "");
// 获取该名字对应的的字节码对象 判断是不是含有Controller注解以及Service注解
try {
Class<?> clazz = Class.forName(className);if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class)) {
classNames.add(className);
}
} catch (ClassNotFoundException e) {e.printStackTrace();
}
}
}}protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {try {
// 匹配用户请求
boolean isPattern = patternUrlRequest(request, response);
if (!isPattern) {
out(response, "404 not found");
}
} catch (Exception e) {
e.printStackTrace();
}}private boolean patternUrlRequest(HttpServletRequest request, HttpServletResponse response) {
String url = request.getRequestURI();
String projectUrl = request.getContextPath();
url = url.replace(projectUrl, "").replaceAll("/+", "/");
UrlMethodMappingModel mappingModel = handlerMapping.get(url);
if (mappingModel != null) // 匹配成功
{
Map<String, Integer> paramsIndex = mappingModel.getParamMap();
Object[] values = new Object[paramsIndex.size()];
// 获取该方法的参数类型
Class<?>[] methodTypes = mappingModel.getMethod().getParameterTypes();
for (Map.Entry<String, Integer> params : paramsIndex.entrySet()) {
String key = params.getKey(); // 变量名
if (key.equals(HttpServletRequest.class.getName())) {
values[params.getValue()] = request;
} else if (key.equals(HttpServletResponse.class.getName())) {
values[params.getValue()] = response;
} else {
String userPostValue = request.getParameter(key);
Class<?> type = methodTypes[params.getValue()];
// 转换用户提交上来的数据
Object convertValue = convertValue(userPostValue, type);
values[params.getValue()] = convertValue;
}
}
// 激活该方法
try {
mappingModel.getMethod().invoke(mappingModel.getController(), values);
} catch (Exception e) {
e.printStackTrace();
}
return true;
} else {
return false;
}}private Object convertValue(String userPostValue, Class<?> type) {
if (type == String.class) {
return userPostValue;
} else if (type == Integer.class || type == int.class) {
return Integer.parseInt(userPostValue);
} else if (type == Long.class || type == long.class) {
return Long.valueOf(userPostValue);
} else {
try {
throw new Exception("不支持的数据类型");
} catch (Exception e) {
e.printStackTrace();
}
}return null;
}protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {doGet(request, response);
}// 首字母小写
private String lowerFirstChar(String className) {
char[] chars = className.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}private void out(HttpServletResponse response, String str) {
try {
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(str);
} catch (IOException e) {
e.printStackTrace();
}
}private class UrlMethodMappingModel {
private Method method;
private Object controller;
private Map<String/* 参数名 */, Integer/* 下标位置 */> paramMap;public Method getMethod() {
return method;
}public Object getController() {
return controller;
}public Map<String, Integer> getParamMap() {
return paramMap;
}public UrlMethodMappingModel(Method method, Object controller, Map<String, Integer> paramMap) {
this.method = method;
this.controller = controller;
this.paramMap = paramMap;
}
}
}

  

测试Controller

@Controller
public class TestController {
@Autowired
ITestService testServ;@RequestMapping(value = "/testAdd")
public void testAdd(@RequestParam("name") String name, HttpServletResponse reponse) {
String result = testServ.add(name);
out(reponse, result);
}
@RequestMapping(value = "/test")
public void test(String name, HttpServletResponse reponse) {
String result = testServ.add(name+" *** ");
out(reponse, result);
}private void out(HttpServletResponse response, String str) {
try {
response.setContentType("application/json;charset=utf-8");
response.getWriter().print(str);
} catch (IOException e) {
e.printStackTrace();
}
}}

  

服务接口定义

public interface ITestService {
String add(String name);
}

服务接口实现

@Service
public class TestServImp implements ITestService {
@Override
public String add(String name) {
return "add " + name + " ";
}}

  运行结果如下

实现一个简易版的SpringMvc框架

总结:大体功能基本已经完成,如果想写出更多功能,建议阅读

实现一个简易版的SpringMvc框架

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,082
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,556
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,406
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,179
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,815
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,898