[SpringBoot] Implementation of public field automatic filling function (enumeration, custom annotation, AOP, reflection)

1. Custom annotations

Use @interface syntax to define annotations.

Annotated parameters are similar to parameterless methods. You can use default to set a default value, for example String value() default "";.

Meta-annotations: There are some annotations that can modify other annotations. These annotations are called meta-annotations.

@Target
Use @Targetcan define Annotationwhich locations in the source code can be applied (multiple written in the form of an array: { ElementType.METHOD, ElementType.FIELD }):

  • Class or interface: ElementType.TYPE;
  • Field: ElementType.FIELD;
  • method: ElementType.METHOD;
  • Construction method: ElementType.CONSTRUCTOR;
  • Method parameters: ElementType.PARAMETER.

@Retention
@RetentionThe life cycle defined by meta-annotations Annotation:

  • Compile time only: RetentionPolicy.SOURCE;
  • Only class files: RetentionPolicy.CLASS;
  • Runtime: RetentionPolicy.RUNTIME.

@RetentionThis Annotationdefaults to if not present CLASS. But usually what we customize Annotationis RUNTIME.

Application examples are as follows:

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();
}

Enumerate database operation types UPDATE, INSERT.

package com.sky.enumeration;

/**
 * 数据库操作类型
 */
public enum OperationType {
    
    

    /**
     * 更新操作
     */
    UPDATE,

    /**
     * 插入操作
     */
    INSERT

}

2. AOP

2.1 AOP related concepts

AOP: Aspect Oriented Programming (aspect-oriented programming, aspect-oriented programming) is actually programming oriented to a specific method. It is designed to use the dynamic proxy mechanism at the bottom of the process of managing bean objects to program specific methods (functional enhancement).

The role of AOP: to enhance existing methods without modifying the source code during the running of the program (non-invasive: decoupling)
to program one or more such specified methods, we call it aspect-oriented programming.

1. Connection point: JoinPoint , a method that can be controlled by AOP (implying relevant information when the method is executed).
2. Notification: Advice refers to repeated logic, that is, common functions (eventually embodied as a method).
3. Pointcut: PointCut , matching the conditions of the connection point, the notification will only be applied when the pointcut method is executed.
4. Aspect: Aspect , describing the corresponding relationship between notifications and entry points (notification + entry point). Aspect classes (classes identified by the @Aspect annotation).
5. Target object: Target , the object to which the notification is applied.

Spring's AOP bottom layer is implemented based on dynamic proxy technology, which means that when the program is running, a corresponding proxy object will be automatically generated for the target object based on dynamic proxy technology. In the proxy object, the functions of the original methods in the target object will be enhanced.

Common application scenarios of AOP:

  • Record system operation logs
  • Permission control
  • Transaction management: As long as the @Transactional annotation is added, the AOP program will automatically start the transaction before the original method is run, and submit or rollback the transaction after the original method is completed.
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.2 Notification types

Notification types of AOP in Spring:

  • @Around: Surround notification. The notification method marked with this annotation is executed before and after the target method.
  • @Before: Pre-notification, the notification method marked with this annotation is executed before the target method
  • @After: Post notification, the notification method marked with this annotation is executed after the target method, regardless of whether there is an exception or not.
  • @AfterReturning: Notification after return. The notification method marked with this annotation is executed after the target method. If there is an exception, it will not be executed.
  • @AfterThrowing: Notification after exception, the notification method marked with this annotation is executed after an exception occurs

Things to note when using notifications:

  • @Around notifications need to call ProceedingJoinPoint.proceed() themselves to let the original method execute. Other notifications do not need to consider the execution of the target method.
  • The return value of the @Around notification method must be specified as Object to receive the return value of the original method. Otherwise, the return value will not be obtained after the original method is executed.

2.3 Extraction

@PointCut annotation, the function of this annotation is to extract the public pointcut expression, and just quote the pointcut expression when needed. This avoids specifying the same pointcut expression in each annotation, enhancing reusability and maintainability.

It should be noted that when the pointcut method is modified with private, the expression can only be referenced in the current aspect class. When other external aspect classes also need to reference the pointcut expression in the current class, private needs to be changed to public, when quoting, the specific syntax is: full class name.method name (), the specific form is as follows:

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

2.3 Notification sequence

By default, the aspect classes are sorted alphabetically by their class names:

  • Notification methods before the target method: the one with the highest alphabetical order is executed first
  • Notification methods after the target method: the one with the highest alphabetical order is executed after

If we want to control the execution order of notifications, there are two ways:

  1. Modify the class name of the aspect class (this method is very cumbersome and inconvenient to manage)
  2. Use the @Order annotation provided by Spring

Use the @Order annotation to control the execution order of notifications:

@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 ...");
    }
}

Insert image description here

2.4 Pointcut expression

Mainly used to decide which methods in the project need to be added to the notification

  1. execution(...): Match execution based on the method's signature,
    mainly based on the method's return value, package name, class name, method name, method parameters and other information. The syntax is:
execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)

The band ?indicates the part that can be omitted

  • Access modifier: can be omitted (for example: public, protected)

  • Package name.Class name: can be omitted

  • throws exception: can be omitted (note that it is the exception declared to be thrown on the method, not the exception actually thrown)
    Insert image description here
    You can use wildcards to describe the entry point

  • *: A single independent arbitrary symbol that can be wildcarded with any return value, package name, class name, method name, a parameter of any type, or a part of a package, class, or method name.

  • ..: Multiple consecutive arbitrary symbols, which can be used as wildcards for any level of packages, or any type and any number of parameters.

Syntax rules for pointcut expressions:

  1. The access modifier of the method can be omitted
  2. The return value can be *replaced by a number (any return value type)
  3. The package name can *be replaced by a number, which represents any package (one layer of packages uses one *)
  4. Use ..the configuration package name to identify this package and all sub-packages under this package
  5. The class name can be *replaced by a number to identify any class
  6. The method name can be *replaced by a number to represent any method.
  7. You can use *configuration parameters, a parameter of any type
  8. You can use ..configuration parameters, any number of parameters of any type

Pointcut expression example

  • Omit method modifiers

    execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
    
  • Use *instead return value type

    execution(* com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))
    
  • Use *instead of package name (one layer of packages uses one *)

    execution(* com.itheima.*.*.DeptServiceImpl.delete(java.lang.Integer))
    
  • Use ..omitted package name

    execution(* com..DeptServiceImpl.delete(java.lang.Integer))    
    
  • Use *instead of class name

    execution(* com..*.delete(java.lang.Integer))   
    
  • Use *alternative method names

    execution(* com..*.*(java.lang.Integer))   
    
  • Use *instead of parameter

    execution(* com.itheima.service.impl.DeptServiceImpl.delete(*))
    
  • Use ..omitted parameters

    execution(* com..*.*(..))
    
  • According to business needs, you can use AND (&&), OR (||), NOT (!) to combine more complex pointcut expressions.

    execution(* com.itheima.service.DeptService.list(..)) || execution(* com.itheima.service.DeptService.delete(..))
    
  • Describing entry point methods is usually based on interface description rather than directly describing the implementation class to enhance scalability.

    execution(* com.itheima.service.DeptService.*(..))
    
  • On the premise of meeting business needs, try to narrow the matching scope of entry points. For example: try not to use... when matching package names, use * to match a single package

    execution(* com.itheima.*.*.DeptServiceImpl.find*(..))
    
  1. @annotation(……): matching based on annotations.
    Insert image description here
    Implementation steps:

  2. Write custom annotations

  3. Add custom annotations to the methods of the business class that are to be used as connection points

2.5 ​Connection points

Connection points can be simply understood as methods that can be controlled by AOP.

JoinPoint is used to abstract the connection point in Spring, and it can be used to obtain relevant information when the method is executed, such as the target class name, method name, method parameters, etc.

  • For @Around notifications, only the ProceedingJoinPoint type can be used to obtain connection point information.
  • For the other four notifications, only JoinPoint can be used to obtain connection point information, which is the parent type of ProceedingJoinPoint.

3. Reflection

3.1 The concept of reflection

The function of dynamically obtaining information and dynamically calling object methods is called the reflection mechanism of the Java language.

  • Objects created using reflection can call the contents of the class regardless of modifiers.

  • It can be used in conjunction with the configuration file , and the object information and methods to be created are written in the configuration file.

    Whatever class is read, an object of that class is created.

    Which method is read, which method is called

    At this time, when the requirements change, there is no need to modify the code, just modify the configuration file.

Bytecode file object: an object automatically created by the virtual machine after the class file is loaded into memory. This object contains at least: constructors, member variables, and member methods.

What reflection obtains is the bytecode file object, which is unique in memory.

3.2 Get the bytecode file object

  • The static method forName ("full class name") in the Class class (most commonly used)
  • Obtained through class attribute
  • Get bytecode file object through object

Code example:

//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 Get the construction method

method name illustrate
Constructor<?>[] getConstructors() Get all structures (can only be modified by public)
Constructor<?>[] getDeclaredConstructors() Get all structures (including private modification)
Constructor getConstructor(Class<?>… parameterTypes) Get the specified structure (can only be modified by public)
Constructor getDeclaredConstructor(Class<?>… parameterTypes) Get the specified structure (including private modification)

Code example:

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 Get the constructor and create the object

Methods involved: newInstance

Code example:

//首先要有一个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 Get member variables

method name illustrate
Field[] getFields() Returns an array of all member variable objects (only public ones can be taken)
Field[] getDeclaredFields() Returns an array of all member variable objects, which can be obtained if they exist
Field getField(String name) Return a single member variable object (only public ones can be taken)
Field getDeclaredField(String name) Returns a single member variable object, which can be obtained if it exists

Code example:

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 Obtain member variables and obtain and modify values

method illustrate
void set(Object obj, Object value) Assignment
Object get(Object obj) Get value

Code example:

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 Get member method

method name illustrate
Method[] getMethods() Returns an array of all member method objects (only public ones can be taken)
Method[] getDeclaredMethods() Returns an array of all member method objects, which can be obtained if they exist
Method getMethod(String name, Class<?>… parameterTypes) Return a single member method object (only public ones can be taken)
Method getDeclaredMethod(String name, Class<?>… parameterTypes) Returns a single member method object, which can be obtained if it exists

Code example:

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 Get member methods and run them

method

Object invoke(Object obj, Object… args): Run method

Parameter 1: Call this method with obj object

Parameter 2: Parameters passed when calling the method (if there are none, don’t write them)

Return value: The return value of the method (if not, don’t write it)

Code example:

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";
    }
}

Interview questions

​Do you think Reflection is good? Okay, there are two directions.

​ The first direction: access the content in the class regardless of modifiers. However, this kind of operation is generally not used in development, and is used by the bottom layer of the framework.

​ The second direction: Reflection can be used in combination with configuration files to dynamically create objects and dynamically call methods.

4. Implementation of automatic filling function of public fields

See Table of Contents 1 for custom annotations.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
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);

Guess you like

Origin blog.csdn.net/XiugongHao/article/details/135431662