手写SpringMVC&IOC

1. 项目结构

项目结构

2. 编写我们的自定义注解

@AutoWired

package cn.luckybin.annaotation;

import java.lang.annotation.*;

/**
 * 自动注入
 */
@Target({ElementType.FIELD})//修饰字段
@Retention(RetentionPolicy.RUNTIME) //运行时能获取
@Documented//生成文档
public @interface EnjoyAutowired {
    String value() default "";
}

@Controller

package cn.luckybin.annaotation;

import java.lang.annotation.*;

/**
 * 控制层
 */
@Target({ElementType.TYPE})//修饰字段
@Retention(RetentionPolicy.RUNTIME) //运行时能获取
@Documented//生成文档
public @interface EnjoyController {
    String value() default "";
}

@Service

package cn.luckybin.annaotation;

import java.lang.annotation.*;

/**
 * 业务层
 */
@Target({ElementType.TYPE})//修饰字段
@Retention(RetentionPolicy.RUNTIME) //运行时能获取
@Documented//生成文档
public @interface EnjoyService {
    String value() default "";
}

@RequestMapping

package cn.luckybin.annaotation;

import java.lang.annotation.*;

/**
 * URI
 */
@Target({ElementType.TYPE, ElementType.METHOD})//修饰字段
@Retention(RetentionPolicy.RUNTIME) //运行时能获取
@Documented//生成文档
public @interface EnjoyRequestMapping {
    String value() default "";
}

@RequestParam

package cn.luckybin.annaotation;

import java.lang.annotation.*;

/**
 * 请求参数
 */
@Target({ElementType.PARAMETER})//修饰字段
@Retention(RetentionPolicy.RUNTIME) //运行时能获取
@Documented//生成文档
public @interface EnjoyRequestParam {
    String value() default "";
}

3. 编写Service接口及其实现类

LybService.java

package cn.luckybin.service;

public interface LybService {
    String query(String name, String tag);
}

LybServiceImpl.java

package cn.luckybin.service.impl;

import cn.luckybin.annaotation.EnjoyService;
import cn.luckybin.service.LybService;

@EnjoyService(value = "LybServiceImpl")
public class LybServiceImpl implements LybService {

    //模拟返回参数
    public String query(String name, String tag) {
        return "name=" + name + ",tag=" + tag;
    }
}

4. 编写Controller

LybController.java

package cn.luckybin.controller;

import cn.luckybin.annaotation.*;
import cn.luckybin.service.LybService;

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

/**
 * 控制层
 */
@EnjoyController(value = "LybController")
@EnjoyRequestMapping(value = "/lyb")
public class LybController {
    //注入实现类实例
    @EnjoyAutowired(value = "LybServiceImpl")
    private LybService lybService;

    //设置路由参数
    @EnjoyRequestMapping(value = "/query")
    public void query(HttpServletRequest request, HttpServletResponse response, @EnjoyRequestParam("name") String name, @EnjoyRequestParam("tag") String tag) {
        try {
            //获取响应输出
            PrintWriter printWriter = response.getWriter();
            //调用业务层,获取结果
            String result = lybService.query(name, tag);
            //将结果返回给客户端
            printWriter.write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5. 编写自定义DispatcherServlet(重点来了!)

package cn.luckybin.servlet;

import cn.luckybin.annaotation.*;
import cn.luckybin.controller.LybController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
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.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
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;

/**
 * 拦截所有Servlet
 */
public class DispatcherServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    //存放类名: cn.luckybin.xxx.xxx.class
    List<String> classNames = new ArrayList<String>();

    //以URI作为Key,Controller作为实例对象,存储在map中
    Map<String, Object> beans = new HashMap<String, Object>();

    //以URI为Key,Method作为实例对象,存储在map中
    Map<String, Object> handlerMap = new HashMap<String, Object>();


    //初始化方法,最先执行
    @Override
    public void init(ServletConfig servletConfig) {
        System.out.println("init");
        //把所有的Bean扫描---扫描所有的class文件
        scanPackage("cn.luckybin");//可以写到properties

        //此时已经将我们项目中所有的.class文件的路径都在classNames集合中
        System.out.println(classNames);

        //将classNames中存储的path路径,去掉.class后缀,使用class.forName()方法将对象加载到容器中
        doInstance();
        //此时已经将所有加了@EnjoyController,@Service的类实例存入容器中
        System.out.println(beans);

        //根据bean进行依赖注入,被@EnjoyAutowired修饰的属性会被注入对应的依赖
        doIoc();

        /**
         * 经过上面步骤,已经将我们的依赖都注入了<br/>
         * 接下来就是将URI与我们Controller类中的方法建立映射关系<br/>
         * lyb/query ---> method 建立URI与Method的映射关系
         */
        buildUrlMapping();
    }

    //递归扫描,搭建.class文件
    private void scanPackage(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()) {
                //递归
                scanPackage(basePackage + "." + path);//cn.luckybin....
            } else {
                //加入classNames
                classNames.add(basePackage + "." + filePath.getName());//cn.luckybin....class
            }
        }
    }

    /**
     * 将classNames中存储的path路径,去掉.class后缀,使用class.forName()方法将对象加载到容器中<br/>
     * 以URI为key,Controller类作为Value
     */
    private void doInstance() {
        if (classNames.size() <= 0) {
            System.out.println("包扫描失败.....");
            return;
        }

        //遍历list中所有的class类,对这些类进行实例化

        for (String className : classNames) {
            //将所有的'.class'后缀替换成空字符串
            String cn = className.replace(".class", "");
            try {
                //获取class对象
                Class<?> clazz = Class.forName(cn);//cn.luckybin.xxxcontroller

                //找出所有Controller类
                if (clazz.isAnnotationPresent(EnjoyController.class)) {
                    Object instance = clazz.newInstance();//创建控制类

                    //获取Controller类中的URI
                    EnjoyRequestMapping requestMapping = clazz.getAnnotation(EnjoyRequestMapping.class);
                    String rmvalue = requestMapping.value();// '/lyb'
                    //以URI作为Key,Controller作为实例对象,存储在map中
                    beans.put(rmvalue, instance);

                    //找出所有Service
                } else if (clazz.isAnnotationPresent(EnjoyService.class)) {
                    EnjoyService service = clazz.getAnnotation(EnjoyService.class);
                    Object instance = clazz.newInstance();
                    beans.put(service.value(), instance);
                } else {
                    continue;
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    //把service注入到controller
    public void doIoc() {
        if (beans.entrySet().size() <= 0) {
            System.out.println("没有一个实例化类");
        }
        //把map里所有的实例遍历出来
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Object instance = entry.getValue();
            //获取实例
            Class<?> clazz = instance.getClass();
            //找出Controller类
            if (clazz.isAnnotationPresent(EnjoyController.class)) {
                //获取所有属性
                Field[] fields = clazz.getDeclaredFields();
                System.out.println("fields=" + fields.length);
                for (Field field : fields) {
                    //判断属性是否需要注入
                    if (field.isAnnotationPresent(EnjoyAutowired.class)) {
                        EnjoyAutowired autowired = field.getAnnotation(EnjoyAutowired.class);
                        //获取key
                        String key = autowired.value();
//                        if (key == null || "".equals(key)) {
//                            //如果没有写key,则将属性名作为key
//                            key = field.getName();
//                        }
                        //因为它是private,需要先将权限打开,否则无法注入
                        field.setAccessible(true);
                        try {
                            //注入实例
                            field.set(instance, beans.get(key));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    } else {
                        continue;
                    }
                }
            } else {
                continue;
            }
        }
    }

    private void buildUrlMapping() {
        if (beans.entrySet().size() <= 0) {
            System.out.println("没有类的实例化.....");
            return;
        }

        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Object instance = entry.getValue();

            Class<?> clazz = instance.getClass();

            if (clazz.isAnnotationPresent(EnjoyController.class)) {
                EnjoyRequestMapping requestMapping = clazz.getAnnotation(EnjoyRequestMapping.class);
                String classUri = requestMapping.value(); // '/lyb'

                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(EnjoyRequestMapping.class)) {
                        EnjoyRequestMapping methodMapping = method.getAnnotation(EnjoyRequestMapping.class);
                        String methodUri = methodMapping.value();
                        handlerMap.put(classUri + methodUri, method); // '/lyb/query'
                    } else {
                        continue;
                    }
                }
            } else {
                continue;
            }
        }
    }

    @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 {
        //获取请求路径  '/lyb/query'  ->
        String uri = req.getRequestURI();
        String context = req.getContextPath();
        String path = uri.replace(context, "");

        Method method = (Method) handlerMap.get(path);

        //根据key = /lyb到map去拿
        LybController instance = (LybController) beans.get("/" + path.split("/")[1]);

        //获取参数
        Object arg[] = hand(req, resp, method);
        try {
            //调用该方法的实例及对应参数
            method.invoke(instance, arg);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据目标方法含有的参数,从request中取出method中需要的参数
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param method   目标方法
     * @return
     */
    private static Object[] hand(HttpServletRequest request, HttpServletResponse response, Method method) {
        //拿到当前执行的方法有哪些参数
        Class<?>[] paramClazzs = method.getParameterTypes();
        //根据参数的个数,new 一个参数的数组,将方法里的所有参数赋值到args来
        Object[] args = new Object[paramClazzs.length];

        int args_i = 0;
        int index = 0;
        for (Class<?> paramClazz : paramClazzs) {
            //判断是否为ServletRequest的子类
            if (ServletRequest.class.isAssignableFrom(paramClazz)) {
                args[args_i++] = request;
            }
            if (ServletResponse.class.isAssignableFrom(paramClazz)) {
                args[args_i++] = response;
            }
            //获取该方法中所有带有注解的参数
            Annotation[] paramAns = method.getParameterAnnotations()[index];
            if (paramAns.length > 0) {
                for (Annotation paramAn : paramAns) {
                    //判断是否为param注解
                    if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                        EnjoyRequestParam rp = (EnjoyRequestParam) paramAn;
                        //从request对象中找到注解里的参数
                        args[args_i++] = request.getParameter(rp.value());
                    }
                }
            }
            index++;
        }
        return args;
    }
}

6. 编写web.xml配置DispatcherServiet

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>lyb</display-name>
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>cn.luckybin.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>

项目地址

码云
代码若有不足之处还望指出,虚心接受各位大佬指点!

发布了9 篇原创文章 · 获赞 0 · 访问量 631

猜你喜欢

转载自blog.csdn.net/luckyman98/article/details/89504830