Spring中毒太深,离开Spring我居然连最基本的接口都不会写了

@Override

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

try {

this.doDispatch(request, response);

} catch (Exception e) {

e.printStackTrace();

}

}

private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception{

String requestUrl = this.formatUrl(request.getRequestURI());

HandlerMapping handlerMapping = handlerMappingMap.get(requestUrl);

if (null == handlerMapping){

response.getWriter().write(“404 Not Found”);

return;

}

//获取方法中的参数类型

Class<?>[] paramTypeArr = handlerMapping.getMethod().getParameterTypes();

Object[] paramArr = new Object[paramTypeArr.length];

for (int i=0;i<paramTypeArr.length;i++){

Class<?> clazz = paramTypeArr[i];

//参数只考虑三种类型,其他不考虑

if (clazz == HttpServletRequest.class){

paramArr[i] = request;

}else if (clazz == HttpServletResponse.class){

paramArr[i] = response;

} else if (clazz == String.class){

Map<Integer,String> methodParam = handlerMapping.getMethodParams();

paramArr[i] = request.getParameter(methodParam.get(i));

}else{

System.out.println(“暂不支持的参数类型”);

}

}

//反射调用controller方法

handlerMapping.getMethod().invoke(handlerMapping.getTarget(), paramArr);

}

private String formatUrl(String requestUrl) {

requestUrl = requestUrl.replaceAll(“/+”,“/”);

if (requestUrl.lastIndexOf(“/”) == requestUrl.length() -1){

requestUrl = requestUrl.substring(0,requestUrl.length() -1);

}

return requestUrl;

}

@Override

public void init(Servle 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 tConfig config) throws ServletException {

//1.加载配置文件

try {

doLoadConfig(config.getInitParameter(“defaultConfig”));

} catch (Exception e) {

System.out.println(“加载配置文件失败”);

return;

}

//2.根据获取到的扫描路径进行扫描

doScanPacakge(myConfig.getBasePackages());

//3.将扫描到的类进行初始化,并存放到IOC容器

doInitializedClass();

//4.依赖注入

doDependencyInjection();

System.out.println(“DispatchServlet Init End…” );

}

private void doDependencyInjection() {

if (iocContainerMap.size() == 0){

return;

}

//循环IOC容器中的类

Iterator<Map.Entry<String,Object>> iterator = iocContainerMap.entrySet().iterator();

while (iterator.hasNext()){

Map.Entry<String,Object> entry = iterator.next();

Class<?> clazz = entry.getValue().getClass();

Field[] fields = clazz.getDeclaredFields();

//属性注入

for (Field field : fields){

//如果属性有WolfAutowired注解则注入值(暂时不考虑其他注解)

if (field.isAnnotationPresent(WolfAutowired.class)){

String value = toLowerFirstLetterCase(field.getType().getSimpleName());//默认bean的value为类名首字母小写

if (field.getType().isAnnotationPresent(WolfService.class)){

WolfService wolfService = field.getType().getAnnotation(WolfService.class);

value = wolfService.value();

}

field.setAccessible(true);

try {

Object target = iocContainerMap.get(beanName);

if (null == target){

System.out.println(clazz.getName() + “required bean:” + beanName + “,but we not found it”);

}

field.set(entry.getValue(),iocContainerMap.get(beanName));//初始化对象,后面注入

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

}

//初始化HanderMapping

String requestUrl = “”;

//获取Controller类上的请求路径

if (clazz.isAnnotationPresent(WolfController.class)){

requestUrl = clazz.getAnnotation(WolfController.class).value();

}

//循环类中的方法,获取方法上的路径

Method[] methods = clazz.getMethods();

for (Method method : methods){

//假设只有WolfGetMapping这一种注解

if(!method.isAnnotationPresent(WolfGetMapping.class)){

continue;

}

WolfGetMapping wolfGetMapping = method.getDeclaredAnnotation(WolfGetMapping.class);

requestUrl = requestUrl + “/” + wolfGetMapping.value();//拼成完成的请求路径

//不考虑正则匹配路径/xx/* 的情况,只考虑完全匹配的情况

if (handlerMappingMap.containsKey(requestUrl)){

System.out.println(“重复路径”);

continue;

}

Annotation[][] annotationArr = method.getParameterAnnotations();//获取方法中参数的注解

Map<Integer,String> methodParam = new HashMap<>();//存储参数的顺序和参数名

retryParam:

for (int i=0;i<annotationArr.length;i++){

for (Annotation annotation : annotationArr[i]){

if (annotation instanceof WolfRequestParam){

WolfRequestParam wolfRequestParam = (WolfRequestParam) annotation;

methodParam.put(i,wolfRequestParam.value());//存储参数的位置和注解中定义的参数名

continue retryParam;

}

}

}

requestUrl = this.formatUrl(requestUrl);//主要是防止路径多了/导致路径匹配不上

HandlerMapping handlerMapping = new HandlerMapping();

handlerMapping.setRequestUrl(requestUrl);//请求路径

handlerMapping.setMethod(method);//请求方法

handlerMapping.setTarget(entry.getValue());//请求方法所在controller对象

handlerMapping.setMethodParams(methodParam);//请求方法的参数信息

handlerMappingMap.put(requestUrl,handlerMapping);//存入hashmap

}

}

}

/**

* 初始化类,并放入容器iocContainerMap内

*/

private void doInitializedClass() {

if (classNameList.isEmpty()){

return;

}

for (String className : classNameList){

if (StringUtils.isEmpty(className)){

continue;

}

Class clazz;

try {

clazz = Class.forName(className);//反射获取对象

if (clazz.isAnnotationPresent(WolfController.class)){

String value = ((WolfController)clazz.getAnnotation(WolfController.class)).value();

//如果直接指定了value则取value,否则取首字母小写类名作为key值存储类的实例对象

iocContainerMap.put(StringUtils.isBlank(value) ? toLowerFirstLetterCase(clazz.getSimpleName()) : value,clazz.newInstance());

}else if(clazz.isAnnotationPresent(WolfService.class)){

String value = ((WolfService)clazz.getAnnotation(WolfService.class)).value();

iocContainerMap.put(StringUtils.isBlank(value) ? toLowerFirstLetterCase(clazz.getSimpleName()) : value,clazz.newInstance());

}else{

System.out.println(“不考虑其他注解的情况”);

}

} catch (Exception e) {

e.printStackTrace();

System.out.println(“初始化类失败,className为” + className);

}

}

}

/**

* 将首字母转换为小写

* @param className

* @return

*/

private String toLowerFirstLetterCase(String className) {

if (StringUtils.isBlank(className)){

return “”;

}

String firstLetter = className.substring(0,1);

return firstLetter.toLowerCase() + className.substring(1);

}

/**

* 扫描包下所有文件获取全限定类名

* @param basePackages

*/

private void doScanPacakge(String basePackages) {

if (StringUtils.isBlank(basePackages)){

return;

}

//把包名的.替换为/

String scanPath = “/” + basePackages.replaceAll(“\.”,“/”);

URL url = this.getClass().getClassLoader().getResource(scanPath);//获取到当前包所在磁盘的全路径

File files = new File(url.getFile());//获取当前路径下所有文件

for (File file : files.listFiles()){//开始扫描路径下的所有文件

if (file.isDirectory()){//如果是文件夹则递归

doScanPacakge(basePackages + “.” + file.getName());

}else{//如果是文件则添加到集合。因为上面是通过类加载器获取到的文件路径,所以实际上是class文件所在路径

classNameList.add(basePackages + “.” + file.getName().replace(“.class”,“”));

}

}

}

/**

* 加载配置文件

* @param configPath - 配置文件所在路径

*/

private void doLoadConfig(String configPath) {

InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(configPath);

Properties properties = new Properties();

try {

properties.load(inputStream);

} catch (IOException e) {

e.printStackTrace();

System.out.println(“加载配置文件失败”);

}

properties.forEach((k, v) -> {

try {

Field field = myConfig.getClass().getDeclaredField((String)k);

field.setAccessible(true);

field.set(myConfig,v);

} catch (Exception e) {

e.printStackTrace();

System.out.println(“初始化配置类失败”);

return;

}

});

}

}

5、这个 Servlet 相比较于上面的 HelloServlet 多了一个 init 方法,这个方法中主要做了以下几件事情:

(1)初始化配置文件,拿到配置文件中配置的参数信息(对应方法:doLoadConfig)。

(2)拿到第 1 步加载出来的配置文件,获取到需要扫描的包路径,然后将包路径进行转换成实际的磁盘路径,并开始遍历磁盘路径下的所有 class 文件,最终经过转换之后得到扫描路径下的所有类的全限定类型,存储到全局变量 classNameList 中(对应方法:doScanPacakge)。

(3)根据第 2 步中得到的全局变量 classNameList 中的类通过反射进行初始化(需要注意的是只会初始化加了指定注解的类)并将得到的对应关系存储到全局变量 iocContainerMap 中(即传说中的 IOC 容器),其中 key 值为注解中的 value 属性,如 value 属性为空,则默认取首字母小写的类名作为 key 值进行存储(对应方法:doInitializedClass)。

(4)这一步比较关键,需要对 IOC 容器中的所有类的属性进行赋值并且需要对 Controller 中的请求路径进行映射存储,为了确保最后能顺利调用 Controller 中的方法,还需要将方法的参数进行存储 。

对属性进行映射时只会对加了注解的属性进行映射,映射时会从 IOC 容器中取出第 3 步中已经初始化的实例对象进行赋值,最后将请求路径和 Controller 中方法的映射关系存入变量 handlerMappingMapkey 值为请求路径,value 为方法的相关信息 (对应方法:doDependencyInjection)。

6、存储请求路径和方法的映射关系时,需要用到 HandlerMapping 类来进行存储:

package com.lonely.wolf.mini.spring.v1;

import java.lang.reflect.Method;

import java.util.Map;

//省略了getter/setter方法

public class HandlerMapping {

private String requestUrl;

private Object target;//保存方法对应的实例

private Method method;//保存映射的方法

private Map<Integer,String> methodParams;//记录方法参数

}

7、初始化完成之后,因为拦截了 /* ,所以调用任意接口都会进入 MyDispatcherServlet ,而且最终都会执行方法 doDispatch,执行这个方法时会拿到请求的路径,然后和全局变量 handlerMappingMap 进行匹配,匹配不上则返回 404,匹配的上则取出必要的参数进行赋值,最后通过反射调用到 Controller 中的相关方法。

8、新建一个 HelloController 和 HelloService 来进行测试:

package com.lonely.wolf.mini.spring.controller;

import com.lonely.wolf.mini.spring.annotation.WolfAutowired;

import com.lonely.wolf.mini.spring.annotation.WolfController;

import com.lonely.wolf.mini.spring.annotation.WolfGetMapping;

import com.lonely.wolf.mini.spring.annotation.WolfRequestParam;

import c Java开源项目【ali1024.coding.net/public/P7/Java/git】 om.lonely.wolf.mini.spring.service.HelloService;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

@WolfController

public class HelloController {

@WolfAutowired

private HelloService helloService;

@WolfGetMapping(“/hello”)

public void query(HttpServletRequest request,HttpServletResponse response, @WolfRequestParam(“name”) String name) throws IOException {

response.setContentType(“text/html;charset=utf-8”);

response.getWriter().write(“Hello:” + name);

}

}

package com.lonely.wolf.mini.spring.service;

import com.lonely.wolf.mini.spring.annotation.WolfService;

@WolfService(value = “hello_service”)//为了演示能否正常取value属性

public class HelloService {

}

9、输入测试路径:http://localhost:8080hello?name=双子孤狼, 进行测试发现可以正常输出:Hello:双子孤狼

上面这个例子只是一个简单的演示,通过这个例子只是希望在没有任何框架的情况下,我们也能知道如何完成一个简单的应用开发。例子中很多细节都没有进行处理,仅仅只是为了体验一下 Spring 的核心思想,并了解 Spring 到底帮助我们做了什么,实际上 Spring 能帮我们做的事情远比这个例子中多得多,Spring 体系庞大,设计优雅,经过了多年的迭代优化,是一款非常值得研究的框架。

总结

本文从介绍 Spring 核心功能开始入手,从如何利用 Spring 完成一个应用开发,讲述到假如没有 Spring 我们该如何基于 Servlet 进行开发,最后再通过一个简单的例子体验了 Spring 的核心思想。

推荐文章

  • [面试官问:前后端分离项目,有什么优缺点?我说:没](()

  • [2020 年腾讯新增 20 亿行代码,鹅厂第一编程语言还是它](()

  • [通俗讲解分布式锁,看完不懂算我输](()

  • [写博客能月入10K?](()

  • [一款基于 Spring Boot 的现代化社区(论坛/问答/社交网络/博客)](()

更多项目源码

  • [这或许是最美的Vue+Element开源后台管理UI](()

  • [推荐一款高颜值的 Spring Boot 快速开发框架](()

  • [一款基于 Spring Boot 的现代化社区(论坛/问答/社交网络/博客)](()

  • [13K点赞都基于 Vue+Spring 前后端分离管理系统ELAdmin,大爱](()

  • [想接私活时薪再翻一倍,建议根据这几个开源的SpringBoot项目](()

最后总结我的面试经验

2021年的金三银四一眨眼就到了,对于很多人来说是跳槽的好机会,大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

BAT面试经验

实战系列:Spring全家桶+Redis等

其他相关的电子书:源码+调优

面试真题:

更多项目源码

  • [这或许是最美的Vue+Element开源后台管理UI](()

  • [推荐一款高颜值的 Spring Boot 快速开发框架](()

  • [一款基于 Spring Boot 的现代化社区(论坛/问答/社交网络/博客)](()

  • [13K点赞都基于 Vue+Spring 前后端分离管理系统ELAdmin,大爱](()

  • [想接私活时薪再翻一倍,建议根据这几个开源的SpringBoot项目](()

最后总结我的面试经验

2021年的金三银四一眨眼就到了,对于很多人来说是跳槽的好机会,大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

[外链图片转存中…(img-TUFQrRrD-1650521638716)]

BAT面试经验

实战系列:Spring全家桶+Redis等

[外链图片转存中…(img-slHC7iLk-1650521638717)]

其他相关的电子书:源码+调优

[外链图片转存中…(img-O5Bocd1x-1650521638718)]

面试真题:

[外链图片转存中…(img-N7DYt86k-1650521638718)]

[外链图片转存中…(img-UfcL5IuK-1650521638719)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值