一、在web.xml中配置DispatcherServlet(實現了Servlet接口)的相關信息,其中包括:
1、初始化參數init-param的name、value(contextConfigLocation: 配置文件的路徑)
2、Servlet處理的請求映射url (servlet-mapping)
二、在DispatcherServlet的初始化方法init()中做如下處理(Tomcat是Servlet容器,啓動Tomcat會加載Servlet並初始化)
1、加載配置文件
//獲取配置文件路徑(servletConfig爲init()的入參)
String contextConfigLocation = servletConfig.getInitParameter("contextConfigLocation");
//獲取文件輸入流(this是DispatcherServlet對象)
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
//使用Properties讀取輸入流
Properties contextConfig = new Properties();
contextConfig.load(inputStream);
//獲取配置信息,此處舉例爲:掃描包的路徑
contextConfig.getProperty("scan-package");
2、將掃描包路徑下的所有類名存入List<String>中
URL resourcePath = this.getClass().getClassLoader().getResource("scan-path");
File scanPath = new File(resourcePath.getFile());
//遞歸獲取文件名並過濾.class文件以外的文件,保存類名(全限定名)
List<String> classNameList = new ArrayList<>();
classNameList.add(className);
3、將加有@Controller/@Service等註解的類的對象放入ioc容器中(利用java反射機制,生成對象)
Map<String, Object> iocMap = new HashMap<>();
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(Controller.class)) {
Object instance = clazz.newInstance();
iocMap.put(beanName, instance);
}
//如果有實現接口的(如Service),需要把接口名及對應的實例引用放入ioc容器中(clazz.getInterfaces())
4、將加有@Autowired註解的屬性成員賦值(通過java反射機制,完成注入)
//獲取字段(instance爲ioc容器中的對象)
Field[] fields = instance.getClass().getDeclaredFields();
//獲取註解類型名稱
String beanName = field.getType().getName();
//將字段設置爲可訪問的
field.setAccessible(true);
//設置字段的值,完成注入
field.set(instance, iocMap.get(beanName));
5、將Controller中加有@RequestMapping註解的方法緩存起來(通過java反射機制,獲得方法)
//緩存容器
Map<String, Method> urlMethodMapping = new HashMap<>();
//instance爲ioc容器中的對象
Class<?> clazz = instance.getClass();
//獲取類RequestMapping路徑
if(clazz.isAnnotationPresent(Controller.class) && clazz.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
String baseUrl = RequestMapping.value();
}
//獲取方法RequestMapping路徑
if (method.isAnnotationPresent(RequestMapping.class)) {
String methodUrl = method.getAnnotation(RequestMapping.class).value();
}
//緩存方法
urlMethodMapping.put(url, method);
三、在Servlet的doGet()和doPost()中,將請求的url與緩存的urlMethodMapping比對,並調用請求的方法。(反射機制)
//HttpServletRequest、HttpServletResponse是doPost/doGet的入參
String url = reqest.getRequestURI();
//獲取緩存的方法
Method method = this.urlMethodMapping.get(url);
//取得方法對應的類型,並從ioc容器中獲取對象,再調用方法
String beanName = method.getDeclaringClass().getSimpleName();
method.invoke(iocMap.get(beanName), reqest, response);
在此感謝下面這篇博客和這本書,我纔對springmvc的流程有了大概的瞭解。
博客:https://blog.csdn.net/qq_40147863/article/details/96505433
書籍:《Spring 5核心原理與30個類手寫實戰》