自己动手实现springmvc

IOC功能:就是一个hashmap里面放着各种类,俗称bean

Tomcat:tomcat启动时将需要要用到的类放到IOC容器里面去,就是那些带注解的(Controller,Autowired,Service,RequestMapping)

注解:就是自己定义几个注解,包括什么时候用(在tomcat启动时)

本节内容有一小部分内容没有实现,对于spring和springmvc源码有个大概了解

开始动手实现springmvc

一.项目结构:

自己动手实现springmvc

不用解释了,很简单了 

 

然后是pom文件,放了一个httpservlet的依赖

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jsp-api</artifactId>
  <version>2.0</version>
  <scope>provided</scope>
</dependency>

然后是web.xml配置,定义DispatcherServlet(自己定义的,不是spring自带的那个)最先初始化

web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>com.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>
</web-app>

二.自定义注解

自己动手实现springmvc

 

首先看哪EnjoyController,模仿@Controller这个注解

package com.annotation;
import java.lang.annotation.*;

@Target(ElementType.TYPE) //注解作用范围,比如:字段上
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented  //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyController {
    String value() default "";//注解后面的那个括号里可以为空
}

EnjoyService,模仿@Service

package com.annotation;
import java.lang.annotation.*;

@Target(ElementType.TYPE) //注解作用范围,比如:类
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented  //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyService {
    String value() default "";//注解后面的那个括号里可以为空
}

 EnjoyRequestMapping

package com.annotation;
import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD}) //注解作用范围,比如:类,方法上
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented  //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyRequestMapping {
    String value() default "";//注解后面的那个括号里可以为空
}

 EnjoyRequestParam

package com.annotation;
import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD}) //注解作用范围,比如:类,方法上
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented  //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyRequestMapping {
    String value() default "";//注解后面的那个括号里可以为空
}

EnjoyAutowired

package com.annotation;
import java.lang.annotation.*;

@Target(ElementType.FIELD) //注解作用范围,比如:字段上
@Retention(RetentionPolicy.RUNTIME) //系统运行时通过反射过的
@Documented  //javadoc
/*
* 注解类可以被继承,只需要声明@Inherited就可以*/
public @interface EnjoyAutowired {
    String value() default "";//注解后面的那个括号里可以为空
}

三.Controller

自己动手实现springmvc

就这一个controller

package com.controller;

import com.annotation.EnjoyAutowired;
import com.annotation.EnjoyController;
import com.annotation.EnjoyRequestMapping;
import com.annotation.EnjoyRequestParam;
import com.service.ZhangjinheService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@EnjoyController
@EnjoyRequestMapping("/zjh")
public class ZhangjinheController {
    @EnjoyAutowired("ZhangjinheServiceImpl")
    private ZhangjinheService zhangjinheService;

    @EnjoyRequestMapping("/query")
    public void query(HttpServletRequest request, HttpServletResponse response,
                      @EnjoyRequestParam("name") String name, @EnjoyRequestParam("age") String age){
        PrintWriter ps = null;
        try {
            ps = response.getWriter();
            String result = zhangjinheService.query(name,age);
            ps.write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

就实现了一个query函数,功能时向页面上输出name和age

包含了一个service,里面用的自定义注解,@EnjoyAutowired

四.Service类 

一个接口,一个实现类

自己动手实现springmvc

package com.service.impl;
import com.annotation.EnjoyService;
import com.service.ZhangjinheService;

@EnjoyService("ZhangjinheServiceImpl")
public class ZhangjinheServiceImpl implements ZhangjinheService{
    @Override
    public String query(String name, String age) {
        return "name==" + name + ";age==" + age;
    }
}

 五.Servlet类

模仿了一个DispatcherServlet(springmvc里面的那个)

自己动手实现springmvc

 这个类里面主要有这么几个方法:都放在servlet的init函数里面其运行

//扫描带注解的包
doScan("com");
doInstance();
//实现autowired
doAutowired();
urlMapping();

doScan()主要是扫描带注解的包,spring在配置的时候也有一个扫描包的选项

doInstance()将那些带注解的包都new出实例,放在一个map里面

doAutowired()将Service里面的那个字段赋值,相当于spring里面的@autowired,主要是将new出来的实例赋值给对应的字段

urlMapping()主要是负责将从浏览器里面输入的网址对应成controller里面的方法

package com.servlet;

import com.annotation.EnjoyAutowired;
import com.annotation.EnjoyController;
import com.annotation.EnjoyRequestMapping;
import com.annotation.EnjoyService;
import com.controller.ZhangjinheController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DispatcherServlet extends HttpServlet {
    List<String> classNames = new ArrayList<>();
    Map<String, Object> beans = new HashMap<>();
    Map<String, Object> handlerMap = new HashMap<>();
    //tomcat启动时要执行的内容
    public void init(ServletConfig servletConfig){
        //扫描带注解的包
        doScan("com");
        doInstance();
        //实现autowired
        doAutowired();
        urlMapping();
    }

    public void urlMapping() {
        for(Map.Entry<String, Object> entry : beans.entrySet()){
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            if(clazz.isAnnotationPresent(EnjoyController.class)){
                EnjoyRequestMapping reqMap = clazz.getAnnotation(EnjoyRequestMapping.class);
                String classPath = reqMap.value();
                Method[] methods = clazz.getMethods();
                for (Method method : methods){
                    if(method.isAnnotationPresent(EnjoyRequestMapping.class)){
                        EnjoyRequestMapping reqMap1 = method.getAnnotation(EnjoyRequestMapping.class);
                        String methodPath = reqMap1.value();
                        handlerMap.put(classPath + methodPath, method);
                    }else{
                        continue;
                    }
                }
            }
        }
    }

    public void doAutowired() {
        for(Map.Entry<String, Object> entry : beans.entrySet()){
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            if(clazz.isAnnotationPresent(EnjoyController.class)){
                Field[] fields= clazz.getDeclaredFields();
                for (Field field : fields){
                   if(field.isAnnotationPresent(EnjoyAutowired.class)){
                       EnjoyAutowired auto = field.getAnnotation(EnjoyAutowired.class);
                       String key = auto.value();
                       Object value = beans.get(key);

                       //因为当前变量是私有的,不能随便摄入,所以需要打开
                       field.setAccessible(true);

                       try {
                           field.set(key, value);
                       } catch (IllegalAccessException e) {
                           e.printStackTrace();
                       }
                   }
                }
            }
        }
    }

    public void doInstance() {
        for(String className: classNames){
            String cn = className.replace(".class", "");
            try {
                Class<?> clazz = Class.forName(cn);
                if(clazz.isAnnotationPresent(EnjoyController.class)){
                    Object value = clazz.newInstance();
                    //获得类上的注解
                    EnjoyRequestMapping reqMap = clazz.getAnnotation(EnjoyRequestMapping.class);
                    String key = reqMap.value();

                    beans.put(key, value);
                }else if(clazz.isAnnotationPresent(EnjoyService.class)){
                    Object value = clazz.newInstance();
                    EnjoyService reqMap = clazz.getAnnotation(EnjoyService.class);
                    String key = reqMap.value();
                    beans.put(key,value);
                }else {
                    continue;
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    public void doScan(String basePackage) {
        URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.","/"));
        String fileStr = url.getFile();
        File file = new File(fileStr);
        String[] filesStr = file.list();
        for (String path: filesStr){
            File filePath = new File(fileStr + path);
            if(filePath.isDirectory()){
                doScan(basePackage + "." + path);
            }else{
                classNames.add(basePackage + "." + filePath.getName());
            }
        }
    }

    @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 {

        String uri =  req.getRequestURI();// self-spirngmvc/zjh/query
        String context = req.getContextPath();//self-springmvc
        String path = uri.replace(context, "");
        Method method = (Method)handlerMap.get(path);
        ZhangjinheController instance = (ZhangjinheController)beans.get("/" + path.split("/")[1]);
        Object[] args;
        method.invoke(instance, args);
    }
}

 这里有个方法没有实现,就是那个args[]