手写简易版spring MVC框架

自己手写一个简易版Spring MVC主要是为了强化自己对Spring框架的理解
1.首先新建一个项目:
这里写图片描述

2.编辑pom.xml文件,主要是增加了servlet-api依赖和jetty插件

<?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.wlj.myframework</groupId>
  <artifactId>mvcframework</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>mvcframework 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>
    <servlet.api.version>2.4</servlet.api.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>${servlet.api.version}</version>
      <scope>provided </scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>mvcframework</finalName>
    <plugins>
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.3.7.v20160115</version>
        <configuration>
          <scanIntervalSeconds>10</scanIntervalSeconds>
          <httpConnector>
            <port>8080</port>
          </httpConnector>
          <webApp>
            <contextPath>/wlj</contextPath>
          </webApp>

        </configuration>
      </plugin>
    </plugins>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->



        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.7.0</version>
        </plugin>
        .............
      </plugins>
    </pluginManagement>
  </build>
</project>

3.新建java、resources等目录:
这里写图片描述
demo目录下是模拟业务代码,framework是框架目录

4.在servlet目录下新建DispatherServlet类:


public class WLJDispatherServlet extends HttpServlet{

    private Properties contextConfig=new Properties();

    private List<String> classNames=new ArrayList<>();

    //IOC容器
    private Map<String,Object> ioc=new HashMap<>();

    private List<Handler> handlerMapping=new ArrayList<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            dispatch(req, resp);
        } catch (IOException e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception");
        }
    }


    private void dispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        Handler handler = getHandler(req);
        if(handler==null){
            resp.getWriter().write("404 NOT FOUND!!");
            return;
        }
        //获取匹配的方法的参数类型列表
        Class<?>[] parameterTypes = handler.methed.getParameterTypes();

        //保存所有需要自动赋值的参数值
        Object[] paramValues = new Object[parameterTypes.length];

        //客户端请求的参数map
        Map<String,String[]> params = req.getParameterMap();
        for (Map.Entry<String, String[]> entry : params.entrySet()) {
            System.out.print("dispatch:param[]->"+Arrays.toString(entry.getValue()));

            System.out.println();
            String value = Arrays.toString(entry.getValue()).replaceAll("\\[|\\]","").replaceAll(",\\s",",");

            if(!handler.paramIndexMapping.containsKey(entry.getKey())){ continue;}

            //若找到匹配参数则开始填充参数值
            Integer index = handler.paramIndexMapping.get(entry.getKey());
            paramValues[index]=convert(parameterTypes[index],value);
        }

        //设置方法中的request和response
        Integer reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
        paramValues[reqIndex]=req;
        Integer respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
        paramValues[respIndex]=resp;

        try {

            handler.methed.invoke(handler.controller,paramValues);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doLoadConfig(String contextConfigLocation){
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            if(null!=is){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doScanner(String scanPackage){
        System.out.println("scanPackage:"+scanPackage);
        //文件路径转化为包路径
        URL url= this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        System.out.printf("url:"+url.toString());

        File classDir = new File(url.getFile());

        for(File file:classDir.listFiles()){

            //是文件夹
            if(file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }else{
                //存扫描到的类名
                String className=scanPackage+"."+file.getName().replace(".class","");
                classNames.add(className);
            }
        }


    }

    //实例化扫描到的类
    private void doInstance() throws IllegalAccessException, InstantiationException {
        if(classNames.isEmpty()){
            return;
        }
        try {
            for (String name : classNames) {
                Class<?> clazz = Class.forName(name);

                //不是所有的类都要初始化,只认加了注解的类

                if(clazz.isAnnotationPresent(WLJController.class)){
                    //key默认类名首字母小写
                    String beanName = lowerFirstCase(clazz.getName());
                    ioc.put(beanName,clazz.newInstance());

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

                    //1.默认采用首字母小写
                    //2.如果自定义了名字,优先使用自定义的名字
                    //3.根据接口类型来赋值
                    WLJService annotation = clazz.getAnnotation(WLJService.class);
                    String beanName = annotation.value();
                    //未定义自定义名字
                    if(!"".equals(beanName.trim())){
                        beanName=lowerFirstCase(beanName);
                    }
                    //某接口实现类的entry
                    ioc.put(beanName,clazz.newInstance());

                    //某接口的entry,不过其实例就是上面的实例
                    for(Class<?> i:clazz.getInterfaces()){

                        ioc.put(i.getName(),clazz.newInstance());
                    }
                }else{
                    continue;
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private void doAutowired(){
        if(ioc.isEmpty()){
            return;
        }
        //循环ioc容器,对需要注入的属性进行注入
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //依赖注入,无论该属性的访问规则怎样,因为是反射
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if(!field.isAnnotationPresent(WLJAutowired.class)){
                    continue;
                }
                WLJAutowired autowired = field.getAnnotation(WLJAutowired.class);

                String beanName=autowired.value().trim();
                if("".equals(beanName)){
                    beanName=field.getType().getName();
                }

                //暴力访问(只要加了注解,我就访问)
                field.setAccessible(true);

                try {
                    //field.set的第二个参数为: entry.getValue()set方法的参数
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    private void initHandlerMapping(){
        if(ioc.isEmpty()){
            return;
        }
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz=entry.getValue().getClass();
            if(!clazz.isAnnotationPresent(WLJController.class)){
                continue;
            }
            String baseUrl="";
            if(clazz.isAnnotationPresent(WLJRequestMapping.class)){
                WLJRequestMapping annotation = clazz.getAnnotation(WLJRequestMapping.class);
                baseUrl=annotation.value().trim();
            }

            //扫描所有公共方法
            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(WLJRequestMapping.class)){
                    continue;
                }

                WLJRequestMapping annotation = method.getAnnotation(WLJRequestMapping.class);
                String regex=("/"+baseUrl+annotation.value()).replaceAll("/+","/");
                Pattern compile = Pattern.compile(regex);
                handlerMapping.add(new Handler(entry.getValue(),method,compile));

                System.out.println("Mapping:"+regex+","+method);
            }
        }
    }


    private String lowerFirstCase(String str){
        char[] chars = str.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }
    @Override
    public void init(ServletConfig config){
       //从这里启动
        //1.加载配置文件
        System.out.println("contextConfigLocation:"+config.getInitParameter("contextConfigLocation"));
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2.扫描所有相关的类
        doScanner(contextConfig.getProperty("scanPackage"));
        //3.初始化所有相关的类

        try {
            doInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }


        //4.自动注入
        doAutowired();

        //================以下mvc内容======================
        //5.初始化handlerMapping
        initHandlerMapping();

        System.out.println("WLJ Spring init completed..");
    }



    private Handler getHandler(HttpServletRequest req){
        if(handlerMapping.isEmpty()){
            return null;
        }
        String url=req.getRequestURI();
        String contextPath = req.getContextPath();

        url=url.replace(contextPath,"").replaceAll("/+","/");

        for (Handler handler : handlerMapping) {
            Matcher matcher = handler.pattern.matcher(url);
            //没有匹配成功,下一个
            if(!matcher.matches()){
                continue;
            }
            return handler;
        }
        return null;
    }

    private Object convert(Class<?> type,String value){
        if(type==Integer.class){
            return Integer.valueOf(value);
        }
        return value;
    }

    private class Handler{
        protected Object controller;    //保存方法对应的实例
        protected Method methed;        //保存映射的方法
        protected Pattern pattern;
        protected Map<String,Integer> paramIndexMapping;    //参数顺序

        protected Handler(Object controller, Method methed, Pattern pattern) {
            this.controller=controller;
            this.methed = methed;
            this.pattern = pattern;
            this.paramIndexMapping=new HashMap<>();
            putParamIndexMapping(methed);
        }

        private void putParamIndexMapping(Method methed){
            //提取方法中加了注解的参数
            //为什么是二维数组呢?因为每个参数可以有多个注解修饰
            Annotation[][] params = methed.getParameterAnnotations();
            //params.length=3  虽然前两个参数没有注解修饰,但是他们的params[i]无元素
            //params[0][]空  params[1][]空  params[2][0]=WLJRequestParam
            for (int i=0;i<params.length;i++) {
                for (Annotation a : params[i]) {
                    if(a instanceof WLJRequestParam){
                        String paramName = ((WLJRequestParam) a).value();
                        if(!"".equals(paramName.trim())){
                            paramIndexMapping.put(paramName,i);
                        }
                    }
                }
            }

            Class<?>[] parameterTypes = methed.getParameterTypes();
            for(int i=0;i<parameterTypes.length;i++){
                Class<?> type = parameterTypes[i];
                if(type==HttpServletRequest.class||type==HttpServletResponse.class){
                    paramIndexMapping.put(type.getName(),i);
                }
            }
        }
    }
}

5.在WEB-INF的web.xml中配置该servlet:
这里写图片描述
(红色字样不用管)
在Resources目录下新建application.properties文件:
文件内容只有 :
scanPackage=com.wlj.demo

6.自定义注解
@WLJAutowired:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJAutowired {

    String value() default "";
}

@WLJController:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJController {

    String value() default "";
}

@WLJRequestMapping:

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJRequestMapping {

    String value() default "";
}

@WLJRequestParam:

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJRequestParam {

    String value() default "";
}

@WLJService:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WLJService {

    String value() default "";
}

==================至此结束,框架代码结束=========================
业务代码:
在action目录下新建DemoAction类:

@WLJController
@WLJRequestMapping("/demo")
public class DemoAction {

    @WLJAutowired
    private IDemoService demoService;


    @WLJRequestMapping("/query")
    public void query(HttpServletRequest req, HttpServletResponse resp,@WLJRequestParam("name") String name){

        String res=demoService.get(name);
        try {
            resp.getWriter().write(res);
        }catch (IOException e){
            e.printStackTrace();
        }

    }
}

新建service接口以及实现类:

public interface IDemoService {

    String get(String name);
}


@WLJService
public class DemoServce implements IDemoService {
    @Override
    public String get(String name) {
        return "My Name is "+name;
    }
}

================================================
启动jetty容器
这里写图片描述

在浏览器中输入地址测试:
这里写图片描述
这里写图片描述

OK,Mission Complete!!!!!!!!!!!!

猜你喜欢

转载自blog.csdn.net/qq_33535433/article/details/80934759