前言:
spring框架是目前互联网应用开发最流行的框架之一,作为一个后台的开发人员应当不能错过向大佬学习学习的机会,所以阅读spring的源码还是非常有价值的。总的来说阅读源码基本可以获得以下好处
1、编写代码的规范
2、学习如何编写健壮性代码
3、框架的设计、设计模式、思想等
4、知识的查漏补缺
当然,肯定不仅仅是上面的优点,不管怎么样,有时间多看源码、多思考、对于程序员来说还是好处大大滴,好了废话不多说了,直接看今天分享的内容,手写一个简单的springmvc框架
github地址:源码地址
一、了解SpringMVC运行的流程
(1)用户发送请求至前端控制器DispatcherServlet
(2)DispatcherServlet收到请求调用HandlerMapping处理器映射器。
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
(4)DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
(5)执行处理器(Controller,也叫后端控制器)。
(6)Controller执行完成返回ModelAndView
(7)HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
(8)DispatcherServlet将ModelAndView传给ViewReslover视图解析器
(9)ViewReslover解析后返回具体View
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
(11)DispatcherServlet响应用户。
二、手写SpringMVC思路
(1)编写注解
如@controller、@service、@requestParam、@requestMapping、@autowired
这些注解作用在类、属性、参数上
(2)实例化bean
这一步没有在springmvc的图中展现出来,这是因为由spring ioc支持
(3)依赖注入
依赖注入主要是针对@autowired,这也是spring ioc支持
(4)uri映射到对应的instance和method
这里主要就是上面图中的部分逻辑,也是关键
(5)参数处理
主要处理被@requestparam修饰的参数
(6)在web.xml中配置拦截的servlet
主要配置自己手写的dispatcherServlet拦截
三、关键部分代码
1、web.xml配置拦截器
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<display-name>DispatcherServlet</display-name>
<description></description>
<servlet-class>com.taolong.mymvc.servlet.DispatcherServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2、 编写注释类,简单看一个@MyController注释类,其他的都差不多
package com.taolong.mymvc.annotatioin;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({java.lang.annotation.ElementType.TYPE})//作用范围
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
String value() default "";
}
3、编写核心DispatcherServlet类,基本上所有的逻辑都在这个类里面,分开解析
(1)扫描所有的类名,就是为实例化类做准备
private void scanPackage(String basePackage) {
URL url = this.getClass().getClassLoader().getResource("/"+replaceTo(basePackage));
System.out.println("url = "+url);
String parentPath = url.getFile();
File file = new File(parentPath);
String[] fileList = file.list();
if (null == fileList || fileList.length == 0) return;
for (String fileName : fileList) {
File filePath = new File(parentPath+fileName);
//如果是文件夹,则继续递归扫描
if (filePath.isDirectory()) {
scanPackage(basePackage+"."+fileName);
}else {//将class文件加入到集合中,方便下次实例化
classNames.add(basePackage+"."+filePath.getName());
}
}
}
//路径
private String replaceTo(String basePackage) {
return basePackage.replaceAll("\\.", "/");
}
代码还是比较简单,主要就是扫描指定基础包下面的所有的类名称
(2)实例化类,其实就是根据(1)步中类名通过反射实例化
/**
* 将扫描的类,通过反射实例化,然后加入到map中
*/
private void instanceScanedBeans() {
if (classNames.isEmpty()) {
System.out.println("未扫描到任何的类!");
return;
}
for (String className : classNames) {
//去掉.class,方便反射处理
String cn = className.replace(".class", "");
try {
Class<?> clazz = Class.forName(cn);
//判断类是否被mycontroller注释过,目前做简单处理,只实例化被mycontroller和myservice注释过的类
if (clazz.isAnnotationPresent(MyController.class)) {
MyController annotation = clazz.getAnnotation(MyController.class);
Object instance = clazz.newInstance();//实例化
String instanceKey = annotation.value();//获取annotation的value
if ("".equals(instanceKey)) {//如果注释中没有值,则将类的名字当作key(简单处理)
instanceKey = toLowerFirstWord(clazz.getSimpleName());
}
beans.put(instanceKey, instance);
}else if(clazz.isAnnotationPresent(MyService.class)) {//判断是否被myservice注释
MyService annotation = clazz.getAnnotation(MyService.class);
Object instance = clazz.newInstance();
String instanceKey = annotation.value();
if ("".equals(instanceKey)) {
//这里做了简单处理,直接获取serviceimpl的接口名当作key
instanceKey = toLowerFirstWord(clazz.getInterfaces()[0].getSimpleName());
}
beans.put(instanceKey, instance);
}else {
continue;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
这里只实例化了被@mycontroller和@myservice注释的bean
(3)依赖注入,主要是解决controller中通过@autowired注入的service属性
/**
* 依赖注入,如controller中使用autowired注入service
*/
private void iocDI() {
if (beans.isEmpty() || beans.entrySet().isEmpty()) {
System.out.println("没有实例化的类!");
return;
}
for (Map.Entry<String, Object> entry : beans.entrySet()) {
Object instance = entry.getValue();
Class<?> clazz = instance.getClass();
//通过反射获取类的属性,为简化代码,这里只在controller中注入,service中无注入
if (clazz.isAnnotationPresent(MyController.class)) {
Field[] fields = clazz.getDeclaredFields();//获取所有属性
for (Field field : fields) {
//其实就是判断属性是否被autowired修饰
if (field.isAnnotationPresent(MyAutoWired.class)) {
MyAutoWired annotation = field.getAnnotation(MyAutoWired.class);
String value = annotation.value();
field.setAccessible(true);//增加权限
if ("".equals(value)) {
//这里也是简单处理,因为是面向接口编程的,所以为了找到前面实例化的service,应该拿到它的接口名
//value=com.taolong.service.UserService
value = field.getType().getName();
//value=userService
value = toLowerFirstWord(value.substring(value.lastIndexOf(".")+1));
}
try {
//注入到属性中
field.set(instance, beans.get(value));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}else {
continue;
}
}
}else {
continue;
}
}
}
(4)uri和controller以及method绑定
/**
* 将requestMapping中的url和对应的方法进行绑定
*/
private void handlerMapping() {
if (beans.isEmpty() || beans.entrySet().isEmpty()) {
System.out.println("没有实例化类!");
return;
}
for (Map.Entry<String, Object> entry : beans.entrySet()) {
Object instance = entry.getValue();
Class<?> clazz = instance.getClass();
//首先读取类中的requestmapping的url
if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
String classPathUrl = annotation.value();
//保存url关联的controller,方便dopost时找到对应的类
//简单处理,默认是controller类中存在mapping值,可优化
clazzHandlerMap.put(classPathUrl.replace("/", ""), instance);
Method[] methods = clazz.getMethods();
if (methods == null || methods.length == 0) {
System.out.println("没有对应的方法!");
return;
}
for (Method method : methods) {
if (method.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping annotationMethod = method.getAnnotation(MyRequestMapping.class);
String methodPathUrl = annotationMethod.value();
//key为对应的url,value为映射的方法
handlerMap.put(classPathUrl+methodPathUrl, method);
}else {
continue;
}
}
}else {
continue;
}
}
}
(5)处理请求参数,主要看被@requestParam注释的参数,这里是使用策略模式分别处理request参数response参数@requestparam修饰的参数
//参数解析,并获取注解的值
public Object argumentResolver(HttpServletRequest request,
HttpServletResponse response, Class<?> type, int paramIndex,
Method method) {
Annotation[][] an = method.getParameterAnnotations();
Annotation[] paramAns = an[paramIndex];
for (Annotation paramAn : paramAns) {
if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
MyRequestParam rp = (MyRequestParam)paramAn;
String value = rp.value();
return request.getParameter(value);
}
}
return null;
}
(6)最后看下dopost方法处理请求
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String uri = req.getRequestURI();
String context = req.getContextPath();
System.out.println("uri="+uri+" context="+context);
//去掉uri前面的context
String path = uri.replace(context, "");
if (!handlerMap.containsKey(path)) {
resp.getWriter().write("404 NOT FOUNT");
return;
}
//根据uri找到需要调用的方法
Method method = (Method)handlerMap.get(path);
//简单处理,默认controller类的requestmapping存在且有值
String clzzUrl = path.split("/")[1];
Object instance = clazzHandlerMap.get(clzzUrl);
//处理器
HandlerAdapterService ha = (HandlerAdapterService) beans.get(HANDLERADAPTER);
//使用策略模式处理method中的参数
Object[] args = ha.hand(req, resp, method, beans);
try {
method.invoke(instance, args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* 把字符串的首字母小写
* @param name
* @return
* eg.UserService->userService
*/
private String toLowerFirstWord(String name){
if (null == name || "".equals(name)) return null;
char[] charArray = name.toCharArray();
charArray[0] += 32;
return String.valueOf(charArray);
}
(7)简单的贴下业务代码吧
UserController.java
package com.taolong.mymvc.controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.taolong.mymvc.annotatioin.MyAutoWired;
import com.taolong.mymvc.annotatioin.MyController;
import com.taolong.mymvc.annotatioin.MyRequestMapping;
import com.taolong.mymvc.annotatioin.MyRequestParam;
import com.taolong.mymvc.service.UserService;
@MyController("UserController")
@MyRequestMapping("/user")
public class UserController {
@MyAutoWired
private UserService userServiceabc;
@MyRequestMapping("/saveUser")
public void saveUser(HttpServletRequest request,HttpServletResponse response,
@MyRequestParam("userName")String userName,
@MyRequestParam("passwd") String password) {
try {
PrintWriter printWriter = response.getWriter();
String result = userServiceabc.saveUser(userName, password);
printWriter.write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
UserServiceImpl.java
package com.taolong.mymvc.service.impl;
import com.taolong.mymvc.annotatioin.MyService;
import com.taolong.mymvc.service.UserService;
@MyService
public class UserServiceImpl implements UserService {
@Override
public String saveUser(String name, String passwd) {
System.out.println("name="+name+" passwd="+passwd);
return "save user successful..."+"name="+name+" passwd="+passwd;
}
}
测试结果:
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/111228.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...