手写SpringMVC框架(三)-------具体方法的实现
2020-07-02 17:13
190 查看
续接前文
手写SpringMVC框架(二)结构开发设计
本节我们来开始具体方法的代码实现。
doLoadConfig()方法的开发
思路:我们需要将contextConfigLocation路径读取过来的配置文件springmvc.properties加载到内存中来。
实现:使用properties及类加载器。
代码如下:
import java.io.InputStream; import java.util.Properties; private Properties properties=new Properties(); //加载配置文件 private void doLoadConfig(String contextConfigLocation) { InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation); try { properties.load(resourceAsStream); } catch (IOException e) { e.printStackTrace(); } }
doScan()扫描包方法的实现
代码如下:
List&l 1044 t;String> classNames=new ArrayList<>(); //扫描类 磁盘上的文件夹及文件 private void doScan(String scanPackage) { String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); File pack=new File(scanPackagePath); File[] files = pack.listFiles(); for (File file : files) { if (file.isDirectory()) { //递归 doScan(scanPackage+"."+file.getName()); //比如com.lagou.controller }else if(file.getName().endsWith(".class")){ String className = scanPackage + "." + file.getName().replaceAll(".class", ""); classNames.add(className); } } }
doInstance()方法:IOC实例化容器
代码如下
// ioc容器 private Map<String,Object> ioc = new HashMap<String,Object>(); // ioc容器 // 基于classNames缓存的类的全限定类名,以及反射技术,完成对象创建和管理 private void doInstance() { if(classNames.size() == 0) return; try{ for (int i = 0; i < classNames.size(); i++) { String className = classNames.get(i); // com.lagou.demo.controller.DemoController // 反射 Class<?> aClass = Class.forName(className); // 区分controller,区分service' if(aClass.isAnnotationPresent(LagouController.class)) { // controller的id此处不做过多处理,不取value了,就拿类的首字母小写作为id,保存到ioc中 String simpleName = aClass.getSimpleName();// DemoController String lowerFirstSimpleName = lowerFirst(simpleName); // demoController Object o = aClass.newInstance(); ioc.put(lowerFirstSimpleName,o); }else if(aClass.isAnnotationPresent(LagouService.class)) { LagouService annotation = aClass.getAnnotation(LagouService.class); //获取注解value值 String beanName = annotation.value(); // 如果指定了id,就以指定的为准 if(!"".equals(beanName.trim())) { ioc.put(beanName,aClass.newInstance()); }else{ // 如果没有指定,就以类名首字母小写 beanName = lowerFirst(aClass.getSimpleName()); ioc.put(beanName,aClass.newInstance()); } // service层往往是有接口的,面向接口开发,此时再以接口名为id,放入一份对象到ioc中,便于后期根据接口类型注入 Class<?>[] interfaces = aClass.getInterfaces(); for (int j = 0; j < interfaces.length; j++) { Class<?> anInterface = interfaces[j]; // 以接口的全限定类名作为id放入 ioc.put(anInterface.getName(),aClass.newInstance()); } }else{ continue; } } }catch (Exception e) { e.printStackTrace(); } } // 首字母小写方法 public String lowerFirst(String str) { char[] chars = str.toCharArray(); if('A' <= chars[0] && chars[0] <= 'Z') { chars[0] += 32; } return String.valueOf(chars); }
doAutoWired()依赖注入:
// 实现依赖注入 private void doAutoWired() { if(ioc.isEmpty()) {return;} // 有对象,再进行依赖注入处理 // 遍历ioc中所有对象,查看对象中的字段,是否有@LagouAutowired注解,如果有需要维护依赖注入关系 for(Map.Entry<String,Object> entry: ioc.entrySet()) { // 获取bean对象中的字段信息 Field[] declaredFields = entry.getValue().getClass().getDeclaredFields(); // 遍历判断处理 for (int i = 0; i < declaredFields.length; i++) { Field d 3d03 eclaredField = declaredFields[i]; // @LagouAutowired private IDemoService demoService; if(!declaredField.isAnnotationPresent(LagouAutowired.class)) { continue; } // 有该注解 LagouAutowired annotation = declaredField.getAnnotation(LagouAutowired.class); String beanName = annotation.value(); // 需要注入的bean的id if("".equals(beanName.trim())) { // 没有配置具体的bean id,那就需要根据当前字段类型注入(接口注入) IDemoService beanName = declaredField.getType().getName(); } // 开启赋值 declaredField.setAccessible(true); try { declaredField.set(entry.getValue(),ioc.get(beanName)); } catch (IllegalAccessException e) { e.printStackTrace(); } } } }
相关pojo实体类
package com.lagou.edu.mvcframework.pojo; import javax.sound.midi.MetaEventListener; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; /** * 封装handler方法相关的信息 */ public class Handler { private Object controller; // method.invoke(obj,) private Method method; private Pattern pattern; // spring中url是支持正则的 private Map<String,Integer> paramIndexMapping; // 参数顺序,是为了进行参数绑定,key是参数名,value代表是第几个参数 <name,2> public Handler(Object controller, Method method, Pattern pattern) { this.controller = controller; this.method = method; this.pattern = pattern; this.paramIndexMapping = new HashMap<>(); } public Object getController() { return controller; } public void setController(Object controller) { this.controller = controller; } public Method getMethod() { return method; } public void setMethod(Method method) { this.method = method; } public Pattern getPattern() { return pattern; } public void setPattern(Pattern pattern) { this.pattern = pattern; } public Map<String, Integer> getParamIndexMapping() { return paramIndexMapping; } public void setParamIndexMapping(Map<String, Integer> paramIndexMapping) { this.paramIndexMapping = paramIndexMapping; } }
LgDispatcherServlet里面的initHandleMapping()方法:
private List<Handler> handlerMapping = new ArrayList<>(); /* 构造一个HandlerMapping处理器映射器 最关键的环节 目的:将url和method建立关联 */ private void initHandlerMapping() { if(ioc.isEmpty()) {return;} for(Map.Entry<String,Object> entry: ioc.entrySet()) { // 获取ioc中当前遍历的对象的class类型 Class<?> aClass = entry.getValue().getClass(); if(!aClass.isAnnotationPresent(LagouController.class)) {continue;} String baseUrl = ""; if(aClass.isAnnotationPresent(LagouRequestMapping.class)) { LagouRequestMapping annotation = aClass.getAnnotation(LagouRequestMapping.class); baseUrl = annotation.value(); // 等同于/demo } // 获取方法 Method[] methods = aClass.getMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; // 方法没有标识LagouRequestMapping,就不处理 if(!method.isAnnotationPresent(LagouRequestMapping.class)) {continue;} // 如果标识,就处理 LagouRequestMapping annotation = method.getAnnotation(LagouRequestMapping.class); String methodUrl = annotation.value(); // /query String url = baseUrl + methodUrl; // 计算出来的url /demo/query // 把method所有信息及url封装为一个Handler Handler handler = new Handler(entry.getValue(),method, Pattern.compile(url)); // 计算方法的参数位置信息 // query(HttpServletRequest request, HttpServletResponse response,String name) Parameter[] parameters = method.getParameters(); for (int j = 0; j < parameters.length; j++) { Parameter parameter = parameters[j]; if(parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) { // 如果是request和response对象,那么参数名称写HttpServletRequest和HttpServletResponse handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j); }else{ handler.getParamIndexMapping().put(parameter.getName(),j); // <name,2> } } // 建立url和method之间的映射关系(map缓存起来) handlerMapping.add(handler); } } }
正式调用请求的doPost方法:
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //真正处理请求 //根据uri获取到能处理当前请求的handler(从handlerMapping(List)中获取) Handler handler=getHandler(req); if(handler==null) { resp.getWriter().write("404 not found"); return ; } //参数绑定,获取所有参数类型数组,这个数组长度就是我们最后要传入的args数组的长度 Class<?>[] parameterTypes = handler.getMethod().getParameterTypes(); //根据上述数组长度创建一个新的数组(参数数组,使要传入反射调用的) Object[] paraValues = new Object[parameterTypes.length]; //以下是为了向参数数组中塞值,而且还得保证参数的顺序和方法中的形参顺序一致 Map<String, String[]> parameterMap = req.getParameterMap(); //遍历request中所有参数 for (Map.Entry<String, String[]> param : parameterMap.entrySet()) { //name=1&name=2 name[1,2] String value = StringUtils.join(param.getValue(), ","); //如同1,2 //如果参数和方法中的参数匹配上了,则填充数据 if(!handler.getParamIndexMapping().containsKey(param.getKey())){continue;} Integer index = handler.getParamIndexMapping().get(param.getKey()); //name 在第二个位置 paraValues[index] = value; //把前台传过来的参数值填充到对应的位置去 } Integer requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName()); //0 paraValues[requestIndex]=req; Integer responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName()); //1 paraValues[responseIndex]=resp; //最终调用handler的method属性 try { handler.getMethod().invoke(handler.getController(),paraValues); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } private Handler getHandler(HttpServletRequest req) { if(handlerMapping.isEmpty()){return null;} String url=req.getRequestURI(); for (Handler handler : handlerMapping) { Matcher matcher = handler.getPattern().matcher(url); if(matcher.matches()){continue;} return handler; } return null; }
pom.xml
需要定义编译器的编译细节,为了让编译器编译的时候能够记住形参的名字,而不是args1...等等。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lagou.edu</groupId> <artifactId>mvc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>mvc Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.5</version> </dependency> </dependencies> <build> <plugins> <!--编译插件定义编译细节--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>11</source> <target>11</target> <encoding>utf-8</encoding> <!--告诉编译器,编译的时候记录下形参的真实名称--> <compilerArgs> <arg>-parameters</arg> </compilerArgs> </configuration> </plugin> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> <path>/</path> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build> </project>
至此,我们手写SpringMVC框架已经基本完成。
欢迎访问:
微信公众号(程序员资料站):code_data
相关文章推荐
- 纯手写SpringMVC框架,用注解实现springmvc过程
- MNIST手写数字图片识别(线性回归、CNN方法的手工及框架实现)(未完待续)...
- 关于dorado框架datatable处的右键菜单无法根据具体数据实现动态分别显示不同菜单的解决方法
- 使用ssm框架 通过ajax异步提交数据具体实现方法
- 纯手写SpringMVC框架,用注解实现springmvc过程
- 4 手写实现SpringMVC,第四节:匹配用户请求、执行映射方法
- 源码分析之动手实现手写一个自己的SpringMVC框架(三)
- 基于vue框架手写一个notify插件实现通知功能的方法
- 纯手写实现简单的springmvc框架,实现实体类参数接收,并实现ioc容器,基于yaml配置文件
- 纯手写SpringMVC框架,用注解实现springmvc过程
- 深度解析SpringMvc实现原理手写SpringMvc框架
- Python(TensorFlow框架)实现手写数字识别系统的方法
- 解析Cookie欺骗实现过程及具体应变方法
- 持久化框架数据持久化框架(JPA)及其具体实现
- acm pku 1118 Lining Up的具体实现方法
- windows8将软件固定到任务栏具体实现方法图解
- .Net实现合并文件的具体方法
- Java2下Applet数字签名具体实现方法
- SQL Server 2005表分区的具体实现方法
- SpringMVC 实现根据不同的请求类型(请求方法method=GetOrPost)分别响应