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

news2024/11/15 2:47:34

手写一个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,封装类描述类、方法描述类、参数描述类的对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2047455.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一文入门mysql 数据库

一、数据库概述 什么是数据库 数据库是一个用于存储和管理数据的仓库。数据按照特定的格式存储&#xff0c;可以对数据库中的数据进行增加、修改、删除和查询操作。数据库的本质是一个文件系统&#xff0c;按照一定的逻辑结构组织数据&#xff0c;以方便高效地访问和维护。 什…

使用Python创建LNK文件选择器并导出配置文件

在这篇博客中&#xff0c;我将介绍如何使用Python的wxPython库开发一个GUI应用程序&#xff0c;该应用程序可以选择文件夹中的.lnk&#xff08;快捷方式&#xff09;文件&#xff0c;并将选中的文件导出为特定格式的buttons.ini配置文件。这个工具非常适合用来快速生成配置文件…

使用Java调用Apache commons-text求解字符串相似性实战

目录 前言 一、字符串距离的几种计算方法 1、Levenshtein 距离 2、Overlap Coefficient计算 3、Q-gram Matching 4、余弦相似性计算 二、基于余弦相似性的基地名称对比 1、加载百科中的基地信息列表 2、设置忽略词列表 3、将数据库地名和Excel进行对比 三、总结 前言…

c语言音频.wav读写示例

1 .wav格式说明 一. RIFF 概念 在 Windows 环境下&#xff0c;大部分的多媒体文件都依循着一种结构来存放信息&#xff0c;这种结构称为"资源互换文件格式"(Resources lnterchange File Format)&#xff0c;简称 RIFF。例如声音的 WAV 文件、视频的 AV1 文件等等均…

EmguCV学习笔记 VB.Net 2.4 Image类

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV学习笔记目录 Vb.net EmguCV学习笔记目录 C# 笔者的博客网址&#xff1a;VB.Net-CSDN博客 教程相关说明以及如何获得pdf教…

【图解秒杀系列】秒杀技术点——秒杀按钮点亮、削峰

【图解秒杀系列】秒杀技术点——秒杀按钮点亮、削峰 秒杀按钮点亮涉及的问题以及解决办法处理流程 削峰答题 & 验证码具体流程 排队 秒杀按钮点亮 在秒杀场景中&#xff0c;秒杀商品页面是需要处理按钮点亮的逻辑的。在秒杀未开始前&#xff0c;按钮置灰&#xff0c;不可点…

POSIX信号量semaphore实现线程同步

POSIX标准定义了信号量接口如下&#xff0c;常常用于线程间同步。 #include <semaphore.h>int sem_init(sem_t *sem, int pshared, unsigned int value); int sem_destroy(sem_t *sem); int sem_post(sem_t *sem); int sem_wait(sem_t *sem); sem_init()在sem指向的地址…

微信小程序反编译工具

目录 介绍 工程结构还原 微信开发者工具运行 如何查看当前运行版本? 开启小程序F12 重新打包运行 效果示例 安装 用法 参数说明 获取微信小程序AppID 文件夹名即为AppID 下载地址 介绍 纯Golang实现,一个用于自动化反编译微信小程序的工具,小程序安全利器, 自…

【杂谈】-8个常用的Python图像操作库

8个常用的Python图像操作库 文章目录 8个常用的Python图像操作库1、OpenCV2、Pillow&#xff08;PIL&#xff09;3、Scikit Image4、Numpy5、SciPy6、Mahotas7、SimpleITK8、Matplotlib 在当今世界&#xff0c;数据在每个行业垂直领域中都发挥着至关重要的作用。图像可以是提取…

Redis 操作的原子性及其保证机制

Redis 操作的原子性及其保证机制 1、单命令的原子性2、事务的原子性3、并发操作的考虑4、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; Redis 的操作之所以是原子性的&#xff0c;主要得益于其单线程的执行模型。这种模型确保了每个命…

5.9.8 最优化控制初探——PID参数优化

总目录&#xff1a;http://t.csdnimg.cn/YDe8m 5.9.8 最优化控制初探——PID参数优化 之前在“A_2_PID控制转速例程”例程中&#xff0c;PID参数是手动调节的。然而在已经获得系统完整数学模型的情况下&#xff0c;我们可以使用效率更高的方法&#xff0c;即最优化控制。先来看…

Nios II新建项目

1.Nios II Application and BSP form Template BSP:board support package&#xff08;板级支持包&#xff09; 2.Nios II Sotware Examples SOPC Information File name:选择项目文件夹下的 .sopcinfo 文件 Project name:自定义名称 Project location:Use default locatio…

删除镜像报容器依赖错误

1、删除镜像报容器依赖错误 出现这个错误的原因是因为5303b5323a4c容器使用了此镜像。解决&#xff1a;先停止容器、删除容器、之后再镜像即可。 2、查看镜像对应的容器 # docker ps -a | grep 611a37aa5ffc 3、先停止容器 # docker stop 5303b5323a4c 4、删除容器 # do…

Spring Boot: 2.7.x 至 2.7.18 及更旧的版本,漏洞说明

本文提供的修复指南将帮助开发者有效规避 CVE-2024-38808 和 CVE-2024-38809 的风险。如果你正在使用老版本的 Spring Boot&#xff0c;请尽快参考本文进行修复与升级。 此漏洞来源于spring官网&#xff1a;https://spring.io/blog/2024/08/14/spring-framework-releases-fixe…

flink车联网项目前篇:业务实现1(第67天)

系列文章目录 业务实现 3.1 创建catalog 3.1.1 vvp 3.1.2 mysqlcdc 3.1.2.1 使用限制 3.1.2.2 配置MySQL Catalog 3.1.3 xxxxpm 3.1.3.1 下载Paimon插件 3.1.3.2 在MaxCompute项目中上传Paimon插件 3.1.3.3 创建自定义Catalog类型 3.1.3.5 配置catalog 3.1.4 xxxxx 3.1.4.1 背…

Linux设置yum源为阿里云镜像源

一、验证网络是否可以连接阿里云镜像 #验证网络是否可以连接阿里云镜像 ping mirrors.aliyun.com如果ping不通&#xff0c;则找一台可以连接外网的电脑&#xff0c;ping一下mirrors.aliyun.com&#xff0c;找到mirrors.aliyun.com对应的ip。 二、 手动配置 #删除原yum源 rm -…

微信小程序免费《短视频去水印》

分享一个uniapp开发的微信小程序免费《短视频去水印》小程序 <template><view class"content"><view class"area-wrap"><textarea name"" v-model"state.content" maxlength"800" id"" cols…

接口自动化测试怎么做?该怎么学习

一. 什么是接口测试 顾名思义&#xff0c;接口测试是对系统或组件之间的接口进行测试&#xff0c;主要是校验数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及相互逻辑依赖关系。其中接口协议分为HTTP,WebService,Dubbo,Thrift,Socket等类型&#xff0c;测试类型又主…

《黑神话:悟空》媒体评分解禁 M站均分82

《黑神话&#xff1a;悟空》媒体评分现已解禁&#xff0c;截止发稿时&#xff0c;M站共有43家媒体评测&#xff0c;均分为82分。 部分媒体评测&#xff1a; God is a Geek 100&#xff1a; 毫无疑问&#xff0c;《黑神话&#xff1a;悟空》是今年最好的动作游戏之一&#xff…

linux系统安装mysql服务

linux系统安装mysql服务 1.下载安装包2.下载压缩文件解压安装3. 安装完启动服务4.查看安装密码5.使用上述密码登录6.修改密码7.创建一个root可以在任意主机远程连接数据库8.远程登录成功 1.下载安装包 https://downloads.mysql.com/archives/community/ mysql下载地址 2.下载…