12 手写SpringMVC

1 概述

1、简易的实现SpringMVC中注解类的加载,和IOC、DI的实现
2、实现DispatchServlet根据RequestMapping等注解标注路径的的请求分发
3、利用策略模式和反射对请求方法的参数的处理

2 代码实现

2.1 注解类准备

  • EnjoyController
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({java.lang.annotation.ElementType.TYPE}) //作用范围:用在接口或类上
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented//明该注解将被包含在javadoc中    @Inherited:说明子类可以继承父类中的该注解
public @interface EnjoyController {
    String value() default "";
}

  • EnjoyQualifier
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnjoyQualifier {
    String value() default "";
}

  • EnjoyRequestMapping
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnjoyRequestMapping {
    //路径EnjoyRequestMapping(value)
    String value() default "";
}

  • EnjoyRequestParam
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnjoyRequestParam {
    String value() default "";
}

  • EnjoyService
@Target({java.lang.annotation.ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnjoyService {
    String value() default "";
}

2.2 Controller、Service类准备

  • Controller
@EnjoyController
@EnjoyRequestMapping("/james")
public class JamesController {
    
    @EnjoyQualifier("JamesServiceImpl")//此处为模拟依赖注入
    private JamesService jamesService;
    
    @EnjoyRequestMapping("/query")
    public void query(HttpServletRequest request, HttpServletResponse response,
            @EnjoyRequestParam("name") String name,
            @EnjoyRequestParam("age") String age) {
        
        try {
            PrintWriter pw = response.getWriter();
            String result = jamesService.query(name,age);
            pw.write(result);
        }
        catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }    
}
  • Service
@EnjoyService("JamesServiceImpl")
public class JamesServiceImpl implements JamesService {
    
    public String query(String name, String age) {
        
        return "{name="+name+",age="+age+"}";
    }
    
    public String insert(String param) {
        // TODO Auto-generated method stub
        return  "insert successful.....";
    }
    
    public String update(String param) {
        // TODO Auto-generated method stub
        return "update successful.....";
    }
    
}

2.3 DisPatchServlet类准备(重点)

/**
 * Servlet implementation class DispatcherServlet
 */
public class DispatcherServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
//承接需要IOC装载类的全类名
    List classNames = new ArrayList();
//承接IOC实例化的bean对象
    Map beans = new HashMap();
//承接Controller中方法和对应方法访问路径
    Map handlerMap = new HashMap();

    Properties prop = null;

    private static String HANDLERADAPTER = "jamesHandlerAdapter";

    /**
     * Default constructor.
     */
    public DispatcherServlet() {
        // TODO Auto-generated constructor stub
    }

    /**
     * @see Servlet#init(ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        // 1、我们要根据一个基本包进行扫描,扫描里面的子包以及子包下的类
        scanPackage("com.enjoy");

        for (String classname : classNames) {
            System.out.println(classname);
        }

        // 2、我们肯定是要把扫描出来的类进行实例化
        instance();
        for (Map.Entry entry : beans.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

        // 3、依赖注入,把service层的实例注入到controller
        ioc();

        // 4、建立一个path与method的映射关系
        HandlerMapping();
        for (Map.Entry entry : handlerMap.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        /*
         * InputStream is = this.getClass()
         * .getResourceAsStream("/config/properties/spring.properties"); prop =
         * new Properties(); try { prop.load(is); } catch (IOException e) {
         * e.printStackTrace(); }
         */

    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.doPost(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //获取到请求路径   /james-springmvc/james/query
        String uri = request.getRequestURI();
               //获取容器路径  /james-springmvc
        String context = request.getContextPath();
        //将  "/james-springmvc/james/query"  去掉"/james-springmvc"
        String path = uri.replace(context, "");
        //根据请求路径来获取要执行的方法 
        Method method = (Method) handlerMap.get(path);
        //拿到控制类
        JamesController instance = (JamesController) beans.get("/" + path.split("/")[1]);
        //处理器(处理器也做为Spring内置对象被装进beans中,因为处理器类上也标注了@enjoyService注解)
        HandlerAdapterService ha = (HandlerAdapterService) beans.get(HANDLERADAPTER);

        
        /*
         * @RequestMapping("/order")
         * order(@RequestBody String params, @RequestHeader @RequestParam String param1){//参数有多种类型接收方式
         * }
         */
        
        
        
        //通过参数解析器将参数解析并获取对应参数的值,将参数放在args数组中返回,并传给反射方法来调用目标方法
        Object[] args = ha.hand(request, response, method, beans);

        try {
            method.invoke(instance, args);
            // method.invoke(instance, new
            // Object[]{request,response,null});//拿参数
            
            /*如果有多个参数类型,就得这样写了(可用策略模式,省去以下代码)
             * if(ParamType == HttpServletRequest){
                
            }else if(ParamType == @RquestHeader){
                
            }else
            *用策略模式实现(把粒度控制得更细),新建 JamesHandlerAdapter
            */
            
            
            
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /*
         * 将目标方法和方法访问路径关系传入handlerMap中保存
         * 
         */
    private void HandlerMapping() {
        if (beans.entrySet().size() <= 0) {
            System.out.println("没有类的实例化!");
            return;
        }

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

            Class clazz = instance.getClass();
            // 拿所有Controller的类
            if (clazz.isAnnotationPresent(EnjoyController.class)) {
                //@com.enjoy.james.annotation.EnjoyRequestMapping(value=/james)
                EnjoyRequestMapping requestMapping = (EnjoyRequestMapping) clazz
                        .getAnnotation(EnjoyRequestMapping.class);
                // 获取Controller类上面的EnjoyRequestMapping注解里的请求路径
                String classPath = requestMapping.value();
                // 获取控制类里的所有方法
                Method[] methods = clazz.getMethods();
                // 获取方法上的EnjoyRequestMapping设置的路径,与方法名建立映射关系
                for (Method method : methods) {
                    //判断哪些方法上使用EnjoyRequestMapping路径注解
                    if (method.isAnnotationPresent(EnjoyRequestMapping.class)) {
                        //@com.enjoy.james.annotation.EnjoyRequestMapping(value=/query)
                        EnjoyRequestMapping methodrm = (EnjoyRequestMapping) method
                                .getAnnotation(EnjoyRequestMapping.class);
                        String methodPath = methodrm.value();
                        // 把方法上与路径建立映射关系( /james/query--->public void com.enjoy.james.controller.JamesController.query )
                        handlerMap.put(classPath + methodPath, method);
                    } else {
                        continue;
                    }
                }
            }
        }
    }

    // 初始化IOC容器
    private void ioc() {

        if (beans.entrySet().size() <= 0) {
            System.out.println("没有类的实例化!");
            return;
        }
        //将实例化好的bean遍历,
        for (Map.Entry entry : beans.entrySet()) {
            Object instance = entry.getValue();//获取bean实例

            Class clazz = instance.getClass();//获取类,用来判断类里声明了哪些注解(主要是针对控制类里的判断,比如使用了@Autowired  @Qualifier,对这些注解进行解析)
            //判断该类是否使用了EnjoyController注解
            if (clazz.isAnnotationPresent(EnjoyController.class)) {
                Field[] fields = clazz.getDeclaredFields();// 拿到类里面的属性
                // 判断是否声明了自动装配(依赖注入)注解,比如@Autrowired @Qualifier
                for (Field field : fields) {
                    if (field.isAnnotationPresent(EnjoyQualifier.class)) {
                        EnjoyQualifier qualifier = (EnjoyQualifier) field.getAnnotation(EnjoyQualifier.class);
                        //拿到@EnjoyQualifier("JamesServiceImpl")里的指定要注入的bean名字"JamesServiceImpl"
                        String value = qualifier.value();

                        field.setAccessible(true);//将属性设置为允许修改,否则不能注入
                        try {
                            // 从MAP容器中获取"JamesServiceImpl"对应的bean,并注入实例控制层bean,解决依赖注入
                            field.set(instance, beans.get(value));
                        } catch (IllegalArgumentException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    } else {
                        continue;
                    }
                }
            } else {
                continue;
            }
        }
    }

    private void instance() {
        if (classNames.size() <= 0) {
            System.out.println("包扫描失败!");
            return;
        }
        //遍历扫描到的class文件,将需要实例化的类(加了注解的类)进行反射创建对象(像注解就不需要实例化)
        for (String className : classNames) {
            // com.enjoy.james.service.impl.JamesServiceImpl.class
            String cn = className.replace(".class", "");

            try {
                Class clazz = Class.forName(cn);//拿到class类,用来实例化
                // 将扫描到的类,获取类名,并判断是否标记了EnjoyController注解
                if (clazz.isAnnotationPresent(EnjoyController.class)) {
                    EnjoyController controller = (EnjoyController) clazz.getAnnotation(EnjoyController.class);
                    Object instance = clazz.newInstance();
                    //获取对应的请求路径"/james"
                    EnjoyRequestMapping requestMapping = (EnjoyRequestMapping) clazz
                            .getAnnotation(EnjoyRequestMapping.class);
                    String rmvalue = requestMapping.value();//得到"/james"请求路径
                    //用路径做为key,对应value为实例化对象
                    beans.put(rmvalue, instance);
                } else if (clazz.isAnnotationPresent(EnjoyService.class)) {
                    //获取当前clazz类的注解(通过这个注解可得到当前service的id)  @com.enjoy.james.annotation.EnjoyService(value=JamesServiceImpl)
                    EnjoyService service = (EnjoyService) clazz.getAnnotation(EnjoyService.class);
                    Object instance = clazz.newInstance();
                    //put(JamesServiceImpl,instance)
                    beans.put(service.value(), instance);
                } else {
                    continue;
                }
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private void scanPackage(String basePackage) {
        //扫描编译好的类路径下所有的类
        URL url = this.getClass().getClassLoader().getResource("/" + replaceTo(basePackage));

        String fileStr = url.getFile();

        File file = new File(fileStr);
        //拿到所有类com.enjoy下的james文件夹
        String[] filesStr = file.list();

        for (String path : filesStr) {
            File filePath = new File(fileStr + path);//扫描com.enjoy.james下的所有class类

            //递归调用扫描,如果是路径,继续扫描
            if (filePath.isDirectory()) {
                // com.enjoy.james
                scanPackage(basePackage + "." + path);
            } else {
                classNames.add(basePackage + "." + filePath.getName());//如果是class文件则加入List集合(待生成bean)
            }
        }
    }

    private String replaceTo(String basePackage) {
        return basePackage.replaceAll("\\.", "/");
    }

}
  • JamesHandlerAdapter
    可以将目标方法中的参数进行处理,在请求时将request传入的请求参数根据目标方法method定义的参数类型进行处理,传出一个Object[]对象数组,该数组在框架利用反射调用目标方法时作为参数传入,例如method.invoke(instance, args); 而对于不用的参数类型,我们可以用策略模式来处理
@EnjoyService("jamesHandlerAdapter")
public class JamesHandlerAdapter implements HandlerAdapterService {
    //对method方法里的参数进行处理
    public Object[] hand(HttpServletRequest request,//需要传入request,拿请求的参数
            HttpServletResponse response, Method method,//执行的方法,可以拿到当前待执行的方法有哪些参数
            Map beans) {
        //拿到当前待执行的方法有哪些参数
        Class[] paramClazzs = method.getParameterTypes();
        //根据参数的个数,new 一个参数的数组,将方法里的所有参数赋值到args来
        Object[] args = new Object[paramClazzs.length];
        
        //1、要拿到所有实现了ArgumentResolver这个接口的实现类
        Map argumentResolvers = getBeansOfType(beans,
                ArgumentResolver.class);
        
        int paramIndex = 0;
        int i = 0;
        //对每一个参数进行循环,每个参数都有特殊处理(比如RequestParam的处理类为 RequestParamArgumentResolver )
        for (Class paramClazz : paramClazzs) {
            //哪个参数对应了哪个参数解析类,用策略模式来找
            for (Map.Entry entry : argumentResolvers.entrySet()) {
                ArgumentResolver ar = (ArgumentResolver)entry.getValue();
                
                if (ar.support(paramClazz, paramIndex, method)) {
                    args[i++] = ar.argumentResolver(request,
                            response,
                            paramClazz,
                            paramIndex,
                            method);
                }
            }
            paramIndex++;
        }
        
        return args;
    }
    //获取实现了ArgumentResolver接口的所有实例(其实就是每个参数的注解实例)
    private Map getBeansOfType(Map beans,//所有bean
            Class intfType) //类型的实例
    {
        
        Map resultBeans = new HashMap();
        
        for (Map.Entry entry : beans.entrySet()) {
            //拿到实例-->反射对象-->它的接口(接口有多实现,所以为数组)
            Class[] intfs = entry.getValue().getClass().getInterfaces();
            
            if (intfs != null && intfs.length > 0) {
                for (Class intf : intfs) {
                    //接口的类型与传入进来的类型一样,把实例加到resultBeans里来
                    if (intf.isAssignableFrom(intfType)) {
                        resultBeans.put(entry.getKey(), entry.getValue());
                    }
                }
            }
        }
        
        return resultBeans;
    }
    
}

  • ArgumentResolver
    参数解析接口类
public interface ArgumentResolver {
    
    public boolean support(Class type, int paramIndex, Method method);
    
    //参数解析方法,每一个参数都会调用一次这个方法
    public Object argumentResolver(HttpServletRequest request,
            HttpServletResponse response, Class type, 
            int paramIndex,//参数索引下坐标,有很多注解,你得知道是哪个参数的注解,每个参数的索引顺序不一样
            Method method);
}
  • RequestParamArgumentResolver
    参数解析实现类,解析对应的@EnjoyRequestParam注解
@EnjoyService("requestParamArgumentResolver")
//解析声明注解为RequestParam, 获取注解的值
public class RequestParamArgumentResolver implements ArgumentResolver {
    //判断传进来的参数是否为EnjoyRequestParam
    public boolean support(Class type, int paramIndex, Method method) {
        
        Annotation[][] an = method.getParameterAnnotations();
        
        Annotation[] paramAns = an[paramIndex];
        
        for (Annotation paramAn : paramAns) {
            //判断传进的paramAn.getClass()是不是 EnjoyRequestParam 类型
            if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                return true;
            }
        }
        return false;
    }
    //参数解析,并获取注解的值
    public Object argumentResolver(HttpServletRequest request,
            HttpServletResponse response, Class type, int paramIndex,
            Method method) {
        //获取方法中参数的所有注解并放入二维数组中
        Annotation[][] an = method.getParameterAnnotations();
        //根据paramIndex获取对应的参数注解信息
        Annotation[] paramAns = an[paramIndex];
        
        for (Annotation paramAn : paramAns) {
            //判断该注解是否为EnjoyRequestParam类注解
            if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                EnjoyRequestParam rp = (EnjoyRequestParam)paramAn;
                
                String value = rp.value();
                //获取参数注解中标注的参数别名,根据别名获取请求中的参数值并返回
                return request.getParameter(value);
            }
        }
        
        return null;
    }
    
}

你可能感兴趣的:(12 手写SpringMVC)