【Java基础篇】反射

什么是反射

在这里插入图片描述
其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成Class对象,并将字节码文件中的内容都封装成其他对象,比如Constructor、Field、Method、Modifier,这样便于操作这些成员。

在这里插入图片描述

Class对象: Class类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注解是一种接口。每个数组也属于一个类,该类反映为 Class 对象,该对象由具有相同元素类型和维数的所有数组共享。 Java 原始类型(boolean、 byte、 char、 short、 int、 long、 float 和 double),关键字 void也表示为 Class对象。

Constructor<T> 提供关于类的单个构造方法的信息以及对它的访问权限

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
Parameter提供有关方法参数的信息,包括其名称和修饰符。它还提供了一种获取参数属性的替代方法。

Modifier 类提供了 static 方法和常量,对类和成员访问修饰符进行解码。

简单说:反射技术可以对一个类进行解剖。Class看做是反射的源头

反射优点:

  • 提高了Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力。
  • 允许程序创建和控制任何类的对象,无需提前硬编码目标类

反射缺点:

  • 反射的性能较低。
    • 反射机制主要应用在对灵活性和扩展性要求很高的系统框架上。
  • 反射会模糊程序内部逻辑,可读性较差。

体会反射的动态性

正射: 代码中直接创建对象

// 静态性
public User getInstance() {
   return new User();
}

反射

// 反射的动态性
public <T> T getInstance(String className) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    Class clazz = Class.forName(className);

    Constructor constructor = clazz.getDeclaredConstructor();
    constructor.setAccessible(true);
    return (T) constructor.newInstance();
}

反射的基本步骤

1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。

反射常用API

获取对象

获取字节码Class对象

在这里插入图片描述

Class<?> clazz = Class.forName("类的全路径");
Class<?> clazz = 类名.class;
Class<?> clazz = 对象.getClass();

在这里插入图片描述

获取包名&类名

clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名

clazz.getAnnotation(Pro.class); //类的注解信息

获取构造方法定义信息

clazz.getConstructor(参数类型列表)//获取公开的构造方法
clazz.getConstructors()//获取所有的公开的构造方法

clazz.getDeclaredConstructors()//获取所有的构造方法,包括私有
clazz.getDeclaredConstructor(int.class,String.class)

获取方法定义信息

clazz.getMethods()//获取所有可见的方法,包括继承的方法
clazz.getMethod(方法名,参数类型列表)

clazz.getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
clazz.getDeclaredMethod(方法名,int.class,String.class)

获取成员变量定义信息

clazz.getFields()//获取所有公开的成员变量,包括继承变量
clazz.getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量

clazz.getField(变量名)
clazz.getDeclaredField(变量名)

设置对象

使用commons-lang3:3.9提供的反射工具类

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.9</version>
</dependency>

在这里插入图片描述

反射创建实例

jdk方式获取

clazz.newInstance();//执行无参构造创建对象

Constructor<?> c = clazz.getConstructor(String.class,Integer.class)//获取构造方法
c.newInstance("百里玄策",20);//执行含参构造创建对象

使用commons-lang3:3.9提供的反射工具类

/**
 * 测试构造器反射工具类
 */
@Test
public void constructorUtilsTest() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
    User user = ConstructorUtils.invokeConstructor(User.class);
    System.out.println(user);
}

反射调用成员变量

jdk方式获取

clazz.getDeclaredField(变量名);//获取本类定义的成员变量,包括私有,但不包括继承的变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null

使用commons-lang3:3.9提供的反射工具类

 /**
     * 测试属性反射工具类
     */
@Test
public void fieldUtilsTest() throws IllegalAccessException {
    User user = new User();
    user.setUsername("zysheep");
    user.setAge(20);

    // 反射设置属性值
    FieldUtils.writeField(user,"createTime",new Date(),true);

    Date createTime = user.getCreateTime();
    log.info("createTime: {}",createTime);
    // 反射获取属性值,能获取到来自父类的属性
    Object username = FieldUtils.readField(user, "username",true);
    // Field field = FieldUtils.getField(user.getClass(), "username");
    log.info("username: {}",username);
}

反射调用成员方法

jdk方式获取

Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法

使用commons-lang3:3.9提供的反射工具类

/**
 * 测试方法反射工具类
 */
@Test
public void methodUtilsTest() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {

    MethodUtils.invokeMethod(new User(), true, "say");
}

测试反射API

测试实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    public String username;

    Integer age;

    //定义成员方法
    public void run() {
        System.out.println("生命不止,运动不息!");
    }

    @Override
    public String toString() {
        return "Student [name="+ username +",age="+ age +"]";
    }
}

测试获取类对象

/**
* 获取类对象
*  1、Class.forName(“类的全路径”);
*  2、类名.class
*  3、对象.getClass();
*
* @throws Exception
*/
@Test
public void getClazz() throws Exception {

   Class<?> student1 = Class.forName("cn.zysheep.reflect.Student");//此处的参数是类的全路径名[包名+类名]
   Class<?> student2 = Student.class;
   Class<?> student3 = new Student().getClass();//先创建匿名对象,匿名对象没有名字,然后对象的字节码对象

   System.out.println("反射得到的字节码Class对象: "+ student1);
   System.out.println("获取类的全路径名[包名+类名]: "+ student2.getName());
   System.out.println("只获取类名: "+ student3.getSimpleName());
   System.out.println("获取包名: "+student3.getPackage().getName());
}

在这里插入图片描述

测试获取构造方法

/**
* 获取构造方法
*/
@Test
public void getConstruct() {
   //1.获取字节码Class对象
   Class<?> clazz = Student.class;
   //2.获取构造方法们,并放入cs数组中
   Constructor<?>[] cs = clazz.getConstructors();
   //3.获取每个构造,有参构造,无参构造
   //使用增强for循环完成
   for (Constructor c : cs) {
       //拿到每个构造方法以后可以获取构造方法的相关信息
       System.out.println("获取构造方法的名称: "+c.getName());
       Class[] cp = c.getParameterTypes();
       System.out.println("获取构造方法的参数类型,可能有多个: "+Arrays.toString(cp));
   }
}

在这里插入图片描述

测试获取方法

/**
* 获取成员方法
*/
@Test
public void getMethods() throws NoSuchMethodException {
   //1.获取Class字节码对象
   Class<?> clazz = Student.class;

   //2.获取所有的成员变量,公共的!!!
   /**
    * getMethod(String name, Class<?>... parameterTypes)
    *  name: 方法名
    *  parameterTypes: 参数类型字节码
    */
   Method method = clazz.getMethod("run",String.class);
   // 所有方法,包括父类的方法
   Method[] methods = clazz.getMethods();

   //3.遍历数组,获取每个成员变量的信息
   for (Method m: methods) {
       System.out.println("获取方法名: "+m.getName());
       System.out.println("获取参数: "+ Arrays.toString(m.getParameters()));
       System.out.println("获取参数类型: "+ Arrays.toString(m.getParameterTypes()));
       System.out.println("获取返回值类型: "+m.getReturnType());
   }
}

在这里插入图片描述

测试获取成员变量

  /**
     * 获取成员变量
     */
    @Test
    public void getFields(){
        //1.获取Class字节码对象
        //Class<?>中的"?"是泛型约束的通配符,类似于"*"
        Class<?> clazz = Student.class;

        //2.获取所有的成员变量,公共的!!!
        //!!!注意目前成员变量的修饰符必须是public才能获取到,采用默认修饰符就反射不到
        Field[] fs = clazz.getFields();

        //3.遍历数组,获取每个成员变量的信息
        for (Field f: fs) {
            System.out.println("获取变量名: "+f.getName());
            System.out.println("获取变量类型: "+f.getType().getName());
        }
    }

在这里插入图片描述

测试获取标注的注解信息

不设置@Target注解标记的位置,@Retention注解作用的范围,注解不生效

1、定义@Pro注解

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
    // 名字
    String name() default "";
    // 内容描述
    String desc() default "";
}

2、 使用@Pro注解

@Data
@AllArgsConstructor
@NoArgsConstructor
@Pro(name = "学生类",desc = "学生实体类")
public class Student {

   @Pro(name = "username",desc = "学生姓名")
   public String username;

   Integer age;

   //定义成员方法
   @Pro(name = "run",desc = "跑步方法")
   public void run(String username) {
       System.out.println("生命不止,运动不息!"+username);
   }
}

3、获取@Pro注解

/**
* 获取注解信息
* @throws NoSuchFieldException
* @throws NoSuchMethodException
*/
@Test
void getAnnotation() throws NoSuchFieldException, NoSuchMethodException {
   Class<Student> clazz = Student.class;
   Pro var1 = clazz.getAnnotation(Pro.class);
   Pro var2 = clazz.getField("username").getAnnotation(Pro.class);
   Pro var3 = clazz.getMethod("run", String.class).getAnnotation(Pro.class);
   System.out.println("获取类上的注解信息: "+var1);
   System.out.println("获取属性上的注解信息: "+var2);
   System.out.println("获取方法上的注解信息: "+var3);
}

在这里插入图片描述

测试反射创建对象

/**
* 反射创建对象
* 方式一:通过字节码对象直接调用newInstance(),触发无参构造来创建对象
* 方式二:先获取指定的构造函数,再通过构造函数对象调用newInstance(),触发对应的构造函数来创建对象
* @throws Exception
*/
@Test
public void getObject () throws Exception {
   //1.获取Class字节码对象
   Class<?> clazz = Student.class;

   //2.创建对象
   Object obj = clazz.newInstance();//触发无参构造
   System.out.println("反射创建的对象: "+obj);//Student [name=null,age=0]

   //3.可以指定去调用哪个构造方法,注意根据参数来指定,而且传入的是参数的字节码对象
   Constructor<?> c = clazz.getConstructor(String.class,Integer.class);

   //4.触发指定的构造方法
   Object obj2 = c.newInstance("百里玄策",20);
   System.out.println(obj2);//Student [name=百里玄策,age=3]

   //5.查看对象具体的属性值,或者调用方法,需要把Object强转成指定的子类类型
   /**为什么要把Object强转成子类类型?因为想要使用子类的特有功能,父类无法使用子类的特有功能
    * obj是Object类型,Object中没有Student的属性与功能
    * 这个操作叫做向下转型--想使用子类特有功能时需要做此操作
    */
  
   Student student = (Student)obj2;
   System.out.println(student.getUsername()); // 百里玄策
   System.out.println(student.getAge()); // 20
   student.run();  // 生命不止,运动不息!
}

在这里插入图片描述
注意: 获取有参构造时Constructor<?> c = clazz.getConstructor(String.class,Integer.class),参数类型的顺序必须和构造方法参数一致,否则反射失败
在这里插入图片描述

暴力反射

指可以将程序中的私有的属性或者方法通过反射技术,通过设置私有可见.setAccessible(true),暴力的获取到资源。
1、反射是解释执行的,和对象.方法执行对比,对执行速度有影响,比较耗时。
2、反射调用优化: MethodFieldConstructor对象都有setAccessible()方法。参数设置为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查。

需要使用的常见方法如下:
在这里插入图片描述

测试实体类

@Data
public class Person {
   // 提供私有属性
   private String username;
   private Integer age ;

   private Person(){
       System.out.println("执行私有构造");
   }

   private Person(String username){
       System.out.println("执行有参私有构造: "+ username);
   }

   // 提供私有方法
   private void save(Integer age,String username) {
       System.out.println("执行私有方法save().."+username+age);
   }
}

测试获取私有构造

/**
* 暴力反射获取私有方法
*
* @throws ClassNotFoundException
* @throws NoSuchMethodException
*/
@Test
public void getConstructor() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
   Class<?> clazz = Class.forName("cn.zysheep.reflect.Person");
   // 获取私有无参构造
   Constructor<?> constructor1 = clazz.getDeclaredConstructor();
   // 获取私有有参构造
   Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);
   // 设置无参私有构造可见
   constructor1.setAccessible(true);
   Object var1 = constructor1.newInstance();
   
   // 创建实例,没有设置私有可见会报错
   constructor2.setAccessible(true);
   constructor2.newInstance("百里玄策");
}

注释constructor1.setAccessible(true);运行报错
在这里插入图片描述

测试获取和设置私有属性

/**
 * 暴力反射获取和设置私有属性
 * @throws Exception
 */
@Test
public void getFields() throws Exception {
    //1.获取Class字节码对象
    Class<?> clazz = Person.class;
    //2.获取私有属性
    Field field = clazz.getDeclaredField("username");
    //3.根据获取到的属性对象获取属性的类型
    System.out.println(field.getType().getName());//java.lang.String
    System.out.println(field.getType());//class java.lang.String

    //4.设置属性的值
    // 获取所有构造器
    Constructor<?> constructor = clazz.getDeclaredConstructor();
    // 设置私有构造器可见
    constructor.setAccessible(true);
    // 创建对象
    Object obj = constructor.newInstance();

    //4.2暴力反射!!!需要设置私有属性可见
    field.setAccessible(true);

    //4.3通过字段对象来给刚刚创建的对象obj设置username属性值为“百里玄策”
    //set(Object obj, Object value)
    // obj:要给哪个对象设置属性值,
    // value:是要设置的属性的具体值
    field.set(obj,"百里玄策");
    //4.4通过字段对象获取刚刚给obj对象设置的私有属性的值
    System.out.println(field.get(obj));
    System.out.println(obj);
}

在这里插入图片描述

测试获取和设置私有方法

/**
* 暴力反射获取和设置私有方法
* @throws Exception
*/
@Test
public void getFunction() throws Exception {
   //1.获取Class字节码对象
   Class<?> clazz = Person.class;
   //2.通过暴力反射获取私有方法
   /**
    * Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    * name:要获取的方法名
    * parameterTypes可变参数,是这个方法的参数类型,但注意要加“.class”
    */
   Method method = clazz.getDeclaredMethod("save",Integer.class,String.class);

   //3.1没有对象就通过反射的方式创建对象
   Constructor<?> constructor = clazz.getDeclaredConstructor();
   constructor.setAccessible(true);
   Object obj = constructor.newInstance();

   //3.2 想要执行私有方法,也需要先设置私有方法可见
   method.setAccessible(true);
   /**invoke(Object obj, Object... args),表示通过反射技术执行方法
    * obj :要执行的是哪个对象的方法
    * args:执行这个方法【method对象代表的之前获取到的save()】时需要传入的参数
    */

   //3.3 通过反射技术invoke(),执行目标对象obj的目标方法method【save()】
   //save()被调用时传入的参数是100,"百里玄策"
   method.invoke(obj,100,"百里玄策");
}

在这里插入图片描述

/**
* Method getDeclaredMethod(String name, Class<?>... parameterTypes)
* name:要获取的方法名
* parameterTypes可变参数,是这个方法的参数类型,但注意要加“.class”
*/
Method method = clazz.getDeclaredMethod("save",Integer.class,String.class);

获取私有方法参数类型的顺序和私有方法类型顺序必须一致,否则报错

Java反射机制实际应用

Java反射机制提供的功能:

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法。
  • 在运行时处理注解
  • 生成动态代理

Java在运行时获取泛型实例的方法

java中的泛型其实是伪泛型,java在编译阶段会对变异类型进行擦除,擦出到泛型类的最小上限,编译后得到的class文件里面是没有任何泛型信息的,泛型的控制其实就是java编译器进行的控制,编译阶段进行泛型检查。那如果我们想在运行时知道泛型类的类型,如何做到呢?主要用到下面几个api:
1、public Type getGenericSuperclass()—Class类的方法
2、Type[] getActualTypeArguments()—ParameterizedType方法
3、public Type[] getGenericInterfaces()—Class类的方法

定义一个抽象的泛型类

public abstract class BaseDao<T> {}

再定义一个子类

public class BaseDaoImpl extends BaseDao<String> {}
public static void main(String[] args) throws ClassNotFoundException {
    System.out.println(BaseDaoImpl.class.getGenericSuperclass());
    System.out.println(BaseDaoImpl.class.getSuperclass());
    System.out.println(BaseDao.class.getGenericSuperclass());
    System.out.println(BaseDao.class.getSuperclass());
}

运行结果如下:
在这里插入图片描述
从上面运行结果可以看出两点:
1、通过Class类的getGenericSuperclass()是可以获取到泛型信息的
2、泛型类本身是无法获取到泛型信息的,只能通过泛型类的子类来获取泛型信息。这也很好解释,因为泛型类比如BaseDao类自身,其泛型参数并未确定,自然无法获取泛型信息,而子类的泛型参数类型已经确定,父类泛型信息已经确定,所以可以查询。

具体的查询泛型参数类型的代码如下:

抽象类获取泛型实例的方法

public static void main(String[] args) throws ClassNotFoundException {
     Class clazz = Class.forName("cn.zysheep.ref.BaseDaoImpl");
     Type type = clazz.getGenericSuperclass();
     ParameterizedType parameterizedType = (ParameterizedType)type;
     System.out.println(parameterizedType.getClass().getName());
     System.out.println(parameterizedType);
     Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

     for(Type actualTypeArgument: actualTypeArguments) {
         System.out.println(actualTypeArgument.getClass().getName());
         System.out.println(actualTypeArgument);
          System.out.println(actualTypeArgument.getTypeName());
     }
 }

运行结果如下:
在这里插入图片描述

由上图运行结果可知:
1、getGenericSuperclass()返回的实际类型为ParameterizedTypeImpl,这里面带有父类的泛型类型信息;
2、获取泛型参数信息是通过ParameterizedType的getActualTypeArguments方法,该方法返回的是一个Type类型的数据,该数据存放的数据的实际类型为Class,也就是我们得到了泛型参数的类信息。

接口获取泛型实例的方法

public  interface IService<T> {}
public class UserServiceImpl implements IService<String> {}

获取IService的泛型参数类型的方法如下:

public static void main(String[] args) throws ClassNotFoundException {
    ParameterizedType parameterizedType = (ParameterizedType) UserServiceImpl.class.getGenericInterfaces()[0];
    System.out.println(parameterizedType.getClass().getName());
    System.out.println(parameterizedType);
    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
    for(Type actualTypeArgument: actualTypeArguments) {
        System.out.println(actualTypeArgument.getClass().getName());
        System.out.println(actualTypeArgument);
        System.out.println(actualTypeArgument.getTypeName());
    }
}

运行结果如下:
在这里插入图片描述

和泛型类区别就是getGenericSuperclass()方法换成了getGenericInterfaces(),getGenericInterfaces()方法返回的是Type[],因为一个类可以实现多个接口,所以想要获取哪个接口的泛型信息,需要指定数据下标,这里UserServiceImpl类就实现了一个接口,所以直接UserServiceImpl.class.getGenericInterfaces()[0]就可以了。

上面的代码有一个共同点,就是泛型类或者泛型接口已经有一个子类了,通过子类的Class信息可以获取到泛型父类或者泛型接口的泛型类型信息,那么如果没有子类,怎么直接获取泛型类的泛型类型信息呢?很简单,通过匿名内部类(其实还是通过子类)

new BaseDao<Integer>() {}.getClass().getGenericSuperclass();

new IService<Integer>() {}.getClass().getGenericInterfaces())[0];

在运行时处理注解

private String getTableName(Class<?> clazz) throws NotFoundAnnotationException {
    if (clazz.isAnnotationPresent(Entity.class)) {
        Entity entity = clazz.getAnnotation(Entity.class);
        return entity.value();
    } else {
        throw new NotFoundAnnotationException(clazz.getName() + " is not Entity Annotation.");
    }
}

Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
    PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
    if (field.isAnnotationPresent(Id.class)) {
        Id id = field.getAnnotation(Id.class);
    } else if (field.isAnnotationPresent(Column.class)) {
        Column coulmn = field.getAnnotation(Column.class);
    }
}

GenericUtil 工具类

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * 运行时获取泛型类型
 */
public class GenericUtil {
    public static <T> T getSuperclassType(Object obj, int i) {
        try {
            Class<?> aClass = obj.getClass();//先得到类的字节码
            Type genericSuperclass = aClass.getGenericSuperclass();
            if (genericSuperclass instanceof ParameterizedType) {
                ParameterizedType types = (ParameterizedType) genericSuperclass;//抽象类
                Type[] actualTypeArguments = types.getActualTypeArguments();
                Class<T> reponseClass = (Class) actualTypeArguments[i];
                return reponseClass.newInstance();
            } else {
                return null;
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> T getInterfaceType(Object obj, int i) {
        try {
            Class<?> aClass = obj.getClass();//先得到类的字节码
            Type[] types = aClass.getGenericInterfaces();
            ParameterizedType parameterizedType = (ParameterizedType) types[0];
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            Class<T> reponseClass = (Class) actualTypeArguments[i];
            return reponseClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Class<?> forName(String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

注解+泛型+反射实现通用CRUD工具类

@TableName、@Column、@Id

1、@TableName注解 : 表名称注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TableName {
    String value();
}

2、@Column 注解 : 表属性字段注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
    /**
     * 字段名称
     */
    String value();
    /**
     * 字段的类型
     * @return
     */
    Class<?> type() default String.class;
    /**
     * 字段的长度
     * @return
     */
    int length() default 0;
}

3、@Id 注解 : 表主键字段注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Id {
    String value();
}

NotFoundAnnotationException

public class NotFoundAnnotationException extends RuntimeException {
    public NotFoundAnnotationException() {
    }

    public NotFoundAnnotationException(String message) {
        super(message);
    }
}

JdbcDaoHelper

public class JdbcDaoHelper {
    /**
     * 数据库用户名
     */
    private static final String USER = "root";
    /**
     * 数据库密码
     */
    private static final String PASSWORD = "root";
    /**
     * 连接数据库的地址
     */
    private static final String URL = "jdbc:mysql//localhost:3306/study";
    private static Connection conn;
    /**
     * 获得一个数据库连接对象
     * @return java.sql.Connection实例
     */
    public static Connection getConnection() {
        try {
            if (conn == null) {
                Class.forName("oracle.jdbc.OracleDriver");
                conn = DriverManager.getConnection(URL, USER, PASSWORD);
            } else {
                return conn;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;
    }
    /**
     * 释放数据库资源
     */
    public static void release(PreparedStatement ps, ResultSet rs) {
        try {
            if (conn != null) {
                conn.close();
                conn = null;
            }
            if (ps != null) {
                ps.close();
                ps = null;
            }
            if (rs != null) {
                rs.close();
                rs = null;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

BaseDao

import java.util.List;
import java.util.Map;

public interface BaseDao<T> {

    JdbcTemplate jdbcTemplate = new JdbcTemplate();

    void save(T t) throws Exception;

    void delete(Object id, Class<T> clazz) throws Exception;

    void update(T t) throws Exception;

    T get(Object id, Class<T> clazz) throws Exception;

    /**
     * 根据条件查询
     *
     * @param sqlWhereMap key:条件字段名 value:条件字段值
     * @param clazz
     * @return
     * @throws Exception
     */
    List<T> findAllByConditions(Map<String, Object> sqlWhereMap, Class<T> clazz) throws Exception;
}

BaseDaoImpl

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.*;
import java.util.Date;

public abstract class BaseDaoImpl<T> implements BaseDao<T> {

    //表的别名
    private static final String TABLE_ALIAS = "t";

    @Override
    public void save(T t) throws Exception {
        Class<?> clazz = t.getClass();
        //获得表名
        String tableName = getTableName(clazz);
        //获得字段
        StringBuilder fieldNames = new StringBuilder();        //字段名
        List<Object> fieldValues = new ArrayList<Object>();    //字段值
        StringBuilder placeholders = new StringBuilder();    //占位符

        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
            if (field.isAnnotationPresent(Id.class)) {
                fieldNames.append(field.getAnnotation(Id.class).value()).append(",");
                fieldValues.add(pd.getReadMethod().invoke(t));
            } else if (field.isAnnotationPresent(Column.class)) {
                fieldNames.append(field.getAnnotation(Column.class).value()).append(",");
                fieldValues.add(pd.getReadMethod().invoke(t));
            }
            placeholders.append("?").append(",");
        }
        //删除最后一个逗号
        fieldNames.deleteCharAt(fieldNames.length() - 1);
        placeholders.deleteCharAt(placeholders.length() - 1);
        //拼接sql
        StringBuilder sql = new StringBuilder("");
        sql.append("insert into ").append(tableName)
                .append(" (").append(fieldNames.toString())
                .append(") values (").append(placeholders).append(")");
         PreparedStatement ps = JdbcDaoHelper.getConnection().prepareStatement(sql.toString());
        //设置SQL参数占位符的值
		setParameter(fieldValues, ps, false);
        //执行SQL
        ps.execute();
        JdbcDaoHelper.release(ps, null);
        System.out.println(sql + "\n" + clazz.getSimpleName() + "添加成功!");
    }

    @Override
    public void delete(Object id, Class<T> clazz) throws Exception {
        //获得表名
        String tableName = getTableName(clazz);
        //获得ID字段名和值
        String idFieldName = "";
        boolean flag = false;
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Id.class)) {
                idFieldName = field.getAnnotation(Id.class).value();
                flag = true;
                break;
            }
        }
        if (!flag) {
            throw new NotFoundAnnotationException(clazz.getName() + " object not found id property.");
        }
        //拼装sql
        String sql = "delete from " + tableName + " where " + idFieldName + "=?";
        PreparedStatement ps = JdbcDaoHelper.getConnection().prepareStatement(sql);
        ps.setObject(1, id);
        //执行SQL
        ps.execute();
        JdbcDaoHelper.release(ps, null);
        System.out.println(sql + "\n" + clazz.getSimpleName() + "删除成功!");
    }

    @Override
    public void update(T t) throws Exception {
        Class<?> clazz = t.getClass();
        //获得表名
        String tableName = getTableName(clazz);
        //获得字段
        List<Object> fieldNames = new ArrayList<Object>();    //字段名
        List<Object> fieldValues = new ArrayList<Object>();    //字段值
        List<String> placeholders = new ArrayList<String>();//占位符
        String idFieldName = "";
        Object idFieldValue = "";
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            PropertyDescriptor pd = new PropertyDescriptor(field.getName(), t.getClass());
            if (field.isAnnotationPresent(Id.class)) {
                idFieldName = field.getAnnotation(Id.class).value();
                idFieldValue = pd.getReadMethod().invoke(t);
            } else if (field.isAnnotationPresent(Column.class)) {
                fieldNames.add(field.getAnnotation(Column.class).value());
                fieldValues.add(pd.getReadMethod().invoke(t));
                placeholders.add("?");
            }
        }
        //ID作为更新条件,放在集合中的最后一个元素
        fieldNames.add(idFieldName);
        fieldValues.add(idFieldValue);
        placeholders.add("?");
        //拼接sql
        StringBuilder sql = new StringBuilder("");
        sql.append("update ").append(tableName).append(" set ");
        int index = fieldNames.size() - 1;
        for (int i = 0; i < index; i++) {
            sql.append(fieldNames.get(i)).append("=").append(placeholders.get(i)).append(",");
        }
        sql.deleteCharAt(sql.length() - 1).append(" where ").append(fieldNames.get(index)).append("=").append("?");
        //设置SQL参数占位符的值
        PreparedStatement ps = JdbcDaoHelper.getConnection().prepareStatement(sql.toString());
        setParameter(fieldValues, ps, false);
        //执行SQL
        ps.execute();
        JdbcDaoHelper.release(ps, null);
        System.out.println(sql + "\n" + clazz.getSimpleName() + "修改成功.");
    }

    @Override
    public T get(Object id, Class<T> clazz) throws Exception {
        String idFieldName = "";
        Field[] fields = clazz.getDeclaredFields();
        boolean flag = false;
        for (Field field : fields) {
            if (field.isAnnotationPresent(Id.class)) {
                idFieldName = field.getAnnotation(Id.class).value();
                flag = true;
                break;
            }
        }
        if (!flag) {
            throw new NotFoundAnnotationException(clazz.getName() + " object not found id property.");
        }
        //拼装SQL
        Map<String, Object> sqlWhereMap = new HashMap<String, Object>();
        sqlWhereMap.put(TABLE_ALIAS + "." + idFieldName, id);
        List<T> list = findAllByConditions(sqlWhereMap, clazz);
        return list.size() > 0 ? list.get(0) : null;
    }

    @Override
    public List<T> findAllByConditions(Map<String, Object> sqlWhereMap, Class<T> clazz) throws Exception {
        List<T> list = new ArrayList<T>();
        String tableName = getTableName(clazz);
        String idFieldName = "";
        //存储所有字段的信息
        //通过反射获得要查询的字段
        StringBuffer fieldNames = new StringBuffer();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String propertyName = field.getName();
            if (field.isAnnotationPresent(Id.class)) {
                idFieldName = field.getAnnotation(Id.class).value();
                fieldNames.append(TABLE_ALIAS + "." + idFieldName)
                        .append(" as ").append(propertyName).append(",");
            } else if (field.isAnnotationPresent(Column.class)) {
                fieldNames.append(TABLE_ALIAS + "." + field.getAnnotation(Column.class).value())
                        .append(" as ").append(propertyName).append(",");
            }
        }
        fieldNames.deleteCharAt(fieldNames.length() - 1);
        //拼装SQL
        String sql = "select " + fieldNames + " from " + tableName + " " + TABLE_ALIAS;
        PreparedStatement ps = null;
        List<Object> values = null;
        if (sqlWhereMap != null) {
            List<Object> sqlWhereWithValues = getSqlWhereWithValues(sqlWhereMap);
            if (sqlWhereWithValues != null) {
                //拼接SQL条件
                String sqlWhere = (String) sqlWhereWithValues.get(0);
                sql += sqlWhere;
                //得到SQL条件中占位符的值
                values = (List<Object>) sqlWhereWithValues.get(1);
            }
        }
        //设置参数占位符的值
        if (values != null) {
            ps = JdbcDaoHelper.getConnection().prepareStatement(sql);
            setParameter(values, ps, true);
        } else {
            ps = JdbcDaoHelper.getConnection().prepareStatement(sql);
        }
        //执行SQL
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            T t = clazz.newInstance();
            initObject(t, fields, rs);
            list.add(t);
        }
        //释放资源
        JdbcDaoHelper.release(ps, rs);
        System.out.println(sql);
        return list;
    }

    /**
     * 根据结果集初始化对象
     */
    private void initObject(T t, Field[] fields, ResultSet rs)
            throws SQLException, IntrospectionException,
            IllegalAccessException, InvocationTargetException, IntrospectionException {
        for (Field field : fields) {
            String propertyName = field.getName();
            Object paramVal = null;
            Class<?> clazzField = field.getType();
            if (clazzField == String.class) {
                paramVal = rs.getString(propertyName);
            } else if (clazzField == short.class || clazzField == Short.class) {
                paramVal = rs.getShort(propertyName);
            } else if (clazzField == int.class || clazzField == Integer.class) {
                paramVal = rs.getInt(propertyName);
            } else if (clazzField == long.class || clazzField == Long.class) {
                paramVal = rs.getLong(propertyName);
            } else if (clazzField == float.class || clazzField == Float.class) {
                paramVal = rs.getFloat(propertyName);
            } else if (clazzField == double.class || clazzField == Double.class) {
                paramVal = rs.getDouble(propertyName);
            } else if (clazzField == boolean.class || clazzField == Boolean.class) {
                paramVal = rs.getBoolean(propertyName);
            } else if (clazzField == byte.class || clazzField == Byte.class) {
                paramVal = rs.getByte(propertyName);
            } else if (clazzField == char.class || clazzField == Character.class) {
                paramVal = rs.getCharacterStream(propertyName);
            } else if (clazzField == Date.class) {
                paramVal = rs.getTimestamp(propertyName);
            } else if (clazzField.isArray()) {
                paramVal = rs.getString(propertyName).split(",");    //以逗号分隔的字符串
            }
            PropertyDescriptor pd = new PropertyDescriptor(propertyName, t.getClass());
            pd.getWriteMethod().invoke(t, paramVal);
        }
    }

    /**
     * 根据条件,返回sql条件和条件中占位符的值
     *
     * @param sqlWhereMap key:字段名 value:字段值
     * @return 第一个元素为SQL条件,第二个元素为SQL条件中占位符的值
     */
    private List<Object> getSqlWhereWithValues(Map<String, Object> sqlWhereMap) {
        if (sqlWhereMap.size() < 1) return null;
        List<Object> list = new ArrayList<Object>();
        List<Object> fieldValues = new ArrayList<Object>();
        StringBuffer sqlWhere = new StringBuffer(" where ");
        Set<Map.Entry<String, Object>> entrySets = sqlWhereMap.entrySet();
        for (Iterator<Map.Entry<String, Object>> iteraotr = entrySets.iterator(); iteraotr.hasNext(); ) {
            Map.Entry<String, Object> entrySet = iteraotr.next();
            fieldValues.add(entrySet.getValue());
            Object value = entrySet.getValue();
            if (value.getClass() == String.class) {
                sqlWhere.append(entrySet.getKey()).append(" like ").append("?").append(" and ");
            } else {
                sqlWhere.append(entrySet.getKey()).append("=").append("?").append(" and ");
            }
        }
        sqlWhere.delete(sqlWhere.lastIndexOf("and"), sqlWhere.length());
        list.add(sqlWhere.toString());
        list.add(fieldValues);
        return list;
    }

    /**
     * 获得表名
     */
    private String getTableName(Class<?> clazz) throws NotFoundAnnotationException {
        if (clazz.isAnnotationPresent(TableName.class)) {
            TableName entity = clazz.getAnnotation(TableName.class);
            return entity.value();
        } else {
            throw new NotFoundAnnotationException(clazz.getName() + " is not Entity Annotation.");
        }
    }

    /**
     * 设置SQL参数占位符的值
     */
    private void setParameter(List<Object> values, PreparedStatement ps, boolean isSearch) throws SQLException {
        for (int i = 1; i <= values.size(); i++) {
            Object fieldValue = values.get(i - 1);
            Class<?> clazzValue = fieldValue.getClass();
            if (clazzValue == String.class) {
                if (isSearch)
                    ps.setString(i, "%" + (String) fieldValue + "%");
                else
                    ps.setString(i, (String) fieldValue);
            } else if (clazzValue == boolean.class || clazzValue == Boolean.class) {
                ps.setBoolean(i, (Boolean) fieldValue);
            } else if (clazzValue == byte.class || clazzValue == Byte.class) {
                ps.setByte(i, (Byte) fieldValue);
            } else if (clazzValue == char.class || clazzValue == Character.class) {
                ps.setObject(i, fieldValue, Types.CHAR);
            } else if (clazzValue == Date.class) {
                ps.setTimestamp(i, new Timestamp(((Date) fieldValue).getTime()));
            } else if (clazzValue.isArray()) {
                Object[] arrayValue = (Object[]) fieldValue;
                StringBuffer sb = new StringBuffer();
                for (int j = 0; j < arrayValue.length; j++) {
                    sb.append(arrayValue[j]).append("、");
                }
                ps.setString(i, sb.deleteCharAt(sb.length() - 1).toString());
            } else {
                ps.setObject(i, fieldValue, Types.NUMERIC);
            }
        }
    }
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
标题:深入理解Java反射机制 摘要:Java反射机制是一种强大的特性,它允许在运行时动态地获取和操作类的信息。本文将深入探讨Java反射机制的原理、应用场景以及使用方法。 正文: 一、什么是Java反射机制? Java反射机制是指在运行时通过程序获取类的信息,并且可以动态调用类的方法、访问和修改类的属性,以及创建对象等操作。它使得我们可以在不知道类的具体信息的情况下,对类进行操作。 二、Java反射机制的原理 Java反射机制的核心类是Class类,它代表一个类或接口。通过Class类,我们可以获取类的构造方法、方法、属性等信息。Java反射机制主要涉及以下几个重要的类和接口: 1. Class类:代表一个类或接口,在运行时可以获取类的相关信息。 2. Constructor类:代表类的构造方法,可以用来创建对象。 3. Method类:代表类的方法,可以用来调用方法。 4. Field类:代表类的属性,可以用来访问和修改属性的值。 通过这些核心类和接口,我们可以在运行时动态地加载和使用类,而不需要在编译时确定类的具体信息。 三、Java反射机制的应用场景 Java反射机制在许多框架和工具中得到广泛应用,下面列举几个常见的应用场景: 1. 框架开发:许多框架,如Spring、Hibernate等,都使用了反射机制来实现动态创建对象、调用方法等功能。 2. 单元测试:JUnit等测试框架使用反射机制来执行被测试类的方法。 3. 配置文件解析:通过反射机制,可以根据配置文件中的类名动态加载和初始化对象。 4. 动态代理:反射机制可以实现动态代理,通过在运行时生成代理对象,来增强方法的功能。 四、Java反射机制的使用方法 Java反射机制提供了一系列的方法来操作类,包括获取类名、获取构造方法、调用方法、访问和修改属性等。下面是一些常用的反射方法: 1. 获取类名:使用Class类的getName()方法可以获取类的全限定名。 2. 获取构造方法:使用Class类的getConstructors()或getDeclaredConstructors()方法可以获取类的构造方法。 3. 调用方法:使用Method类的invoke()方法可以调用类的方法。 4. 访问和修改属性:使用Field类的get()和set()方法可以访问和修改类的属性。 需要注意的是,在使用反射机制时,要处理好异常情况,并且在性能要求较高的场景下,应谨慎使用反射,以避免性能开销过大的问题。 结论: Java反射机制是一种强大的特性,它允许在运行时动态地获取和操作类的信息。通过反射机制,我们可以实现诸如动态创建对象、调用方法、访问和修改属性等功能。尽管反射机制具有一定的性能开销,但在许多框架和工具中得到广泛应用。了解和掌握Java反射机制,将会为我们的开发工作带来更多的灵活性和便利性。 参考文献: - Oracle官方文档 - Java反射:https://docs.oracle.com/javase/tutorial/reflect/ - 廖雪峰的Java教程 - 反射:https://www.liaoxuefeng.com/wiki/1252599548343744/1264804593397984
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李熠漾

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

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

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

打赏作者

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

抵扣说明:

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

余额充值