【SpringBoot】公共字段自动填充功能实现(枚举、自定义注解、AOP、反射) 您所在的位置:网站首页 interface定义注解 【SpringBoot】公共字段自动填充功能实现(枚举、自定义注解、AOP、反射)

【SpringBoot】公共字段自动填充功能实现(枚举、自定义注解、AOP、反射)

2024-07-08 21:18| 来源: 网络整理| 查看: 265

1. 自定义注解

使用@interface语法来定义注解(Annotation)。

注解的参数类似无参数方法,可以用default设定一个默认值,比如String value() default "";。

元注解:有一些注解可以修饰其他注解,这些注解就称为元注解(meta annotation)。

@Target 使用@Target可以定义Annotation能够被应用于源码的哪些位置(多个写成数组的形式:{ ElementType.METHOD, ElementType.FIELD }):

类或接口:ElementType.TYPE;字段:ElementType.FIELD;方法:ElementType.METHOD;构造方法:ElementType.CONSTRUCTOR;方法参数:ElementType.PARAMETER。

@Retention 元注解@Retention定义了Annotation的生命周期:

仅编译期:RetentionPolicy.SOURCE;仅class文件:RetentionPolicy.CLASS;运行期:RetentionPolicy.RUNTIME。

如果@Retention不存在,则该Annotation默认为CLASS。但是通常我们自定义的Annotation都是RUNTIME。

应用实例如下:

package com.sky.annotation; import com.sky.enumeration.OperationType; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解,用于标识某个方法需要进行公共字段自动填充处理 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoFill { // 自定义的数据库操作类型 UPDATE INSERT OperationType value(); }

枚举数据库操作类型 UPDATE, INSERT。

package com.sky.enumeration; /** * 数据库操作类型 */ public enum OperationType { /** * 更新操作 */ UPDATE, /** * 插入操作 */ INSERT } 2. AOP 2.1 AOP 相关概念

AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实就是面向特定方法编程。旨在管理bean对象的过程中底层使用动态代理机制,对特定的方法进行编程(功能增强)。

AOP的作用:在程序运行期间在不修改源代码的基础上对已有方法进行增强(无侵入性: 解耦) 面向这样的指定的一个或多个方法进行编程,我们就称之为面向切面编程。

1. 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)。 2. 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)。 3. 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用。 4. 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)。切面类(被@Aspect注解标识的类)。 5. 目标对象:Target,通知所应用的对象。

Spring的AOP底层是基于动态代理技术来实现的,也就是说在程序运行的时候,会自动的基于动态代理技术为目标对象生成一个对应的代理对象。在代理对象当中就会对目标对象当中的原始方法进行功能的增强。

AOP 常见应用场景:

记录系统的操作日志权限控制事务管理:只要添加@Transactional注解之后,AOP程序自动会在原始方法运行前先来开启事务,在原始方法运行完毕之后提交或回滚事务 org.springframework.boot spring-boot-starter-aop 2.2 通知类型

Spring中AOP的通知类型:

@Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行@Before:前置通知,此注解标注的通知方法在目标方法前被执行@After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行@AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行@AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行

在使用通知时的注意事项:

@Around环绕通知需要自己调用 ProceedingJoinPoint.proceed() 来让原始方法执行,其他通知不需要考虑目标方法执行@Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值,否则原始方法执行完毕,是获取不到返回值的。 2.3 抽取

@PointCut注解,该注解的作用是将公共的切入点表达式抽取出来,需要用到时引用该切入点表达式即可。避免了每一个注解里面都指定了相同的切入点表达式,增强复用性和可维护性。

需要注意的是:当切入点方法使用private修饰时,仅能在当前切面类中引用该表达式,当外部其他切面类中也要引用当前类中的切入点表达式,就需要把private改为public,而在引用的时候,具体的语法为:全类名.方法名(),具体形式如下:

@Slf4j @Component @Aspect public class MyAspect2 { //引用MyAspect1切面类中的切入点表达式 @Before("com.itheima.aspect.MyAspect1.pt()") public void before(){ log.info("MyAspect2 -> before ..."); } } 2.3 通知顺序

默认按照切面类的类名字母排序:

目标方法前的通知方法:字母排名靠前的先执行目标方法后的通知方法:字母排名靠前的后执行

如果我们想控制通知的执行顺序有两种方式:

修改切面类的类名(这种方式非常繁琐、而且不便管理)使用Spring提供的@Order注解

使用@Order注解,控制通知的执行顺序:

@Slf4j @Component @Aspect @Order(2) //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行) public class MyAspect2 { //前置通知 @Before("execution(* com.itheima.service.*.*(..))") public void before(){ log.info("MyAspect2 -> before ..."); } //后置通知 @After("execution(* com.itheima.service.*.*(..))") public void after(){ log.info("MyAspect2 -> after ..."); } } @Slf4j @Component @Aspect @Order(3) //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行) public class MyAspect3 { //前置通知 @Before("execution(* com.itheima.service.*.*(..))") public void before(){ log.info("MyAspect3 -> before ..."); } //后置通知 @After("execution(* com.itheima.service.*.*(..))") public void after(){ log.info("MyAspect3 -> after ..."); } } @Slf4j @Component @Aspect @Order(1) //切面类的执行顺序(前置通知:数字越小先执行; 后置通知:数字越小越后执行) public class MyAspect4 { //前置通知 @Before("execution(* com.itheima.service.*.*(..))") public void before(){ log.info("MyAspect4 -> before ..."); } //后置通知 @After("execution(* com.itheima.service.*.*(..))") public void after(){ log.info("MyAspect4 -> after ..."); } }

在这里插入图片描述

2.4 切入点表达式

主要用来决定项目中的哪些方法需要加入通知

execution(……):根据方法的签名来匹配 execution主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为: execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)

其中带?的表示可以省略的部分

访问修饰符:可省略(比如: public、protected)

包名.类名: 可省略

throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常) 在这里插入图片描述 可以使用通配符描述切入点

* :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分

.. :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数

切入点表达式的语法规则:

方法的访问修饰符可以省略返回值可以使用*号代替(任意返回值类型)包名可以使用*号代替,代表任意包(一层包使用一个*)使用..配置包名,标识此包以及此包下的所有子包类名可以使用*号代替,标识任意类方法名可以使用*号代替,表示任意方法可以使用 * 配置参数,一个任意类型的参数可以使用.. 配置参数,任意个任意类型的参数

切入点表达式示例

省略方法的修饰符号

execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))

使用*代替返回值类型

execution(* com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))

使用*代替包名(一层包使用一个*)

execution(* com.itheima.*.*.DeptServiceImpl.delete(java.lang.Integer))

使用..省略包名

execution(* com..DeptServiceImpl.delete(java.lang.Integer))

使用*代替类名

execution(* com..*.delete(java.lang.Integer))

使用*代替方法名

execution(* com..*.*(java.lang.Integer))

使用 * 代替参数

execution(* com.itheima.service.impl.DeptServiceImpl.delete(*))

使用..省略参数

execution(* com..*.*(..))

根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式。

execution(* com.itheima.service.DeptService.list(..)) || execution(* com.itheima.service.DeptService.delete(..))

描述切入点方法通常基于接口描述,而不是直接描述实现类,增强拓展性

execution(* com.itheima.service.DeptService.*(..))

在满足业务需要的前提下,尽量缩小切入点的匹配范围。如:包名匹配尽量不使用 …,使用 * 匹配单个包

execution(* com.itheima.*.*.DeptServiceImpl.find*(..))

@annotation(……) :根据注解匹配 在这里插入图片描述 实现步骤:

编写自定义注解

在业务类要做为连接点的方法上添加自定义注解

2.5 ​连接点

连接点,连接点可以简单理解为可以被AOP控制的方法。

在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。

对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint类型对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型 3. 反射 3.1 反射的概念

动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

利用反射创建的对象可以无视修饰符调用类里面的内容

可以跟配置文件结合起来使用,把要创建的对象信息和方法写在配置文件中。

读取到什么类,就创建什么类的对象

读取到什么方法,就调用什么方法

此时当需求变更的时候不需要修改代码,只要修改配置文件即可。

字节码文件对象:当class文件加载到内存之后,虚拟机自动创建出来的对象。这个对象里面至少包含了:构造方法,成员变量,成员方法。

反射获取的是字节码文件对象,这个对象在内存中是唯一的。

3.2 获取字节码文件对象 Class这个类里面的静态方法forName(“全类名”)(最常用)通过class属性获取通过对象获取字节码文件对象

代码示例:

//1.Class这个类里面的静态方法forName //Class.forName("类的全类名"): 全类名 = 包名 + 类名 Class clazz1 = Class.forName("com.itheima.reflectdemo.Student"); //源代码阶段获取 --- 先把Student加载到内存中,再获取字节码文件的对象 //clazz 就表示Student这个类的字节码文件对象。 //就是当Student.class这个文件加载到内存之后,产生的字节码文件对象 //2.通过class属性获取 //类名.class Class clazz2 = Student.class; //因为class文件在硬盘中是唯一的,所以,当这个文件加载到内存之后产生的对象也是唯一的 System.out.println(clazz1 == clazz2);//true //3.通过Student对象获取字节码文件对象 Student s = new Student(); Class clazz3 = s.getClass(); System.out.println(clazz1 == clazz2);//true System.out.println(clazz2 == clazz3);//true 3.3 获取构造方法 方法名说明Constructor[] getConstructors()获得所有的构造(只能public修饰)Constructor[] getDeclaredConstructors()获得所有的构造(包含private修饰)Constructor getConstructor(Class… parameterTypes)获取指定构造(只能public修饰)Constructor getDeclaredConstructor(Class… parameterTypes)获取指定构造(包含private修饰)

代码示例:

public class ReflectDemo2 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { //1.获得整体(class字节码文件对象) Class clazz = Class.forName("com.itheima.reflectdemo.Student"); //2.获取构造方法对象 //获取所有构造方法(public) Constructor[] constructors1 = clazz.getConstructors(); for (Constructor constructor : constructors1) { System.out.println(constructor); } System.out.println("======================="); //获取所有构造(带私有的) Constructor[] constructors2 = clazz.getDeclaredConstructors(); for (Constructor constructor : constructors2) { System.out.println(constructor); } System.out.println("======================="); //获取指定的空参构造 Constructor con1 = clazz.getConstructor(); System.out.println(con1); Constructor con2 = clazz.getConstructor(String.class,int.class); System.out.println(con2); System.out.println("======================="); //获取指定的构造(所有构造都可以获取到,包括public包括private) Constructor con3 = clazz.getDeclaredConstructor(); System.out.println(con3); //了解 System.out.println(con3 == con1); //每一次获取构造方法对象的时候,都会新new一个。 Constructor con4 = clazz.getDeclaredConstructor(String.class); System.out.println(con4); } } 3.4 获取构造方法并创建对象

涉及到的方法:newInstance

代码示例:

//首先要有一个javabean类 public class Student { private String name; private int age; public Student() { } public Student(String name) { this.name = name; } private Student(String name, int age) { this.name = name; this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } public String toString() { return "Student{name = " + name + ", age = " + age + "}"; } } //测试类中的代码: //需求1: //获取空参,并创建对象 //1.获取整体的字节码文件对象 Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student"); //2.获取空参的构造方法 Constructor con = clazz.getConstructor(); //3.利用空参构造方法创建对象 Student stu = (Student) con.newInstance(); System.out.println(stu); System.out.println("============================================="); //测试类中的代码: //需求2: //获取带参构造,并创建对象 //1.获取整体的字节码文件对象 Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student"); //2.获取有参构造方法 Constructor con = clazz.getDeclaredConstructor(String.class, int.class); //3.临时修改构造方法的访问权限(暴力反射) con.setAccessible(true); //4.直接创建对象 Student stu = (Student) con.newInstance("zhangsan", 23); System.out.println(stu); 3.5 获取成员变量 方法名说明Field[] getFields()返回所有成员变量对象的数组(只能拿public的)Field[] getDeclaredFields()返回所有成员变量对象的数组,存在就能拿到Field getField(String name)返回单个成员变量对象(只能拿public的)Field getDeclaredField(String name)返回单个成员变量对象,存在就能拿到

代码示例:

public class ReflectDemo4 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { //获取成员变量对象 //1.获取class对象 Class clazz = Class.forName("com.itheima.reflectdemo.Student"); //2.获取成员变量的对象(Field对象)只能获取public修饰的 Field[] fields1 = clazz.getFields(); for (Field field : fields1) { System.out.println(field); } System.out.println("==============================="); //获取成员变量的对象(public + private) Field[] fields2 = clazz.getDeclaredFields(); for (Field field : fields2) { System.out.println(field); } System.out.println("==============================="); //获得单个成员变量对象 //如果获取的属性是不存在的,那么会报异常 //Field field3 = clazz.getField("aaa"); //System.out.println(field3);//NoSuchFieldException Field field4 = clazz.getField("gender"); System.out.println(field4); System.out.println("==============================="); //获取单个成员变量(私有) Field field5 = clazz.getDeclaredField("name"); System.out.println(field5); } } public class Student { private String name; private int age; public String gender; public String address; public Student() { } public Student(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } public Student(String name, int age, String gender, String address) { this.name = name; this.age = age; this.gender = gender; this.address = address; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } /** * 获取 * @return gender */ public String getGender() { return gender; } /** * 设置 * @param gender */ public void setGender(String gender) { this.gender = gender; } /** * 获取 * @return address */ public String getAddress() { return address; } /** * 设置 * @param address */ public void setAddress(String address) { this.address = address; } public String toString() { return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}"; } } 3.6 获取成员变量并获取值和修改值 方法说明void set(Object obj, Object value)赋值Object get(Object obj)获取值

代码示例:

public class ReflectDemo5 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { Student s = new Student("zhangsan",23,"广州"); Student ss = new Student("lisi",24,"北京"); //需求: //利用反射获取成员变量并获取值和修改值 //1.获取class对象 Class clazz = Class.forName("com.itheima.reflectdemo.Student"); //2.获取name成员变量 //field就表示name这个属性的对象 Field field = clazz.getDeclaredField("name"); //临时修饰他的访问权限 field.setAccessible(true); //3.设置(修改)name的值 //参数一:表示要修改哪个对象的name? //参数二:表示要修改为多少? field.set(s,"wangwu"); //3.获取name的值 //表示我要获取这个对象的name的值 String result = (String)field.get(s); //4.打印结果 System.out.println(result); System.out.println(s); System.out.println(ss); } } public class Student { private String name; private int age; public String gender; public String address; public Student() { } public Student(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } public Student(String name, int age, String gender, String address) { this.name = name; this.age = age; this.gender = gender; this.address = address; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } /** * 获取 * @return gender */ public String getGender() { return gender; } /** * 设置 * @param gender */ public void setGender(String gender) { this.gender = gender; } /** * 获取 * @return address */ public String getAddress() { return address; } /** * 设置 * @param address */ public void setAddress(String address) { this.address = address; } public String toString() { return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}"; } } 3.7 获取成员方法 方法名说明Method[] getMethods()返回所有成员方法对象的数组(只能拿public的)Method[] getDeclaredMethods()返回所有成员方法对象的数组,存在就能拿到Method getMethod(String name, Class… parameterTypes)返回单个成员方法对象(只能拿public的)Method getDeclaredMethod(String name, Class… parameterTypes)返回单个成员方法对象,存在就能拿到

代码示例:

public class ReflectDemo6 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { //1.获取class对象 Class clazz = Class.forName("com.itheima.reflectdemo.Student"); //2.获取方法 //getMethods可以获取父类中public修饰的方法 Method[] methods1 = clazz.getMethods(); for (Method method : methods1) { System.out.println(method); } System.out.println("==========================="); //获取所有的方法(包含私有) //但是只能获取自己类中的方法 Method[] methods2 = clazz.getDeclaredMethods(); for (Method method : methods2) { System.out.println(method); } System.out.println("==========================="); //获取指定的方法(空参) Method method3 = clazz.getMethod("sleep"); System.out.println(method3); Method method4 = clazz.getMethod("eat",String.class); System.out.println(method4); //获取指定的私有方法 Method method5 = clazz.getDeclaredMethod("playGame"); System.out.println(method5); } } 3.8 获取成员方法并运行

方法

Object invoke(Object obj, Object… args) :运行方法

参数一:用obj对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

代码示例:

package com.itheima.a02reflectdemo1; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectDemo6 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { //1.获取字节码文件对象 Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student"); //2.获取一个对象 //需要用这个对象去调用方法 Student s = new Student(); //3.获取一个指定的方法 //参数一:方法名 //参数二:参数列表,如果没有可以不写 Method eatMethod = clazz.getMethod("eat",String.class); //运行 //参数一:表示方法的调用对象 //参数二:方法在运行时需要的实际参数 //注意点:如果方法有返回值,那么需要接收invoke的结果 //如果方法没有返回值,则不需要接收 String result = (String) eatMethod.invoke(s, "重庆小面"); System.out.println(result); } } public class Student { private String name; private int age; public String gender; public String address; public Student() { } public Student(String name) { this.name = name; } private Student(String name, int age) { this.name = name; this.age = age; } /** * 获取 * @return name */ public String getName() { return name; } /** * 设置 * @param name */ public void setName(String name) { this.name = name; } /** * 获取 * @return age */ public int getAge() { return age; } /** * 设置 * @param age */ public void setAge(int age) { this.age = age; } public String toString() { return "Student{name = " + name + ", age = " + age + "}"; } private void study(){ System.out.println("学生在学习"); } private void sleep(){ System.out.println("学生在睡觉"); } public String eat(String something){ System.out.println("学生在吃" + something); return "学生已经吃完了,非常happy"; } } 面试题

​ 你觉得反射好不好?好,有两个方向

​ 第一个方向:无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。

​ 第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。

4. 公共字段自动填充功能实现

自定义注解见 目录1。

org.springframework.boot spring-boot-starter-aop package com.sky.aspect; import com.sky.annotation.AutoFill; import com.sky.constant.AutoFillConstant; import com.sky.context.BaseContext; import com.sky.enumeration.OperationType; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.time.LocalDateTime; /** * 自定义切面,实现公共字段自动填充处理逻辑 */ @Aspect @Component @Slf4j public class AutoFillAspect { /** * 切入点 */ @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)") public void autoFillPointCut() { } /** * 前置通知,在通知中进行公共字段的赋值 */ @Before("autoFillPointCut()") public void autoFill(JoinPoint joinPoint) { log.info("开始进行公共字段自动填充..."); // 获取到当前被拦截的方法上的数据库操作类型 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 方法签名对象 AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 获得方法上的注解对象 OperationType operationType = autoFill.value(); // 获得数据库操作类型 // 获取到当前被拦截的方法的参数 -- 实体对象 Object[] args = joinPoint.getArgs(); if (args == null || args.length == 0) { return; } Object entity = args[0]; // 准备赋值的数据 LocalDateTime now = LocalDateTime.now(); Long currentId = BaseContext.getCurrentId(); // 根据当前不同的操作类型,为对应的属性通过反射来赋值 if (operationType == OperationType.INSERT) { try { Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class); Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class); Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class); // 通过反射为对象属性赋值 setCreateTime.invoke(entity, now); setCreateUser.invoke(entity, currentId); setUpdateTime.invoke(entity, now); setUpdateUser.invoke(entity, currentId); } catch (Exception e) { e.printStackTrace(); } } else if (operationType == OperationType.UPDATE) { // 为2个公共字段赋值 try { Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class); setUpdateTime.invoke(entity, now); setUpdateUser.invoke(entity, currentId); } catch (Exception e) { e.printStackTrace(); } } } } /** * 根据id修改分类 * @param category */ @AutoFill(value = OperationType.UPDATE) void update(Category category);


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有