学习Java的日子 Day71 手写一个SpringMVC的框架(一)

手写一个SpringMVC的框架

1.理解为什么要写这样一个框架

SpringMVC 实际上跟Servlet是一样,都是 Controller的一个解决方案,也就是说我们手写这个框架的目的就是为了替换原来的 Servlet

注意:

spring不是框架,springMVC才是框架,八大框架之一

controller层使用springMVC,用注解和配置就可以了

SpringMVC在项目启动时,会扫描web.xml的controller配置内容,首先会自动跳到controller层

Servlet的缺点:

  1. 一个请求就要写一个Sevlet,就导致了需要写大量的 Servlet ,增加了程序的复杂度
  2. 每个Servlet中获取参数都要getParameter,会导致代码产生大量的冗余
  3. 每个Servlet都需要实现配置,如果在web.xml中来编写的话太复杂了
  4. 如果需要解密和加密(站在后端程序员的角度,先解密再加密),会导致每个Servlet需要去写这个逻辑显得很复杂
  5. 异常处理显得很鸡肋,要重写很多的类
  6. 请求参数还不能自动封装成对象
  7. 前端如果传递JSON格式,那么后台每一次都要 getInputStream
  8. 返回参数如果是JSON格式每一次都需要 getWriter()

在这里插入图片描述

2.这个框架的整体实现逻辑是什么

使用的角度:浏览器发送请求给DispatchServlet(不是发送给controller层,整个项目里只有一个servlet ),servlet会根据不同的请求地址去分发给controller层(controller是一个类)
在这里插入图片描述

/back/user:父级uri,login.action:子级uri(.action特定的规范就可以去访问对应的controller层)

@controller:controller层

大类的@RequestMapping(“/back/user”):父级uri

方法的@RequestMapping(“login.action”):子级uri

路径携带的数据会解析出来传到方法上的参数上面

在这里插入图片描述

准备工作,左边是spring-mvc-framework的基本准备工作,右边是shop-web的准备工作
在这里插入图片描述

放大版本:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

ApplicationListener全局监听器,右边是异常处理类
在这里插入图片描述

3.开始手写这样一个框架

3.1 建项目

大的聚合项目里面包裹web项目和springMVC项目

在这里插入图片描述

创建聚合项目shop-parent

在这里插入图片描述

创建web项目shop-web,webapp

在这里插入图片描述

在shop-web里建包,com.qf.shop.web,在建小包pojo实体包和控制器Controller包

在这里插入图片描述

在实体类里要写无参构造,有参构造,get、set、toString,太麻烦了,所以使用lombok依赖

Lombok能通过注解的方式,在编译时自动为属性生成无参构造方法、有参构造、get、set、equals、hashcode、toString方法

安装插件File -> Plugins -> Marketplace 中所有lombok -> Install

实体类user

@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
@Data //get set toString
public class User {
    private String username;
    private String password;
}

在web项目的pom.xml添加依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
    <scope>provided</scope>
</dependency>

建一个Controller包,存在前台用户和后台用户(back包)

里面都是存放Controller类文件,要加注解(加了才是Controller层),springmvc才知道这个是Controller

在这里插入图片描述

前台用户UserController

package com.qf.shop.web.controller;
import com.qf.shop.mvc.annotation.Controller;

@Controller
//前台用户
public class UserController {
    public void login(String username,String password){
        System.out.println("前台用户 - login:" + username + " -- " + password);
    }

    public void register(User user){
        System.out.println("前台用户 - register:" + user);
    }
}

后台用户UserController,放在Controller/back包里

package com.qf.shop.web.controller.back;
import com.qf.shop.mvc.annotation.Controller;
//后台用户
@Controller
public class UserController {

    public void login(String username,String password){
        System.out.println("后台用户 - login:" + username + " -- " + password);
    }

    public void register(User user){
        System.out.println("后台用户 - register:" + user);
    }
}

ProvinceController

注意:要考虑参数,返回值(JSON字符串,路径)

package com.qf.shop.web.controller;
import com.qf.shop.mvc.annotation.Controller;

@Controller
public class ProvinceController {
    //获取所有的省份信息,返回json格式的字符串
    public String getProvinces(){
        System.out.println("ProvinceController -- getProvinces()");
        return "";
    }

    //根据省份的code,查询出对应的城市信息,返回json格式的字符串
    public String getCities(String code){
        System.out.println("ProvinceController -- getCities():" + code);
        return "";
    }

}

A(测试类)

package com.qf.shop.web.controller;

public class A {
    public void method(){
        System.out.println("好好学习");
    }
}

Controller包里,springMVC怎么知道这里面的是Controller,哪些不是Controller

建一个Java项目spring-mvc-framework

在这里插入图片描述

在MVC中的pom.xml添加jdk1.8依赖

<build>
	<finalName>spring-mvc-framework</finalName>
	<plugins>
		<plugin>
			<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<encoding>UTF-8</encoding>
				</configuration>
		</plugin>
	</plugins>
</build>

shop-web的pom.xml添加jdk1.8依赖

    <build>
        <finalName>shop-web</finalName>
            <plugins>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
</project>

3.2 准备注解

在这里插入图片描述

web项目中
在这里插入图片描述

mvc项目中

在这里插入图片描述

在这里插入图片描述

建包创建Controller注解

在这里插入图片描述

package com.qf.shop.mvc.annotation;

/**
 * 作用在Controller类上,说明该类是Controller类
 */
@Target(ElementType.TYPE) //作用在类上
@Retention(RetentionPolicy.RUNTIME) //运行时有效
public @interface Controller {
}

spring-mvc-framework项目要作用到shop-web项目中,需要在shop-web项目的pom.xml添加依赖

<dependency>
      <groupId>com.qf.shop.mvc</groupId>
      <artifactId>spring-mvc-framework</artifactId>
      <version>1.0-SNAPSHOT</version>
</dependency>

给上面的Controller包里面的类都添加上Controller注解,除了A类不添加 (@Controller),A类是测试类

3.2.1 web项目中指定Controller包的路径(哪个包)

在web项目中新建一个config包,建一个AppConfig类

这是一个联通springmvc的配置类,要写在@configuration的value

package com.qf.shop.web.config;

import com.qf.shop.mvc.annotation.Configuration;

/**
 * Web项目的配置类
 * 指定Controller层的扫描路径
 * 指定前后置处理器类的路径(后面加)
 * 指定捕获全局异常类的路径(后面加)
 */

@Configuration(value="com.qf.shop.web.controller")
public class AppConfig {
}

在mvc项目的annotation注解包中添加一个注解Configuration

package com.qf.shop.mvc.annotation;

/**
 * 作用在web项目中AppConfig类上,标注该web项目需要扫描的Controller层的路径
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {
    String value();
}

在这里插入图片描述

在这里插入图片描述

要在mvc项目中想办法拿到web项目中的config.class(Appconfig类),再利用class对象找到对应的注解信息路径即:@Configuration(value=“com.qf.shop.web.controller”)

在web项目中的web.xml配置一下,配置类config的路径

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <!-- 将web项目的配置类config的全路径存放在ServletContext对象中 -->
  <context-param>
    <param-name>config</param-name>
    <param-value>com.qf.shop.web.config.AppConfig</param-value>
  </context-param>
    
</web-app>

接下来要在mvc项目中想办法拿到这个全路径,就可以找到class对象,就找到对应的注解信息

添加一个servlet依赖

 <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>javax.servlet-api</artifactId>
       <version>4.0.1</version>
       <scope>provided</scope>
</dependency>

先建一个监听器包,项目一启动就可以运行,ApplicationListener类

启动web项目,已经在web项目中引入了mvc的依赖,所以mvc项目的代码就可以生效,但是监听器要写注解@WebListener,但是不用这种方法,用注解

缺点:用注解,项目启动,ApplicationListener一定会运行

框架设计的目的是我想让它启动它就启动,不想它启动就不能启动,启不启动,web项目说了算

在web的web.xml配置监听器,mvc项目的监听器才有效果

  <listener>
    <listener-class>com.qf.shop.mvc.listener.ApplicationListener</listener-class>
  </listener>

mvc中的ApplicationListener类

package com.qf.shop.mvc.listener;

import com.qf.shop.mvc.annotation.Configuration;

public class ApplicationListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("项目启动了...");

        //获取到web项目中配置文件类的全路径
        //获取全局域对象
        ServletContext servletContext = sce.getServletContext();
        //com.qf.shop.web.config.AppConfig
        String config = servletContext.getInitParameter("config");

        try {
            //获取web项目中配置文件类的class对象  AppConfig.class
            Class<?> configClass = Class.forName(config);

            //获取web项目中配置类(AppConfig)中的Configuration注解
            Configuration configClassAnnotation = configClass.getAnnotation(Configuration.class);
            //获取web项目中Controller层的扫描路径  com.qf.shop.web.controller
            String value = configClassAnnotation.value();
            //System.out.println("配置文件类路径:" + value);


        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

配置tomcat,修改一下,下面修改为/

在这里插入图片描述

3.3 异常处理

关于config的各种异常,mvc项目有自己的自定义的异常

在这里插入图片描述

放大版:

在这里插入图片描述

在mvc项目中建包com.qf.shop.mvc.constant,创建一个ResponseCodeInterface接口

异常信息接口

package com.qf.shop.mvc.constant;

/**
 * 异常信息的接口
 */
public interface ResponseCodeInterface {
    public int getCode();
    public void setCode(int code);
    public String getMessage();
    public void setMessage(String message);
}

接口的实现类,使用enum更好,这是提供了一些异常信息类型,并不是异常处理

接口是定义规范的

package com.qf.shop.mvc.constant;

public enum ResponseCode implements ResponseCodeInterface{

    CONFIG_EXCEPTION(100,"config的配置信息出错"),
    CONFIGURATION_EXCEPTION(101,"需要配置Configuration这个注解"),
    CLASS_FILE_EXCEPTION(102,"class文件转换异常"),
    REQUEST_MAPPING_PATH_EXCEPTION(103,"RequestMapping地址设置有误"),
    REQUEST_PATH_EXCEPTION(104,"uri映射错误"),
    ADVISER_CONFIG_EXCEPTION(105,"未配置处理器的路径"),
    EXCEPTION_CONFIG_EXCEPTION(106,"未配置全局异常的路径");

    //枚举类中的属性
    private int code;
    private String message;

    //无参构造,有参构造,get,set,tostring方法省略
}

新建一个异常exception包,建一个FrameworkException类,继承RuntimeException,让他报错,但是不用强制在方法上处理

不需要无参构造,让ApplicationListener抛出异常,必须在括号中写内容

package com.qf.shop.mvc.exception;

/**
 * spring-MVC的异常类
 */
@AllArgsConstructor
@Data
public class FrameworkException extends RuntimeException{
    private int code;
    private String message;
}

新建一个工具包utils,建StringUtils类

package com.qf.shop.mvc.utils;

public class StringUtils {
    
    /**
     * 判断字符串是否为空
     */
    public static boolean isEmpty(String value) {
        return value == null || value.length() == 0;
    }
}

包结构

在这里插入图片描述

更新一下ApplicationListener,处理异常 v2.0

pakage com.qf.shop.mvc.listener;

public class ApplicationListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("项目启动了...");

        //获取到web项目中配置文件类的全路径
        ServletContext servletContext = sce.getServletContext();
        //com.qf.shop.web.config.AppConfig
        String config = servletContext.getInitParameter("config");
        
        //有可能用户没有在web.xml配置config,所以需要判断
        if (StringUtils.isEmpty(config)) {
            throw new FrameworkException(ResponseCode.CONFIG_EXCEPTION.getCode(),ResponseCode.CONFIG_EXCEPTION.getMessage());
        }

        //获取web项目中配置文件类的class对象(下面封装了一个异常处理方法)
        Class<?> configClass = getConfigClass(config);

            //获取web项目中配置类(AppConfig)中的Configuration注解
        String controllerPosition = getControllerPosition(configClass);
        System.out.println(controllerPosition);//com.qf.shop.web.controller

    }

    //获取web项目中Controller层的扫描路径的方法
    public String getControllerPosition(Class<?> configClass){

        //获取web项目中配置类(AppConfig)中的Configuration注解
        Configuration configClassAnnotation = configClass.getAnnotation(Configuration.class);
        
        //判断web项目中配置类(AppConfig)中是否有Configuration注解
        if(configClassAnnotation == null){
            throw new FrameworkException(ResponseCode.CONFIGURATION_EXCEPTION.getCode(),ResponseCode.CONFIGURATION_EXCEPTION.getMessage());
        }
        
        String controllerPosition = configClassAnnotation.value();
        return controllerPosition;
    }

    //获取web项目中配置文件类的class对象的方法
    public Class<?> getConfigClass(String config){
        try {

            Class<?> configClass = Class.forName(config);
            return configClass;
        } catch (ClassNotFoundException e) {
            throw new FrameworkException(ResponseCode.CLASS_FILE_EXCEPTION.getCode(),ResponseCode.CLASS_FILE_EXCEPTION.getMessage());
        }
    }
    
}

现在可以在mvc项目的ApplicationListener监听器类中定位到web项目的Controller路径

记录在一个map集合中,key是路径(浏览器/back/user/login.action,父级uri+子级uri),value是这个类描述对象,在项目启动时就存储,在监听器存储

在mvc中创建一个注解RequestMapping

package com.qf.shop.mvc.annotation;

/**
 * 作用在Controller类上或方法上,标注父级uri和子级uri
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    String value();
}

更新一下web项目中的@RequestMapping(“back/user”)

package com.qf.shop.web.controller.back;

@Controller
@RequestMapping("/back/user")
public class UserController {

    @RequestMapping("login.action")
    public void login(String username,String password){
        System.out.println("后台用户 - login:" + username + " -- " + password);
    }

    @RequestMapping("register.action")
    public void register(User user){
        System.out.println("后台用户 - register:" + user);
    }
}

ProvinceController

package com.qf.shop.web.controller;

@Controller
@RequestMapping("/province")
public class ProvinceController {
    //获取所有的省份信息,返回json格式的字符串
    @RequestMapping("/getProvinces.action")
    public String getProvinces(){
        System.out.println("ProvinceController -- getProvinces()");
        return "";
    }

    //根据省份的code,查询出对应的城市信息,返回json格式的字符串
    @RequestMapping("/getCities.action")
    public String getCities(String code){
        System.out.println("ProvinceController -- getCities():" + code);
        return "";
    }

}

UserController

package com.qf.shop.web.controller;

@Controller
@RequestMapping("/user")
//前台用户
public class UserController {
    @RequestMapping("/login.action")
    public void login(String username,String password){
        System.out.println("前台用户 - login:" + username + " -- " + password);
    }

    @RequestMapping("/register.action")
    public void register(User user){
        
        System.out.println("前台用户 - register:" + user);
    }
}

3.4 类描述类

在这里插入图片描述

在这里插入图片描述

类描述类概念:类描述web项目中的Controller类

三种类型:类描述类,方法描述类,参数描述类

在mvc项目建一个model包,新建三个类:类描述类,方法描述类,参数描述类

在这里插入图片描述

ParameterDefinition,参数描述类

package com.qf.shop.mvc.model;

/**
 * 参数描述类
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ParameterDefinition {
    private String name; // 参数名
    private Class<?> clazz; // 参数类型
    private int index; // 参数位置,参数下标
    private Parameter parameter;//参数对象
}

MethodDefinition 方法描述类

package com.qf.shop.mvc.model;

/**
 * 方法描述类
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class MethodDefinition {

    private String requestMappingPath;//子级uri
    private Method method;//方法对象  反射用
    private String name;//方法名
    private List<ParameterDefinition> parameterDefinitions;//参数描述类对象的集合(一个方法可能有多个参数)
    private Class<?> returnType;//返回值类型 反射

}

BeanDefinition 类描述类

package com.qf.shop.mvc.model;

/**
 * 类描述类
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class BeanDefinition<T> {

    private String requestMappingPath;//父级uri
    private Class<?> clazz;//该Controller类的class对象
    private  T t;//该Controller类的对象,调用方法会用,方法名.invoke(该类对象,xxx)
    private String name;//类名
    private MethodDefinition methodDefinition;//方法描述类的对象(不是集合,就是一个方法,因为一个路径就只有一个action,就只有一个功能,找到一个方法)
}

方法描述类的对象(不是集合,就是一个方法,因为一个路径就只有一个action,就只有一个功能,找到一个方法),解释:

在这里插入图片描述

问题:下面有几个类描述类?两个

package com.qf.shop.web.controller.back;

@Controller
@RequestMapping("/back/user")
public class UserController {

    @RequestMapping("login.action")
    public void login(String username,String password){
        System.out.println("后台用户 - login:" + username + " -- " + password);
    }

    @RequestMapping("register.action")
    public void register(User user){
        System.out.println("后台用户 - register:" + user);
    }
}

3.5 拼接路径

修改发布路径

放在tomcat目录的webapp里,运行不起的话,就删除webapp里的文件

在这里插入图片描述

在这里插入图片描述

需要拿到下面的class文件(反射),操作的就是下面的一个一个的class文件

在这里插入图片描述

下面就是拼接工作

在这里插入图片描述

更新ApplicationListener v3.0

在这里插入图片描述

package com.qf.shop.mvc.listener;

public class ApplicationListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("项目启动了...");

        //获取到web项目中配置文件类的全路径
        ServletContext servletContext = sce.getServletContext();
        //com.qf.shop.web.config.AppConfig
        String config = servletContext.getInitParameter("config");
        //有可能用户没有在web.xml配置config,所以需要判断
        if (StringUtils.isEmpty(config)) {
            throw new FrameworkException(ResponseCode.CONFIG_EXCEPTION.getCode(),ResponseCode.CONFIG_EXCEPTION.getMessage());
        }

        //获取web项目中配置文件类的class对象(下面封装了一个异常处理方法)
        Class<?> configClass = getConfigClass(config);

        //获取web项目中配置类(AppConfig)中的Configuration注解
        //com.qf.shop.web.controller
        String controllerPosition = getControllerPosition(configClass);


        //获取Controller层的绝对路径  D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes\com\qf\shop\web\controller
        String controllerAbsolutePath = getControllerAbsolutePath(servletContext, controllerPosition);

        //将该路径下的所有文件对象存入到集合中
        List<File> fileList = new ArrayList<>();
        findFileByPath(controllerAbsolutePath,fileList);//A类也在

        //转换为 List<Class<?>>  没有A类
        //[class com.qf.shop.web.controller.back.UserController, class 		     
        //   com.qf.shop.web.controller.ProvinceController, class   
        //     com.qf.shop.web.controller.UserController  ]
        List<Class<?>> classList = transformTo(servletContext, fileList);

        //封装类描述类对象、方法描述类对象、参数描述类对象,存入Map中
        handlerClassList(classList);

        //扫描结束
        System.out.println("扫描结束...");
    }

    
    //下面都是封装的方法


    //封装类描述类对象、方法描述类对象、参数描述类对象,存入Map中
    public void handlerClassList(List<Class<?>> classList){

        for (Class<?> clazz : classList) {

            //class com.qf.shop.web.controller.back.UserController
            //class com.qf.shop.web.controller.ProvinceController
            //class com.qf.shop.web.controller.UserController
            // System.out.println(clazz);

            //获取类描述类对象需要的信息 ----------------------------
            Object t=null;
            try {
                //创建大类对象
                t = clazz.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }

            String className = clazz.getName();//大类的类名
            RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);//大类的父级RequestMapping
            String fatherUri = classRequestMapping.value();//父级uri
            //父级uri可能没有写
            if (StringUtils.isEmpty(fatherUri)){
                throw new FrameworkException(ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getCode(),ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getMessage());
            }

            Method[] methods = clazz.getDeclaredMethods();//大类的方法
            for (Method method : methods) {
                method.setAccessible(true);//设置权限

                //获取方法描述类对象需要的信息 ----------------------------
                String methodName = method.getName();//方法的方法名
                Class<?> returnType = method.getReturnType();//方法的返回值类型
                RequestMapping methodRequestMapping  = method.getAnnotation(RequestMapping.class);//方法上的RequestMapping
                //方法上的RequestMapping有可能没有,不需要报错
                if (methodRequestMapping == null){
                    continue;
                }
                String sonUri  = methodRequestMapping.value();//子级uri
                //子级uri可能没有写
                if (StringUtils.isEmpty(sonUri)){
                    throw new FrameworkException(ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getCode(),ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getMessage());

                }
                List<ParameterDefinition> parameterDefinitions = null;//参数描述类对象的集合
                Parameter[] parameters = method.getParameters();//参数对象数组
                if (parameters.length!=0){
                    parameterDefinitions =new ArrayList<>();//参数描述类对象要放在集合里
                    for (int i = 0; i < parameters.length; i++) {//获取参数的下标
                        
                        //获取参数描述类对象的信息  ----------------------------
                       Parameter parameter = parameters[i];//参数对象
                        String parameterName = parameter.getName();//参数名(注意:java在编译时,不会将参数名编译到class文件中,class文件里的参数名使用arg0、arg1...替代,如果想要将参数名编译到class文件中必须使用编译插件)
                        Class<?> parameterType = parameter.getType();//参数类型的class对象

                        //创建参数描述类对象
                        ParameterDefinition parameterDefinition = new ParameterDefinition(parameterName, parameterType, i,parameter);
                        //添加到参数描述类对象的集合
                        parameterDefinitions.add(parameterDefinition);
                    }
                }

                //创建方法描述类的对象
                MethodDefinition methodDefinition = new MethodDefinition(sonUri, method, methodName, parameterDefinitions, returnType);
                //创建类描述对象
                BeanDefinition<Object> beanDefinition = new BeanDefinition<>(fatherUri, clazz, t, className, methodDefinition);

                //拼接uri
                String uri=fatherUri+sonUri;

                //存入Map容器中
                ConcurrentHashMap<String, BeanDefinition> maps = TypeContainer.getMaps();
                //key-uri,value-类描述类对象
                maps.put(uri,beanDefinition);

                Set<Map.Entry<String, BeanDefinition>> entries = maps.entrySet();
                //for (Map.Entry<String, BeanDefinition> entry : entries) {
                    //System.out.println(entry);
 //打印entry如下:                  ///back/userregister.action=BeanDefinition(requestMappingPath=/back/user, clazz=class com.qf.shop.web.controller.back.UserController, t=com.qf.shop.web.controller.back.UserController@23571581, name=com.qf.shop.web.controller.back.UserController, //methodDefinition=MethodDefinition(requestMappingPath=register.action, method=public void com.qf.shop.web.controller.back.UserController.register(com.qf.shop.web.pojo.User), name=register,
//parameterDefinitions=[ParameterDefinition(name=user, clazz=class com.qf.shop.web.pojo.User, index=0)], returnType=void))
                //}
            }
        }

    }
    
    
    //将List<File> 转换为 List<Class<?>>
    public List<Class<?>> transformTo(ServletContext servletContext,List<File> fileList){

        //发布路径 -- D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes
        String realPath = servletContext.getRealPath("WEB-INF\\classes");

        List<Class<?>> classList = new ArrayList<>();
        for (File file : fileList) {

            //D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes\com\qf\shop\web\controller\A.class
            String absolutePath = file.getAbsolutePath();

            //\com\qf\shop\web\controller\A.class   去掉前面的部分
            absolutePath = absolutePath.replace(realPath,"");

            //com\qf\shop\web\controller\A.class
            absolutePath = absolutePath.substring(1);

            //com\qf\shop\web\controller\A
            absolutePath = absolutePath.substring(0,absolutePath.lastIndexOf("."));

            //com.qf.shop.web.controller.A
            absolutePath = absolutePath.replace("\\",".");

            try {
                Class<?> clazz = Class.forName(absolutePath);
                Controller controllerAnnotation = clazz.getAnnotation(Controller.class);
                //判断是否有Controller注解,没有则跳过,有(就是一个Controller类)则添加到集合中
                if(controllerAnnotation == null){
                    continue;
                }
                classList.add(clazz);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        return classList;
    }


    //将该路径下的所有文件对象存入到集合中
    public void findFileByPath(String controllerAbsolutePath, List<File> fileList){
        File file = new File(controllerAbsolutePath);
        File[] files = file.listFiles();
        for (File f : files) {
            if(f.isDirectory()){//文件夹
                findFileByPath(f.getAbsolutePath(),fileList); //递归
            }else if(f.isFile()){//文件
                fileList.add(f);
            }
        }
    }

    //获取Controller层的绝对路径
    public String getControllerAbsolutePath(ServletContext servletContext,String controllerPosition){
        //发布路径 -- D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes
        //getRealPath()获取的是发布路径
        String realPath = servletContext.getRealPath("WEB-INF\\classes");

        //com.qf.shop.web.controller --> com\qf\shop\web\controller   替换 一个是转义字符,两个\\才是\
        controllerPosition =  controllerPosition.replace(".","\\");

        //拼接 -- D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes\com\qf\shop\web\controller
        //File.separator是系统分隔符 ,windows是\,linux是/
        String controllerAbsolutePath = realPath + File.separator + controllerPosition;
        return controllerAbsolutePath;
    }

    //获取web项目中Controller层的扫描路径的方法
    public String getControllerPosition(Class<?> configClass){

        //获取web项目中配置类(AppConfig)中的Configuration注解
        Configuration configClassAnnotation = configClass.getAnnotation(Configuration.class);
        //判断web项目中配置类(AppConfig)中是否有Configuration注解
        if(configClassAnnotation == null){
            throw new FrameworkException(ResponseCode.CONFIGURATION_EXCEPTION.getCode(),ResponseCode.CONFIGURATION_EXCEPTION.getMessage());
        }
        String controllerPosition = configClassAnnotation.value();
        return controllerPosition;
    }

    //获取web项目中配置文件类的class对象的方法
    public Class<?> getConfigClass(String config){
        try {

            Class<?> configClass = Class.forName(config);
            return configClass;
        } catch (ClassNotFoundException e) {
            throw new FrameworkException(ResponseCode.CLASS_FILE_EXCEPTION.getCode(),ResponseCode.CLASS_FILE_EXCEPTION.getMessage());
        }
    }
}

新建一个container包

容器类TypeContainer

package com.qf.shop.mvc.container;

import com.qf.shop.mvc.model.BeanDefinition;

import java.util.concurrent.ConcurrentHashMap;

public class TypeContainer {

    private static ConcurrentHashMap<String, BeanDefinition> maps;

    static {
        maps = new ConcurrentHashMap<>();
    }
	//拿到map对象
    public static ConcurrentHashMap<String, BeanDefinition> getMaps() {
        return maps;
    }
}

添加编译插件 :maven-compiler-plugin插件

含义:将方法中的参数名编译到class文件中

String parameterName = parameter.getName();//参数名(注意:java在编译时,不会将参数名编译到class文件中,class文件里的参数名使用arg0、arg1…替代,如果想要将参数名编译到class文件中必须使用编译插件)

    <build>
        <finalName>spring-mvc-framework</finalName>
        <pluginManagement>

            <plugins>
                <!-- 这个插件就是java类生成class的编译插件 -->
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <!-- 编译参数 -->
                        <compilerArgument>-parameters</compilerArgument>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

web项目也要添加(如果还不行,就删除web项目中的target文件夹)

    <build>
        <finalName>shop-web</finalName>
        <pluginManagement>

            <plugins>
                <!-- 这个插件就是java类生成class的编译插件 -->
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <!-- 编译参数 -->
                        <compilerArgument>-parameters</compilerArgument>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

web项目框架

在这里插入图片描述

mvc项目框架

!在这里插入图片描述

总结

1.完善ApplicationListener,封装类描述类、方法描述类、参数描述类的对象

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A 北枝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值