【Java基础】反射详述简单模拟SpringMVC

news2024/10/6 18:34:31

🚩 本文已收录至专栏:JAVA基础
👍希望能对你有所帮助

一.概述

反射是指对于任何一个Class类,在运行的时候都可以直接得到这个类全部成分,使得我们可以动态操作Java代码,同时反射也破坏了Java的封装性

例如:在运行时,可以直接得到这个类的构造器对象(Constructor)、成员变量对象(Field)、成员方法对象(Method),不管是否为私有,这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制
在这里插入图片描述

反射的关键和核心思想:必须先获取编译后的Class类对象,然后就可以分析类对象中的全部成分并进行一些操作。

HelloWorld.java -> javac -> HelloWorld.class
// 获取类对象
Class c = HelloWorld.class;
// 获取其他类中的各个成分....

反射的用途:反射为绝大部分Java框架的底层实现原理。它常常被用于开发各种具有通用性的框架或者工具

二.获取类对象

(1) 引入

反射的第一步就是先获取Class类对象,然后我们便可以基于获取的类对象解析类中的全部成分。我们有三种方式可以获取类对象:
在这里插入图片描述

三种方式对比显然直接通过类名.class方式获取最简单,但我们依旧需要看使用场景选择不同的方式。例如:假如我们需要获取方法参数传递对象的类对象,只能使用对象.getClass()

我们先创建一个学生类,用于获取类对象。

// 反射学习
package relate;

public class Student {
}

(2) 第一种方式

我们可以通过Class类的一个静态方法forName("全限名")获取类对象

package relate;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取类对象
        Class c = Class.forName("relate.Student");
        // 打印类对象,也可以直接通过getSimpleName获取类名
        System.out.println(c+"====>"+c.getSimpleName());
    }
}

// 打印结果
// class relate.Student====>Student

全限名:可以看作是包名+类名

(3) 第二种方式

我们也可以通过一种更为简单的方式获取:类名.class

package relate;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取类对象
        Class c = Student.class;
        // 打印类对象,,也可以直接通过getSimpleName获取类名
        System.out.println(c+"====>"+c.getSimpleName());
    }
}
// 打印结果相同
// class relate.Student====>Student

(4) 第三种方式

此外我们还可以通过Object类中的getClass方法获取类对象:对象.getClass()

package relate;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取类对象
        Student student = new Student();
        Class c = student.getClass();
        // 打印类对象,,也可以直接通过getSimpleName获取类名
        System.out.println(c+"====>"+c.getSimpleName());
    }
}
// 打印结果也相同
// class relate.Student====>Student

我们已经获取了类对象,接下来便可以逐步解析类对象中的各个成分了,尽管类中存在一些私有的成分,但我们仍然可以获取到,也就是说可以破坏封装性

三.获取构造器对象

(1) 引入

我们可以通过反射获取类对象的任意构造器,然后再创建对象,尽管有些类的构造器是私有的,但并不妨碍我们基于此创建对象。
在这里插入图片描述

Class类提供了一些方法供我们获取构造器对象:

方法说明
Constructor<?>[] getConstructors()返回所有构造器对象的数组(只能拿public的
Constructor<?>[] getDeclaredConstructors()返回所有构造器对象的数组,存在(包括private)就能拿到
Constructor getConstructor(Class<?>… parameterTypes)返回单个构造器对象,只能拿public的
Constructor getDeclaredConstructor(Class<?>… parameterTypes)返回单个构造器对象,存在包括private都能拿到

(2) 获取多个构造器

(2.1) 公有构造器

通过getConstructors方法我们只能获取所有的公有构造器:

//    public Student() {
//    }

//    public Student(String name, int age) {
//        this.name = name;
//        this.age = age;
 //   }

public class Test {
    public static void main(String[] args) {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取公有public构造器对象
        Constructor[] constructors = c.getConstructors();
        // 3.打印结果
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }
}

// 可以看到我们声明的两个构造器
// public relate.Student()
// public relate.Student(java.lang.String,int)

假如我们将带参的构造器改为私有的:

private Student(String name, int age) {
    this.name = name;
    this.age = age;
}

再次运行发现我们只能获取到公有的无参构造器,而私有的带参构造器则无法获取到

// 打印结果
// public relate.Student()

(2.2) 任意构造器

我们只需要使用getDeclaredConstructors即可获取所有构造器,包括私有构造器,这也是我们常用的。用法与上述类似:

//    public Student() {
//    }

//    private Student(String name, int age) {
//        this.name = name;
//        this.age = age;
 //   }

public class Test {
    public static void main(String[] args) {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取所有存在的构造器对象
        Constructor[] constructors = c.getDeclaredConstructors();
        // 3.打印结果
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }
}
// 打印结果
// public relate.Student()
// private relate.Student(java.lang.String,int)

(3) 获取单个构造器

我们也可以根据构造器参数的类型与数量获取单个构造器。

(3.1) 公有构造器

我们可以通过getConstructor获取单个构造器,与上述类似,只能获取公有构造器

//    public Student() {
//    }

//   public Student(String name, int age) {
//       this.name = name;
//        this.age = age;
//   }

package relate;

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取单个公有构造器对象
        Constructor constructor1 = c.getConstructor();  // 无参直接方法名获取
        Constructor constructor2 = c.getConstructor(String.class, int.class); // 带参数需要指定每个参数类型
        // 3.打印结果
        System.out.println("无参构造器===》" + constructor1);
        System.out.println("带参构造器===》" + constructor2);
    }
}
// 打印结果
// 无参构造器===》public relate.Student()
// 带参构造器===》public relate.Student(java.lang.String,int)

(3.2) 任意构造器

我们还可以通过getDeclaredConstructor获取单个任意构造器

//    public Student() {
//    }

//    public Student(String name, int age) {
//        this.name = name;
//        this.age = age;
//   }

package relate;

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取任意构造器对象
        Constructor constructor1 = c.getDeclaredConstructor(); // 无参直接方法名获取
        Constructor constructor2 = c.getDeclaredConstructor(String.class, int.class); // 带参数需要指定每个参数类型
        // 3.打印结果
        System.out.println("无参构造器===》" + constructor1);
        System.out.println("带参构造器===》" + constructor2);
    }
}
// 打印结果
// 无参构造器===》public relate.Student()
// 带参构造器===》private relate.Student(java.lang.String,int)

(4) 创建对象

我们可以通过如下方法利用构造器对象创建对象。

方法说明
T newInstance(Object… initargs)根据指定的构造器创建对象(若构造器为private需要先取消检查)
public void setAccessible(boolean flag)设置为true,表示取消访问检查,进行暴力反射 ,无视private

(4.1) 基于公有构造器

我们可以基于获取到的构造器对象来创建对象。

//    public Student() {
//    }

package relate;

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取构造器对象
        Constructor constructor = c.getDeclaredConstructor();

        // 3.根据无参构造器创建对象,0个参数
        Student student = (Student) constructor.newInstance();
        System.out.println(student);
    }
}
// 打印结果
// Student{name='null', age=0}

(4.2) 基于私有构造器

尽管我们可以获取到私有构造器,但是通过其创建对象时会发现程序会抛出异常,这时因为我们还要通过setAccessible打开权限。这也暴露了我们使用单例模式并不能一定保证只有一个对象。

//   private Student(String name, int age) {
//       this.name = name;
//        this.age = age;
//   }

package relate;

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取构造器对象
        Constructor constructor = c.getDeclaredConstructor(String.class, int.class);

        // 3.打开权限
        constructor.setAccessible(true);

        // 4.创建对象
        Student student = (Student) constructor.newInstance("观止", 19);
        System.out.println(student);
    }
}
// 打印结果
// Student{name='观止', age=19}

四.获取成员变量对象

(1) 引入

我们同样可以通过反射获取类对象的任意成员变量并进行赋值或获取值。
在这里插入图片描述

Class类也提供了一些方法供我们获取成员变量对象:

方法说明
Field[] getFields()返回所有成员变量对象的数组(只能拿public的)
Field[] getDeclaredFields()返回所有成员变量对象的数组,存在就能拿到
Field getField(String name)返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name)返回单个成员变量对象,存在就能拿到

需要注意的是:

  • getFields将返回所有的公共字段,包括从父类中继承来的公共字段
  • getDeclaredFields只返回自身包含的全部字段。

(2) 获取多个成员变量

(2.1) 公有成员变量

可以通过getFields获取所有公有成员变量

 //   public String name;
 //   private int age;
 
import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取公有成员变量
        Field[] fields = c.getFields();
        // 3. 遍历打印结果
        for (Field field : fields) {
            System.out.println(field + "===>" + field.getName());
        }
    }
}
// 打印结果
// public java.lang.String relate.Student.name ===> name ===> class java.lang.String

(2.2) 任意成员变量

可以通过getDeclaredFields获取所有任意成员变量

 //   public String name;
 //   private int age;

package relate;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取任意成员变量
        Field[] fields = c.getDeclaredFields();
        // 3. 遍历打印结果
        for (Field field : fields) {
            System.out.println(field + " ===> " + field.getName());
        }
    }
}
// 打印结果
// public java.lang.String relate.Student.name ===> name ===> class java.lang.String
// private int relate.Student.age ===> age ===> int

(3) 获取单个成员变量

(3.1) 公有成员变量

可以通过getField获取单个公有成员变量

package relate;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.根据名称获取公有成员变量
        Field name = c.getField("name");
        // 3.打印结果
        System.out.println(name + " ===> " + name.getName()+ " ===> " + name.getType());
    }
}
// 打印结果
// public java.lang.String relate.Student.name ===> name ===> class java.lang.String

(3.2) 任意成员变量

可以通过getDeclaredField获取单个任意成员变量

package relate;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.根据名称获取任意成员变量
        Field age = c.getDeclaredField("age");
        // 3.打印结果
       System.out.println(age + " ===> " + age.getName()+ " ===> " + age.getType());
    }
}
// 打印结果
// private int relate.Student.age ===> age ===> int

(4) 赋值&获取值

我们可以获取或修改对象的字段值,尽管有限字段是私有不对外暴露的,但依旧可以通过反射进行操作。

方法说明
void set(Object obj, Object value):赋值
Object get(Object obj)获取值。

我们必须先创建对象,再进行操作,因为不基于对象,对象值是毫无意义的。

package relate;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        
        // 2.获取成员变量
        Field name = c.getDeclaredField("name");
        Field age = c.getDeclaredField("age");
        
        // 3. 赋予访问私有字段权限
        age.setAccessible(true);
        
        // 3.给字段赋值
        Student student = new Student();
        name.set(student, "观止");
        age.set(student, 20);
        
        // 打印对象并获取字段值
        System.out.println(student + "===>" + name.get(student) + "===>" + age.get(student));
    }
}

// 打印结果
// Student{name='观止', age=20}===>观止===>20

我们以前都是通过对象.getXX获取字段值和对象.setXX()设置值,而通过反射则是以XX.get(对象)获取字段值和xx.set(对象,X)设置值,刚好相反。

五.获取方法对象

(1) 引入

我们同样可以通过反射获取类对象的任意方法对象并且手动进行触发操作。

在这里插入图片描述

Class类也提供了一些方法供我们获取方法对象:

方法说明
Method[] getMethods()返回所有成员方法对象的数组(只能拿public的)
Method[] getDeclaredMethods()返回所有成员方法对象的数组,存在就能拿到
Method getMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象(只能拿public的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象,存在就能拿到

需要注意的是:

  • getMethods将返回所有的公共方法,包括从父类中继承来的公共方法
  • getDeclaredMethods只返回自身包含的全部方法。

我们为学生类添加三个方法用于测试:

    public void run() {
        System.out.println("invoke public run...");
    }

    public void sing(String name) {
        System.out.println("sing " + name);
    }

    private void dance() {
        System.out.println("invoke private dance...");
    }

    private void eat(String name) {
        System.out.println("eat " + name);
    }

(2) 获取多个方法

(2.1) 公有方法

我们可以通过getMethods获取类中所有的方法包括其父类中的方法。

package relate;

import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取多个成员方法
        Method[] methods = c.getMethods();
        // 3.遍历
        for (Method method : methods) {
		System.out.println("方法名称:"+method.getName()+",返回值类型:"+method.getReturnType()+",参数个数:"+method.getParameterCount());
        }
    }
}
// 除了Student类中的方法,其继承自Object的方法也会打印
// 方法名称:run,返回值类型:void,参数个数:0
// 方法名称:toString,返回值类型:class java.lang.String,参数个数:0
// 方法名称:wait,返回值类型:void,参数个数:2
// 方法名称:wait,返回值类型:void,参数个数:1
// 方法名称:wait,返回值类型:void,参数个数:0
// 继承自Object.....

(2.2) 任意方法

我们可以看到上述虽然获取到了很多方法,但并没有获取到私有的两个方法,我们可以通过getDeclaredMethods获取任意方法。

package relate;

import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取多个成员方法
        Method[] methods = c.getDeclaredMethods();
        // 3.遍历
        for (Method method : methods) {
            System.out.println("方法名称:"+method.getName()+",返回值类型:"+method.getReturnType()+",参数个数:"+method.getParameterCount());
        }
    }
}
// Student类中声明的任意方法
// 方法名称:run,返回值类型:void,参数个数:0
// 方法名称:toString,返回值类型:class java.lang.String,参数个数:0
// 方法名称:dance,返回值类型:void,参数个数:0
// 方法名称:eat,返回值类型:void,参数个数:1

(3) 获取单个方法

(3.1) 公有方法

我们可以通过getMethod获取单个公有方法对象。

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取公有成员方法
        Method run = c.getMethod("run"); // 无参直接方法名获取
        Method sing = c.getMethod("sing", String.class); // 带参数需要指定每个参数类型

        // 3.打印结果
        System.out.println("方法名称:"+run.getName()+",返回值类型:"+run.getReturnType()+",参数个数:"+run.getParameterCount());
        System.out.println("方法名称:"+sing.getName()+",返回值类型:"+sing.getReturnType()+",参数个数:"+sing.getParameterCount());
    }
}

// 打印结果
// 方法名称:run,返回值类型:void,参数个数:0
// 方法名称:sing,返回值类型:void,参数个数:1

(3.2) 私有方法

我们可以通过getMethod获取任意方法对象。

public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取私有成员方法
        Method dance = c.getDeclaredMethod("dance"); // 无参直接方法名获取
        Method eat = c.getDeclaredMethod("eat", String.class); // 带参数需要指定每个参数类型

        // 3.打印结果
        System.out.println("方法名称:"+dance.getName()+",返回值类型:"+dance.getReturnType()+",参数个数:"+dance.getParameterCount());
        System.out.println("方法名称:"+eat.getName()+",返回值类型:"+eat.getReturnType()+",参数个数:"+eat.getParameterCount());
    }
}
// 打印结果
// 方法名称:dance,返回值类型:void,参数个数:0
// 方法名称:eat,返回值类型:void,参数个数:1

(4) 触发方法

我们可以触发获取到的方法,不管它是否为私有方法。

方法说明
Object invoke(Object obj, Object… args)参数一:通过对象调用该方法 参数二:调用方法的传递的参数(如果没有可以不写) 返回值:方法的返回值(如果没有可以不写)
public class Test {
    public static void main(String[] args) throws Exception {
        // 1.获取类对象
        Class c = Student.class;
        // 2.获取私有成员方法
        Method run = c.getDeclaredMethod("run");
        Method eat = c.getDeclaredMethod("eat", String.class);

        // 2.1 获取执行私有方法权限
        eat.setAccessible(true);

        // 3.创建对象用于触发方法
        Student student = new Student();
        // 4. 触发方法
        Object runRes = run.invoke(student); // 没返回值runRes为null
        Object eatRes = eat.invoke(student, "水果");  // 没返回值eatRes为null

        // 3.打印结果
        System.out.println("runRes===>" + runRes + ",eatRes===>" + eatRes);
    }
}
// 打印结果
// invoke public run...
// eat 水果
// runRes===>null,eatRes===>null

六.作用

(1) 泛型擦除

由于反射是作用在运行时的技术,而泛型只在编译阶段可以约束只能操作某种数据类型,使用反射泛型将不能产生对我们产生约束,此时就相当于泛型被擦除了。

例如:我们编写如下代码时,往集合添加Integer类型数据不会产生报错,而添加其他类型数据将无法通过编译。

ArrayList<Integer> list = new ArrayList<>();
list.add(100);   // 合法
// list.add(“观止"); // 产生编译错误
list.add(99);  // 合法

而使用反射操作,此时集合的泛型将不能产生约束,可以为集合存入其他任意类型的元素的。泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了。

public class Test {
    public static void main(String[] args) throws Exception {

        // 1.创建对象
        ArrayList<Integer> list = new ArrayList<>();
        // 尝试添加
        list.add(666);
//        list.add("观止"); // 报错
        // 2.获取类对象
        Class listClass = list.getClass();
        // 3.获取add方法
        Method add = listClass.getDeclaredMethod("add", Object.class);
        // 4.添加其他类型元素
        add.invoke(list,"观止");
        add.invoke(list,999);
        // 5.打印查看数据是否添加
        System.out.println(list);
    }
}
// 打印结果
// [666, 观止, 999]

可以看到外面不单单存在Integer类型数据,还存在它所不允许的String类型数据,跳过了泛型对我们的约束作用。

(2) 通用框架的底层原理

几乎所有的框架实现都用到了反射技术,使用反射可以帮助我们很好的处理多变的情况。

例如:给你任意一个对象,在不清楚对象字段的情况下,可以把对象的字段名称和对应值存储到文件中去。

不使用反射显然我们无法对可变的情况进行固定的操作,更别说获取一些不对外暴露的私有字段。使用反射却能很好的帮助我们解决这种情况。


public class Test {
    public static void main(String[] args) throws Exception {
        Student s = new Student("彭于晏", 40, '男', 177.5, "男星");
        printAllFiled(s);
        Teacher t = new Teacher("观止", 6000);
        printAllFiled(t);

    }

    public static void printAllFiled(Object obj) throws IllegalAccessException {
        // 1.获取类对象
        Class objClass = obj.getClass();
        // 2. 获取类名打印横幅
        System.out.println("========" + objClass.getSimpleName() + "========");
        // 3.获取所有字段
        Field[] fields = objClass.getDeclaredFields();
        // 4.打印所有字段以及字段值
        for (Field field : fields) {
            // 5. 允许访问操作私有字段
            field.setAccessible(true);
            System.out.println(field.getName() + " = " + field.get(obj));
        }
    }
}

可以看到即使我们的类结构存在差异它也能正常的获取并打印数值。

(3) 简单模拟SpringMVC

我们知道使用SpringMVC大幅度的简化了我们使用Servlet开发需要重复写大量类和重复代码的烦恼。我们使用反射加上上文所学的自定义注解可以简单模拟一下SpringMVC中@RequestMapping的功能。

  • 导入servlet所需要的坐标并进行一些配置
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!--    确保能获取方法参数列表真实名称-->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf8</encoding>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
  • 自定义@RequestMapping注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    String value();
}
  • 创建一个Servlet简单模拟在SpringMVC中配置请求路径的写法
// 接收以AppServlet开头的任意路径
@WebServlet("/AppServlet/*")
public class AppServlet extends HttpServlet {

    // mvc请求路径 -> 方法对象
    Map<String, Method> hashmap = new HashMap<>();

    // 获取并加载所有存在注解的映射路径以及方法对象
    @Override
    public void init() {
        // 1. 获取类对象
        Class<AppServlet> appServletClass = AppServlet.class;
        // 2. 获取所有方法
        Method[] methods = appServletClass.getDeclaredMethods();
        // 3. 保存所有路径
        for (Method method : methods) {
            // 判断是否存在注解
            boolean present = method.isAnnotationPresent(RequestMapping.class);
            if (present) {
                // 将路径+方法对象存入map
                RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                String path = annotation.value();
                hashmap.put(path, method);
            }
        }
    }

 	// 作为中转站,根据请求路径转发到对应注解匹配的路径
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("utf-8");    //设置 HttpServletResponse使用utf-8编码
        response.setHeader("Content-Type", "text/html;charset=utf-8");    //通知浏览器使用utf-8解码
        // 0.获取请求路径
        String requestURI = request.getRequestURI();
        String targetPath = requestURI.substring("/MyMVC_war/AppServlet".length());
        // 获取请求参数集合
        ArrayList list = new ArrayList();
        Map<String, String[]> parameterMap = request.getParameterMap();
        // 3. 创建对象用于触发方法
        AppServlet appServlet = new AppServlet();
        // 判断请求路径是否存在
        if (hashmap.containsKey(targetPath)) {
            // 存在调用方法
            try {
                Method method = hashmap.get(targetPath);
                Object res = "";
                // 判断方法是否带参数
                if (method.getParameterCount() == 0) {
                    // 无参
                    res = method.invoke(appServlet);
                } else {
                    // 带参
                    for (Parameter parameter : method.getParameters()) {
                        // 类型转换适配
                        String value = parameterMap.get(parameter.getName())[0];
                        if (value == null) {
                            throw new RuntimeException("参数名称不匹配");
                        }
                        list.add(convert(parameter.getType(), value));
                    }
                    // 参数传递
                    res = method.invoke(appServlet, list.toArray());
                }
                response.getWriter().write(res.toString());
            } catch (Exception e) {
                throw new RuntimeException("server error 500!", e);
            }
        } else {
            response.getWriter().write("路径不存在");
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        doGet(request, response);
    }

    // 将获取到的参数转为指定类型
    public static Object convert(Class<?> type, String str) {
        // 整型
        if (type.isAssignableFrom(int.class) || type.isAssignableFrom(Integer.class)) {
            return Integer.valueOf(str);
        } else if (type.isAssignableFrom(double.class) || type.isAssignableFrom(Double.class)) {
            // 浮点型
            return Double.valueOf(str);
        } else {
            // 字符串
            return str;
        }
    }

	// 模拟SpringMVC写法 
    @RequestMapping("/add")
    private Object add(String name, int age) {
        return "add invoke ==> " + name + " ==> " + age;
    }

    @RequestMapping("/delete")
    private Object delete(int id) {
        return "delete invoke:" + id;
    }

    @RequestMapping("/update")
    private Object update() {
        return "update invoke";
    }

    @RequestMapping("/select")
    private Object select() {
        return "select invoke";
    }

}
  • 通过postman进行测试
    在这里插入图片描述
    可以看到我们成功通过反射将请求路径映射到注解所对应的方法中

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

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

相关文章

如何清理电脑缓存,分享4个简单方法!

案例&#xff1a;如何清理电脑缓存 【朋友们&#xff01;我感觉我电脑的内存已经严重不足了&#xff0c;想清理下电脑的缓存释放空间&#xff0c;却不知如何清理&#xff0c;大家有什么好的方法吗&#xff1f;】 经常使用电脑的朋友可能都会发现&#xff0c;好像我们没下载什…

平台和编译器决定 char 是 signed char 或者 unsigned char

平台和编译器决定 char 是 signed char 或者 unsigned charThe C and C standards allows the character type char to be signed or unsigned, depending on the platform and compiler. Most systems, including x86 GNU/Linux and Microsoft Windows, use signed char, but …

NetCore3.1或Net6.0项目升级到Net7.0

其实与我之前发布的步骤基本一致&#xff0c;升级到net6.0之后&#xff0c;在升级net7.0基本没有可修改的代码&#xff0c;只是升级一些nuget包而已&#xff0c;NetCore3.1升级到Net6.0&#xff0c;可参考此文章&#xff1a;NetCore3.1项目升级到Net6.0_csdn_aspnet的博客-CSDN…

Rust China Conf 2023 筹备启动:议题征集开始

大会介绍Rust China Conf 2023 由 Rust 中文社区发起主办、知名企业和开源组织联合协办&#xff0c;是年度国内规模最大并唯一的 Rust 线下大型会议&#xff0c;深受 Rust 中文社区开发者与相关企业的喜爱与推崇。本次大会为线下会议&#xff0c;将于6月17日-18日在上海举办&am…

Java虚拟机总结

前言 Java是目前用户最多、使用范围最广的软件开发技术之一。Java的技术体系主要由支撑Java程序运行的虚拟机、提供各开发领域接口支持的Java APl、,Java编程语言及许多第三方Java框架&#xff08;如Spring、Struts等&#xff09;构成。在国内&#xff0c;有关Java APl、Java语…

【蓝桥杯嵌入式】第十四届蓝桥杯嵌入式省赛[第一场]程序设计题以及详细题解

文章目录原题展示原题分析原题题解LED相关LCD相关按键相关ADC相关定时器相关PWM输入捕获小结文章福利原题展示 原题分析 今年的第一场比赛绝对np,官方将串口直接省掉了&#xff0c;将其替换成很多小功能&#xff0c;如&#xff1a;切换计时、频率均匀变化、锁机制等等&#xff…

Angular 全屏后选择器 (nz-select) 下拉选项框失效【开发笔记】

问题&#xff1a;Angular 全屏后选择器 (nz-select) 下拉选择无法使用 如图&#xff1a; 相应解决方法的文章&#xff1a;https://medium.com/shahar.kazaz/adding-fullscreen-support-to-ng-zorro-a38140da676 三种解决方法&#xff1a; ① FullscreenOverlyContainer&#…

SAP中用CS20批量修改BOM应用问题处理实例

在应用中可能会遇到这样的情况&#xff0c;用户通过某个工艺或技术上的改进&#xff0c;节约了某个原料的用量&#xff0c;而这个原料可能应用在一批成品上。如果成品数量太大&#xff0c;就需要做批量的变更了。 CS20这个事务应该就是用于做BOM批量处理的&#xff0c;笔者之前…

C++基础回顾(上)

C基础回顾&#xff08;上&#xff09; 目录C基础回顾&#xff08;上&#xff09;前言关键字和标识符运算符数据类型函数类前言 C之前学过一点&#xff0c;但是很长时间都没用过&#xff0c;翻出了书从头看了一遍&#xff0c;简短地做了笔记&#xff0c;以便自己之后查看和学习…

5、存储引擎

1、查看存储引擎 查看mysql提供什么存储引擎&#xff1a; show engines;2、设置系统默认的存储引擎 查看默认的存储引擎&#xff1a; show variables like %storage_engine%; #或 SELECT default_storage_engine;修改默认的存储引擎 如果在创建表的语句中没有显式指定表的存…

教你精通Java语法之第十二章、递归

目录 一、递归 1.1递归的概念 1.1.1定义 1.1.2原理 1.1.3思路 1.2单路递归 1.2.1阶乘 1.2.2正向输出数字 1.2.3反向输出字符串 1.3多路递归 1.3.1斐波那契数列 1.3.2兔子问题 1.3.3青蛙爬楼梯 1.4汉诺塔问题 1.5猴子吃桃问题 1.6老鼠走迷宫问题 二、递归的时…

从视频中截取gif怎么弄?三步简单完成视频转gif制作

电影、电视剧等短视频充斥着我们的生活&#xff0c;很多小伙伴会将这些视频中的有趣画面提取出来做成Gif动画表情包。那么&#xff0c;怎么才能从视频中提取gif动画呢&#xff1f; 一、使用什么工具才能从视频中提取gif呢&#xff1f; 通过使用GIF中文网这款专业的视频转gif&…

RabbitMQ (工作队列:Work Queues)

本章目录&#xff1a; 什么是Work Queues模拟场景&#xff0c;使用Work Queues官网文档&#xff1a;RabbitMQ tutorial - Work Queues — RabbitMQ 一、何为Work Queues 我们先看下它的结构图 显然&#xff0c;它与入门案例相比只是多了几个消费者。 以下是官方文档说明 In …

【目标检测】目标检测遇上知识图谱:Object detection meets knowledge graphs论文解读与复现

前言 常规的目标检测往往是根据图像的特征来捕捉出目标信息&#xff0c;那么是否有办法加入一些先验信息来提升目标检测的精准度&#xff1f; 一种可行的思路是在目标检测的输出加入目标之间的关联信息&#xff0c;从而对目标进行干涉。 2017年8月&#xff0c;新加波管理大学…

Vue——插槽

目录 插槽内容与出口​ 渲染作用域​ 默认内容​ 具名插槽​ 动态插槽名​ 作用域插槽​ 具名作用域插槽​ 高级列表组件示例​ 无渲染组件​ 插槽内容与出口​ 在之前的章节中&#xff0c;我们已经了解到组件能够接收任意类型的 JavaScript 值作为 props&#xff0c;…

微信小程序 | 基于ChatGPT实现电影推荐小程序

文章目录** 效果预览 **1、根据电影明星推荐2、根据兴趣标签推荐3、根据电影名推荐一、需求背景二、项目原理及架构2.1 实现原理&#xff08;1&#xff09;根据用户的兴趣标签&#xff08;2&#xff09;根据关联类似主题的题材&#xff08;3&#xff09;根据特定的电影明星2.2 …

IK集成ElasticSearch,IK分词器的下载及使用

IK集成ElasticSearch&#xff0c;IK分词器的下载及使用 下载ElasticSearch 8.7.0网址&#xff1a;Download Elasticsearch | Elastic 历史版本地址&#xff1a;Past Releases of Elastic Stack Software | Elastic 解压ElasticSearch 什么是IK分词器 分词∶即把一段中文或…

IO流基础

目录 1.FileOutPutStream字节输入流 1.1FileOutPutStream使用 1.1.1创建对象 FileOutPutStream fos new FileOutPutStream("路径或者File对象")&#xff1b; 1.1.2.写数据 调用write方法&#xff0c;参数是int类型&#xff0c;但传入文件中是asci…

【LeetCode: 剑指 Offer II 112. 最长递增路径 | 递归 | DFS | 深度优先遍历 | 记忆化缓存表】

&#x1f34e;作者简介&#xff1a;硕风和炜&#xff0c;CSDN-Java领域新星创作者&#x1f3c6;&#xff0c;保研|国家奖学金|高中学习JAVA|大学完善JAVA开发技术栈|面试刷题|面经八股文|经验分享|好用的网站工具分享&#x1f48e;&#x1f48e;&#x1f48e; &#x1f34e;座右…

海康工业相机网口相机丢包问题解决方法

​1.1 系统环境设置 1.1.1 网卡设置 网卡推荐使 Intel 芯片的独立千兆网口,例如 intel I350、I210 等芯片组网卡 设置网卡巨型帧为选择 9KB 或 9014 字节 *不同网卡类型,网卡属性有差异,需灵活参考 设置网卡接收与传输缓存区到最大(intel 网卡一般为 2048,realtek 一般…