手写SpringMVC&IOC

手写SpringMVC&IOC

    • 1. 项目结构
    • 2. 编写我们的自定义注解
    • 3. 编写Service接口及其实现类
    • 4. 编写Controller
    • 5. 编写自定义DispatcherServlet(重点来了!)
    • 6. 编写web.xml配置DispatcherServiet
    • 项目地址

1. 项目结构

手写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 classNames = new ArrayList();

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

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


    //初始化方法,最先执行
    @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();

        /**
         * 经过上面步骤,已经将我们的依赖都注入了
* 接下来就是将URI与我们Controller类中的方法建立映射关系
* 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()方法将对象加载到容器中
* 以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 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 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



    lyb
    
        dispatcherServlet
        cn.luckybin.servlet.DispatcherServlet
        
        0
    
    
        dispatcherServlet
        
        /
    


项目地址

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

你可能感兴趣的:(Java,Spring,SpringMVC,SpringIOC,源码,Java反射,Java,Spring,SpringMVC,SpringIOC,源码)