Spring手写简化版MVC流程详解

spring是一个非常流行的技术框架,其中spring mvc组件在其中非常重要的地位,主要面要客户端提供服务,我们今天来手写一个简化版的mvc,且包括ioc部分,主要利用servlet机制来实现,类的关系如下:

准备注解类,类于spring的@Autowired、@Service、@Controller、@RequestMapping、@RequestParam

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSAutowired {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSController {
    String value() default "";
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSRequestMapping {
    String value() default "";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSRequestParam {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CSService {
    String value() default "";
}

准备service interface

public interface IDemoService {
    public String get(String name);
}

准备service实现类,利用@CSService

@CSService
public class DemoService implements IDemoService {
    @Override
    public String get(String name) {
        return "My name is "+name;
    }
}

准备对外服务的类,主要利用@CSController注解

@CSController
@CSRequestMapping("/demo")
public class DemoAction {
    @CSAutowired
    private IDemoService demoService;
    @CSRequestMapping("/query")
    public void query(HttpServletRequest req, HttpServletResponse resp, @CSRequestParam("name") String name){
        String result=demoService.get(name);
        try {
            resp.getWriter().write(result);
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }
    @CSRequestMapping("/add")
    public void add(HttpServletRequest req, HttpServletResponse resp, @CSRequestParam("aa") Integer a,@CSRequestParam("b") Integer b){
        try {
            resp.getWriter().write(a+"+"+b+"="+(a+b));
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }
    @CSRequestMapping("/remove")
    public void remove(HttpServletRequest req, HttpServletResponse resp, @CSRequestParam("id") Integer id){
        try {
            resp.getWriter().write("id="+id);
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }
}

准备servlet

主要实现了以下功能:

1).根据@CSController对外服务的url如何mapping到具体方法 doHandlerMap

2).service和controller bean的管理 iocBeans

3).如何实列化bean doInstance

4).如何获取url中参数值 doDispatch中

5).找到需要加载的class doScanner

6).如何自动autowired doAutoWried

public class CSDispatchServlet extends HttpServlet {
    public static String urlPattern="/custom";
    private void doDispatch(HttpServletRequest request,HttpServletResponse response) throws Exception{
        String url=request.getRequestURI();
        String contextPath=request.getContextPath();
        url=url.replace(urlPattern,"");
        if(!handlerMap.containsKey(url)){
            response.getWriter().write("404 not found!");
            return;
        }
        Method method=handlerMap.get(url);
        Annotation[][] methodParameterAnnotations= method.getParameterAnnotations();
        Parameter[]  methodParameters= method.getParameters();
        Annotation[][] paramerterAnnotations=method.getParameterAnnotations();
        ArrayList<Object> methodParameterValues=new  ArrayList<Object>();
        Map<String,String[]> requestParams= request.getParameterMap();
        int parmeterCnt=0;
        for(Parameter parameter:methodParameters){
            if(parameter.getType()==HttpServletRequest.class ){
                methodParameterValues.add(request);
            }else if(parameter.getType()==HttpServletResponse.class){
                methodParameterValues.add(response);
            }else {
               String methodParamName="";
               if(paramerterAnnotations[parmeterCnt].length>0) {
                       Annotation annotation= paramerterAnnotations[parmeterCnt][0];
                       if(annotation instanceof CSRequestParam) {
                           methodParamName = ((CSRequestParam) annotation).value();
                       }
               }
               if("".equals(methodParamName.trim())){
                   methodParamName=parameter.getName();
               }
                String value="";
               //String value=Arrays.toString(requestParams.get(methodParamName));
                if(requestParams.get(methodParamName).length>1)
                    value=Arrays.toString(requestParams.get(methodParamName));
                else if(requestParams.get(methodParamName).length==1)
                     value= requestParams.get(methodParamName)[0];
                else
                    value="999999";
               if(parameter.getType()==String.class)
                   methodParameterValues.add(value);
               else if(parameter.getType()==Integer.class) {
                   try {
                       methodParameterValues.add(Integer.parseInt(value));
                   } catch (Exception e){
                       methodParameterValues.add(99999999);
                   }
               }else {
                    //可以扩展复杂类型转换
               }
            }
            parmeterCnt++;
        }
        String beanName=this.genBeanName(method.getDeclaringClass().getSimpleName());
        method.invoke(this.iocBeans.get(beanName), methodParameterValues.toArray());
    }
    private String genBeanName(String beanName){
        if(beanName.length()>1)
            beanName=beanName.substring(0,0).toLowerCase()+beanName.substring(1);
        else
            beanName=beanName.toLowerCase();
        return beanName;
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatch(req,resp);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
    private ArrayList<String> classs=new ArrayList<String>();
    private ConcurrentHashMap<String,Object> iocBeans=new ConcurrentHashMap<String,Object>();
    private ConcurrentHashMap<String,Method> handlerMap=new ConcurrentHashMap<String,Method>();
    private void doInstance() {
        try {
            for (String className : classs) {
                if (!className.contains(".")) continue;
                Class<?> clazz = Class.forName(className);
                String beanName="";
                if (clazz.isAnnotationPresent(CSController.class)) {
                    CSController controller = clazz.getAnnotation(CSController.class);
                    beanName=controller.value();
                }else if(clazz.isAnnotationPresent(CSService.class)){
                    CSService service=clazz.getAnnotation(CSService.class);
                    beanName=service.value();
                }else {
                    continue;
                }
                Object instance=clazz.newInstance();
                if("".equals(beanName.trim()))
                    beanName=clazz.getSimpleName();
                beanName=genBeanName(beanName);
                iocBeans.put(beanName,instance);
                if(clazz.isAnnotationPresent(CSService.class)){
                    for(Class  c:  clazz.getInterfaces()){
                        if(iocBeans.containsKey(c.getName())) continue;
                        iocBeans.put(c.getName(),instance);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    private void doAutoWried(){
        for(Object o:iocBeans.values()){
            if(o==null) continue;
            Class clazz=o.getClass();
            if(clazz.isAnnotationPresent(CSService.class) || clazz.isAnnotationPresent(CSController.class)){
                Field[] fields=clazz.getDeclaredFields();
                for(Field f:fields){
                    if(!f.isAnnotationPresent(CSAutowired.class)) continue;
                    CSAutowired autowired=f.getAnnotation(CSAutowired.class);
                    String beanName=autowired.value();
                    if("".equals(beanName)) beanName=f.getType().getName();
                    f.setAccessible(true);
                    try{
                        Object o1=iocBeans.get(beanName);
                        f.set(o,iocBeans.get(beanName));
                    }catch (IllegalAccessException e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    private void doHandlerMap(ServletConfig config){
        for(Object o:iocBeans.values()){
            if(!o.getClass().isAnnotationPresent(CSController.class)) continue;
            String baseUrl="";
            if(o.getClass().isAnnotationPresent(CSRequestMapping.class)){
                CSRequestMapping requestMapping=o.getClass().getAnnotation(CSRequestMapping.class);
                baseUrl=requestMapping.value();
            }
            for(Method method: o.getClass().getMethods()){
                if(method.isAnnotationPresent(CSRequestMapping.class)) {
                    CSRequestMapping requestMapping=method.getAnnotation(CSRequestMapping.class);
                    String url=baseUrl+requestMapping.value().replaceAll("/+","/");
                    String contextPath=config.getServletContext().getContextPath();
                    this.handlerMap.put(url,method);
                }
            }
        }
    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        InputStream is=null;
        try{
            System.out.println("custom servlet init........");
            /*
            Properties configContext=new Properties();
            is=this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
            configContext.load(is);
            String scanPackage=configContext.getProperty("scanPackage");
            */
            Enumeration<String> enumerations= config.getInitParameterNames();
            while (enumerations.hasMoreElements()){
                System.out.println(enumerations.nextElement());
            }
            doScanner("com.mesui.spring.custom");
            doInstance();
            doAutoWried();
            doHandlerMap( config);
        }catch (Exception exception){
            exception.printStackTrace();
        }finally {
        }
    }
    private void doScanner(String scanPackage){
        URL url= this.getClass().getClassLoader().getResource("") ;
        String filePath="";
        try {
              filePath= URLDecoder.decode( url.getPath(),"UTF-8")+"/"+scanPackage.replaceAll("\\.","/");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        File classDir=new File(filePath);
        for(File file:classDir.listFiles()){
            if(file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else if(!file.getName().endsWith(".class")) {
                continue;
            }
            if(!file.isDirectory()) {
                String clzzName = (scanPackage + "." + file.getName().replace(".class", ""));
                //map.put(clzzName,null);
                classs.add(clzzName);
            }
        }
    }
}

在利用spring的configuration类初始化servlet

这边为了方便进行偷懒,这样/custom/下的服务按照自已逻辑对对外服务,不按照spring mvc的进行,另外自已可以tomcat的web.xml中标记servlet完全脱离spring

    @Configuration
public class MybatisPlusConfig {
    @Bean
    public ServletRegistrationBean CustomServlet(){
        return new ServletRegistrationBean(new CSDispatchServlet(),CSDispatchServlet.urlPattern+"/*");
    }
}

测试

结论

从上面的例子中我们可以看到自已写一个mvc也很方便,不是什么难事,但是这个只是用于学习,毕竟spring是一个体系,我们自已不可能将所有内容重新写一遍,但是自已写着玩有助于对spring mvc和IOC的理解。

到此这篇关于Spring手写简化版mvc流程详解的文章就介绍到这了,更多相关Spring mvc内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 使用springmvc运行流程分析,手写spring框架尝试

    目录 1.配置阶段 2.初始化阶段 3.运行阶段 springMVC介绍以及执行流程 什么是SpringMVC? SpringMVC的优势是什么? 该文章主要是分析Springmvc启动的流程(配置阶段.初始化阶段和运行阶段),可以让自己对spring框架有更深一层的理解.对框架比较感兴趣的朋友都可以了解阅读下,对于我所描述的内容有错误的还望能不吝指出. 对于springmvc中的整个流程我个人把他分为这几个阶段,包括个人手写的spring也是参照此按阶段实现: 1.配置阶段 根据web.xml

  • SpringMVC获取请求参数实现方法介绍

    目录 一.通过ServletAPI获取 二.通过控制器方法的形参获取请求参数 三.@RequestParam 四.@RequestHeader 五.@CookieValue 六.通过POJO获取请求参数 七.解决获取请求参数的乱码问题 我们已经学习过@RequestMapping了,学的属性可能比较多,但是我们常用的也就value和method.所以说我们已经可以把我们的浏览器发送的请求和控制器方法来创建映射关系了. 一.通过ServletAPI获取 将HttpServletRequest作为控

  • SpringMVC视图作用详解

    目录 一.ThymeleafView 二.转发视图 三.重定向视图 四.视图控制器view-controller SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户 SpringMVC视图的种类很多,默认有转发视图和重定向视图 当工程引入jstl的依赖,转发视图会自动转换为JstlView 若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视 图解析器解析之后所得到的是ThymeleafV

  • 一文带你入门SpringMVC的配置与使用

    目录 1.概述 2.中心控制器 3.搭建SpringMVC 更新pom依赖 配置web.xml 配置springmvc-servlet.xml 创建Controller 创建视图层 1.概述 Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架. Spring MVC的特点: 轻量级,简单易学 高效 , 基于请求响应的MVC框架 与Spring兼容性好,无缝结合 约定优于配置 功能强大:RESTful.数据验证.格式化.本地化.主题等 简洁灵

  • 如何从零开始利用js手写一个Promise库详解

    前言 ECMAScript 是 JavaScript 语言的国际标准,JavaScript 是 ECMAScript 的实现.ES6 的目标,是使得 JavaScript 语言可以用来编写大型的复杂的应用程序,成为企业级开发语言. 概念 ES6 原生提供了 Promise 对象. 所谓 Promise,就是一个对象,用来传递异步操作的消息.它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理. 三道思考题 刚开始写前端的时候,处理异步请求经常用

  • Spring体系的各种启动流程详解

    在介绍spring的启动之前,先来说下启动过程中使用到的几个类 基本组件 1.BeanFactory:spring底层容器,定义了最基本的容器功能,注意区分FactoryBean 2.ApplicationContext:扩展于BeanFactory,拥有更丰富的功能.例如:添加事件发布机制.父子级容器,一般都是直接使用ApplicationContext. 3.Resource:bean配置文件,一般为xml文件.可以理解为保存bean信息的文件. 4.BeanDefinition:beand

  • Python利用逻辑回归模型解决MNIST手写数字识别问题详解

    本文实例讲述了Python利用逻辑回归模型解决MNIST手写数字识别问题.分享给大家供大家参考,具体如下: 1.MNIST手写识别问题 MNIST手写数字识别问题:输入黑白的手写阿拉伯数字,通过机器学习判断输入的是几.可以通过TensorFLow下载MNIST手写数据集,通过import引入MNIST数据集并进行读取,会自动从网上下载所需文件. %matplotlib inline import tensorflow as tf import tensorflow.examples.tutori

  • 常用前端手写功能进阶示例详解

    目录 1.Promise.all 2.Promise.race 3.Promise.any 4.冒泡排序 5.选择排序 6.快速排序 7.call 8.apply 9.bind 10.instanceof 11.new 12.统计页面中所有标签的种类和个数 1.Promise.all Promise.myAll = function (promises) { return new Promise((resolve, reject) => { // promises 可以不是数组,但必须要具有 I

  • Spring security自定义用户认证流程详解

    1.自定义登录页面 (1)首先在static目录下面创建login.html 注意:springboot项目默认可以访问resources/resources,resources/staic,resources/public目录下面的静态文件 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页面</titl

  • Spring Boot Shiro auto-configure工作流程详解

    目录 01-Shiro 自动配置原理 02-自动配置类 03-Filter 相关的配置类 04-总结 01-Shiro 自动配置原理 Shiro 与 Spring Boot 集成可以通过 shiro-spring-boot-stater 实现,并能完成必要类自动装配. 实现方式是通过 Spring Boot 的自动配置机制,即 WEB-INF/spring.factories 中通过 EnableAutoConfiguration 指定了 6 个自动化配置类: org.springframewo

  • Spring Security整合Oauth2实现流程详解

    一.创建项目并导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <art

  • MyBatis集成Spring流程详解

    目录 一.Mybatis与spring集成 1. 导入pom依赖 2. 编写配置文件 3. 使用注解开发 4. 测试 5. 管理数据源 二.Aop整合pagehelper插件 要解决的问题 一.Mybatis与spring集成 1. 导入pom依赖 <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8

  • MVC+DAO设计模式下的设计流程详解

    DAO设计 : DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此,DAO层的设计首先是设计DAO的接口,然后在Spring的配置文件中定义此接口的实现类,然后就可在模块中调用此接口来进行数据业务的处理,而不用关心此接口的具体实现类是哪个类,显得结构非常清晰,DAO层的数据源配置,以及有关数据库连接的参数都在Spring的配置文件中进行配置. 在该层主要完成对象-关系映射的建立,通过这个映射,再通过访问业务对象即可实现对数据库的访问,使得开发中不必再用SQL语句编写复杂的

  • Java spring的三种注入方式详解流程

    目录 设置Spring的作用域 自动注入 @Primary Qualifier @ComponentScan不同的配置对性能的影响 懒加载 三种注入方式 字段注入(IDEA 会提示不推荐) 字段注入的bean类外部不可见 循环依赖问题 构造器注入(官方推荐) set方法注入 设置Spring的作用域 或者使用枚举值设置 单例和多里使用场景 自动注入 @Primary 一个接口有多个实现被spring管理吗,在依赖注入式,spring会不知道注入哪个实现类就会抛出NoUniqueBeanDefin

随机推荐