【源码】手写SpringMVC框架源码(二)

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <!--定义MyDispatcherServlet类-->
        <servlet-class>com.panda.servlet.MyDispatcherServlet</servlet-class>
        <!--设置为1后,httpServlet初始化调用init方法-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <!--拦截所有请求 /*-->
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

注解类

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
    String value() default "";
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
    String value() default "";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
    String value() default "";
}

entity/service/controller类

public class User {

    private String id;
    private String username;
    private String password;

    public User(String id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
public interface UserService {

    public String get(String name);
}
@MyService("userService")
public class UserServiceImpl implements UserService {
    @Override
    public String get(String name) {
        return "查询到姓名为:" + name;
    }
}
@MyController
public class UserController {

    @MyAutowired
    private UserService userService;

    @MyRequestMapping("/index")
    public String index(HttpServletRequest request, HttpServletResponse response,
                        @MyRequestParam("name") String name) throws IOException {
        String res = userService.get(name);
        System.out.println(name + "=>" + res);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(res);
        return "index";
    }
}

DispathServlet

public class MyDispatcherServlet extends HttpServlet {
    //类名列表
    private List<String> classNames = new ArrayList<String>();

    //ioc容器,Map<类名,类实例>
    private Map<String, Object> ioc = new HashMap<String, Object>();

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

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init...");

        //1.扫描包,将包内所有对象类名存储到list中
        doScanner("com.panda.web");
        System.out.println("========第一步:扫描文件包==========");
        classNames.forEach(value -> System.out.println("保存类名:" + value));

        //2.IOC实现,单例模式,new出对象,放入Map中
        doInstance();
        System.out.println("========第二步:IOC==========");
        ioc.forEach((key, value) -> System.out.println("IOC名称:" + key + ":" + value));

        //3.DI实现,依赖注入。实现注释MyAutowired的类,进行赋值
        doAutoWired();

        //4.将requestMapping注释的method方法,放入到Arraylist中
        initHandlerMapping();

    }

    private void doScanner(String packageName) {
        //1.获取当前class的文件路径下的com.panda.web下的文件路径,
        URL resource =
                this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        File classDir = new File(resource.getFile());
        for (File classFile : classDir.listFiles()) {
            if (classFile.isDirectory()) {
                //2.递归,如果遇到文件夹,递归扫描此文件夹下的class
                doScanner(packageName + "." + classFile.getName());
            } else {
                //3.文件,直接获取文件名称,存放到List中,形式为com.panda.web.controller.UserController
                String className = (packageName + "." + classFile.getName()).replace(".class", "");
                classNames.add(className);
            }
        }
    }

    private void doInstance() {
        if (classNames.isEmpty()) {
            return;
        }
        try {
            //循环所有扫描出来的类名,将其实例化,并放入ioc容器中
            for (String className : classNames) {
                //类反射机制,获取clazz
                Class<?> clazz = Class.forName(className);
                //如果有MyController注解,说明为controller类,可初始化
                if (clazz.isAnnotationPresent(MyController.class)) {
                    //ioc的beanId的格式为类名,且首字母小写
                    String beanName = lowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName, clazz.newInstance());
                    //如果有MyService注解,说明为service类,可初始化
                } else if (clazz.isAnnotationPresent(MyService.class)) {

                    //1.如果自己起了名字,优先使用自己的
                    //2.如果没起名字,使用首字母小写
                    //3.如果自动注入的为接口,使用接口的全称

                    MyService service = clazz.getAnnotation(MyService.class);
                    String beanName = service.value();
                    if ("".equals(beanName)) {
                        beanName = lowerFirstCase(clazz.getSimpleName());
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                    //service注解的时候,如实现接口时,再put一次
                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (Class<?> i : interfaces) {
                        ioc.put(i.getName(), instance);
                    }
                } else {
                    //如无注解,无需初始化。
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doAutoWired() {
        if (ioc.isEmpty()) {
            return;
        }
        //注入就是对类中定义的一些属性,进行赋值
        //不管是公有的还是私有的,都需要赋值
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //getDeclaredFields获取类中,所有的属性值
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                //如果属性上,无注解,直接循环下一次。如有MyAutowired才进行操作
                if (!field.isAnnotationPresent(MyAutowired.class)) {
                    continue;
                }
                MyAutowired autowired = field.getAnnotation(MyAutowired.class);
                String beanName = autowired.value();
                if ("".equals(beanName)) {
                    //如果beanName是空,使用默认接口注入
                    beanName = field.getType().getName();
                }
                //开始赋值,setAccessible - 强制访问
                field.setAccessible(true);

                //通过反射机制,强制赋值。将ioc初始化实例后的对象赋值到MyAutowired属性值上
                try {
                    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();
            //如果类的注解不是MyController,则继续循环,此方法只处理controller层
            if (!clazz.isAnnotationPresent(MyController.class)) {
                continue;
            }
            String baseUrl = "";
            //如果类注解有MyRequestMapping,说明定义了根路径,将此路径存储起来
            if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
                baseUrl = requestMapping.value();
            }
            //获取controller类的所有方法,并变量
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                //如方法无注解,则继续循环,目的获取到所有MyRequestMapping注解的方法
                if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                    continue;
                }
                //获取MyRequestMapping注解的路径,并与根路径进行拼接
                MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
                String url = (baseUrl + requestMapping.value()).replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(url);
                //url和method的方法,封装到handler类中
                handlerMapping.add(new Handler(pattern, entry.getValue(), method));
                System.out.println("mapped:" + url + "=>" + method);
            }
        }
    }


    private String lowerFirstCase(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }


    //内部类
    private class Handler {
        protected Object controller;//保存controller的实例对象
        protected Method method;//保存controller类的method方法
        protected Pattern pattern;//url正则表达是
        protected Map<String, Integer> paramIndexMapping;//param注解的map存储

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

        private void putParamIndexMapping(Method method) {
            Annotation[][] pa = method.getParameterAnnotations();
            for (int i = 0; i < pa.length; i++) {
                for (Annotation a : pa[i]) {
                    if (a instanceof MyRequestParam) {
                        String paramName = ((MyRequestParam) a).value();
                        if (!"".equals(paramName)) {
                            paramIndexMapping.put(paramName, i);
                        }
                    }
                }
            }
            Class<?>[] paramTypes = method.getParameterTypes();
            for (int i = 0; i < paramTypes.length; i++) {
                Class<?> type = paramTypes[i];
                if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                    paramIndexMapping.put(type.getName(), i);
                }
            }
        }
    }


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

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        doDispatcher(req, res);
    }

    public void doDispatcher(HttpServletRequest req, HttpServletResponse res) {
        try {
            //获取request中的url访问路径和参数,从ArrayList的handlerMapping中查找。找到后,执行method方法
            Handler handler = getHandler(req);
            if (handler == null) {
                res.getWriter().write("404 not found.");
                return;
            }
            Class<?>[] paramTypes = handler.method.getParameterTypes();
            Object[] paramValues = new Object[paramTypes.length];
            //获取访问链接中的params,并对其循环
            Map<String, String[]> params = req.getParameterMap();
            for (Map.Entry<String, String[]> param : params.entrySet()) {
                String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "");
                if (!handler.paramIndexMapping.containsKey(param.getKey())) {
                    continue;
                }
                int index = handler.paramIndexMapping.get(param.getKey());
                paramValues[index] = convert(paramTypes[index], value);
            }
            int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[reqIndex] = req;
            int resIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[resIndex] = res;
            //method方法执行
            handler.method.invoke(handler.controller, paramValues);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //通过request的url链接,寻找handler对象中是否有对应的method
    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;
            }
            //url有对应的method,返回handler
            return handler;
        }
        //url无对应的method,返回null
        return null;
    }

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

码云地址:[https://gitee.com/xumiaofeng/springmvc]

发布了15 篇原创文章 · 获赞 9 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章