当前位置: 代码迷 >> 综合 >> 攻下 spring mvc
  详细解决方案

攻下 spring mvc

热度:15   发布时间:2024-02-11 00:54:39.0

  之前一直对spring mvc 底层的实现原理狠挠头,直到网上听了一节课,花了一下午的时间跟着视频简单的实现了spring mvc。现整理下心得体会。

  个人对spring mvc的理解是 spring mvc = servlet + spring。其底层的实现技术主要是 自定义注解+反射。想要实现spring mvc 首先要先把spring mvc中常用的自定义注解整理实现出来。

1:常用注解

  (1):作用于Controller类上的@Controller 注解

/*** Created by zs.xu on 2020/8/13.*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsController {String value() default "";
}

(2):作用于Service实现类上的@Service注解

/*** Created by zs.xu on 2020/8/13.*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsService {String value() default "";
}

(3):作用于Controller类和其方法上用于映射路径的@RequestMapping注解

/*** Created by zs.xu on 2020/8/13.*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsRequestMapping {String value() default "";
}

(4):作用于用于自动装配实现类的@Autowired 注解

/*** Created by zs.xu on 2020/8/13.*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsAutowired {String value() default "";
}

(5):作用于Controller方法上用于接收请求参数的@RequestParam注解

/*** Created by zs.xu on 2020/8/13.*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XzsRequestParam {String value() default "";
}

2:底层核心方法都是在Servlet中实现的,所以项目在启动时,要根据servlet完成以下动作

  2.1:项目启动时servlet要初始化的一些数据

    //用于保存所有的类的全路径名List<String> classList = new ArrayList<String>();//用于保存所有的bean实例 key:注解的值,value:对象                  的 实例)Map<String,Object> beanMap = new HashMap<String, Object>();//用于保存Controller里的url对呀的方法 key:类上加方法上的RequestMapping的值,value:对呀方法Map<String,Object> urlMap = new HashMap<String, Object>();public void init(ServletConfig config){//1:扫描出项目下所有的class文件,并存对应类的全路径名,以便于后面用反射实例化对象。scanPackage("com.xzs");//2: 根据第一步扫描出的class,配合类对应使用的注解,映射出类对应的实现类 保存到map(key:注解的值,value:对象                  的 实例)中。doInstance();//3:把service实例注入到Controller对象中的service属性doIoc();//4:把Controller的方法与@RequestMapping的value做映射关系,并存下来buildUrlMapping();}

     (1):扫描出项目下所有的class文件,并存对应类的全路径名,以便于后面用反射实例化对象。

          

    //扫描"com.xzs"路径下文件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);}else{//找到class类classList.add(basePackage+"."+filePath.getName());//内容是全类名路径}}}

      (2):根据第一步扫描出的class,配合类对应使用的注解,映射出类对应的实现类 保存到map(key:注解的值,value:对象                  的 实例)中。

    //根据扫描出的list全类名,进行实例化private void doInstance() {if (null==classList ||classList.size()<=0){System.out.println("包扫描失败");return;}for (String className : classList){className = className.replace(".class","");try {Class<?> classZ = Class.forName(className);//判断有没有用注解if (classZ.isAnnotationPresent(XzsController.class)){Object instance = classZ.newInstance();//创建控制类实例化对象XzsRequestMapping requestMapping = classZ.getAnnotation(XzsRequestMapping.class);String rmValue = requestMapping.value();beanMap.put(rmValue,instance);}else if (classZ.isAnnotationPresent(XzsService.class)){XzsService service = classZ.getAnnotation(XzsService.class);Object instance = classZ.newInstance();beanMap.put(service.value(),instance);}else{continue;}} catch (ClassNotFoundException e) {e.printStackTrace();}catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}

     (3):把service实例注入到Controller对象中的service属性

    //把service注入到controller控制层private void doIoc() {if (beanMap.entrySet().size() <= 0){System.out.println("没有一个被实例化的类");}for (Map.Entry<String, Object> entry: beanMap.entrySet()) {Object instance = entry.getValue();Class<?> clasz = instance.getClass();if (clasz.isAnnotationPresent(XzsController.class)){//@XzsAutowired("LYTService")//private LYTService lytService;Field[] fields = clasz.getDeclaredFields();for (Field field:fields){//判断属性值上有没有注解if (field.isAnnotationPresent(XzsAutowired.class)){XzsAutowired annotation = field.getAnnotation(XzsAutowired.class);String key = annotation.value();//"LYTService"field.setAccessible(true);//private类型 的属性  注入不进去,需要放开权限//设置值try {field.set(instance,beanMap.get(key));} catch (IllegalAccessException e) {e.printStackTrace();}}else{continue;}}}}}

      (4):把Controller的方法与@RequestMapping的value做映射关系,并存下来

//4:url 映射 把方法和请求地址做关联 ;;;/xzs/select 找到对应的方法private void buildUrlMapping() {if (beanMap.entrySet().size() <= 0){System.out.println("没有一个被实例化的类");}for (Map.Entry<String, Object> entry : beanMap.entrySet()) {Class<?> clasz = entry.getValue().getClass();if (clasz.isAnnotationPresent(XzsController.class)){XzsRequestMapping controllerMapping = clasz.getAnnotation(XzsRequestMapping.class);String controllerUrl = controllerMapping.value();//拿到了---》/xzsMethod[] methods = clasz.getMethods();for (Method method:methods){//判断属性值上有没有注解if (method.isAnnotationPresent(XzsRequestMapping.class)){XzsRequestMapping methodMapping = method.getAnnotation(XzsRequestMapping.class);String methodUrl = methodMapping.value();//拿到了---》/select//controllerUrl+methodUrl == /xzs/select//key:url路径 ,value:待执行的方法urlMap.put(controllerUrl+methodUrl,method);}else{continue;}}}}}

2.1:Servlet重新继承的HttpServlet的 doGet(),doPost()方法,经过处理,拿到请求路径 ,找到请求路径对应的Controller方法,解析出对应的请求参数然后执行对应的方法

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//根据路径拿到对应的方法//1:获取请求路径--String requestURI = req.getRequestURI();String contextPath = req.getContextPath();String path = requestURI.replace(contextPath, "");//urlMap的key//2:拿到方法Method method = (Method)urlMap.get(path);String[] split = path.split("/");String controllerPath=null!=split&&split.length>1?"/"+split[1]:"";//3:拿到方法对应的控制类Object o = beanMap.get(controllerPath);//4:参数处理Object arg[] = hand(req,resp,method);try {method.invoke(o,arg);} catch (IllegalAccessException e) {} catch (InvocationTargetException e) {e.printStackTrace();}}//获取方法中的参数private static Object[] hand(HttpServletRequest request, HttpServletResponse response,Method method){//拿到当前执行方法有哪些参数Class<?>[] paramClazzs = method.getParameterTypes();//根据参数个数,new一个参数的数组,将方法里的所有参数赋值大agrs中Object[] args = new Object[paramClazzs.length];int args_i = 0;int index = 0;for (Class<?>  paramClazz : paramClazzs) {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) {if(XzsRequestParam.class.isAssignableFrom(paramAn.getClass())){XzsRequestParam mp = (XzsRequestParam) paramAn;//找到注解里的name和valueargs[args_i++] = request.getParameter(mp.value());}}}index++;}return args;}

代码已上传gitee 

  相关解决方案