[Java Basics - Detailed explanation of the annotation mechanism]

 Annotation basics

Annotations are a feature introduced in JDK 1.5. They are used to explain the code. They can annotate packages, classes, interfaces, fields, method parameters, local variables, etc. Its main functions are as follows:

  • Generate documentation and generate javadoc documents through the metadata identified in the code.
  • Compilation check, through the metadata identified in the code, the compiler can check and verify during compilation.
  • Dynamic processing at compile time. Dynamic processing at compile time through metadata identified in the code, such as dynamically generating code.
  • Dynamic processing at runtime. Dynamic processing at runtime through metadata identified in the code, such as using reflection injection instances.

This is relatively abstract. Let’s take a closer look at the common categories of annotations:

  • Java's own standard annotations, including @Override, @Deprecated and @SuppressWarnings are used to indicate overriding a method, indicating that a certain class or method is obsolete, and indicating warnings to be ignored. The compiler will check after being marked with these annotations.
  • Meta-annotation, meta-annotation is an annotation used to define annotation, including @Retention, @Target, a>@Inherited, @Documented, @Retention is used to indicate the stage when the annotation is retained, @Target is used to indicate the use of the annotation The scope of @Inherited is used to indicate that annotations can be inherited, @Documented is used to indicate whether to generate javadoc documents.
  • Custom annotations, you can define annotations according to your own needs, and use meta-annotations to annotate custom annotations.

Next, we understand annotations from this classification perspective.

 Java built-in annotations

Let’s start with the most common Java built-in annotations. Let’s take a look at the following code:

class A{
    public void test() {
        
    }
}

class B extends A{

    /**
        * 重载父类的test方法
        */
    @Override
    public void test() {
    }

    /**
        * 被弃用的方法
        */
    @Deprecated
    public void oldMethod() {
    }

    /**
        * 忽略告警
        * 
        * @return
        */
    @SuppressWarnings("rawtypes")
    public List processList() {
        List list = new ArrayList();
        return list;
    }
}

The standard annotations that come with Java 1.5, including @Override, @Deprecated and @SuppressWarnings:

  • @Override: Indicates that the current method definition will override the method in the parent class
  • @Deprecated: Indicates that the code is deprecated. If the code annotated with @Deprecated is used, the compiler will issue a warning.
  • @SuppressWarnings: Indicates turning off compiler warning information

Let's take a closer look at these built-in annotations, and introduce meta-annotations through the definitions of meta-annotations in these built-in annotations.

 Built-in annotation - @Override

Let’s first take a look at the definition of this annotation type:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

From its definition, we can see that this annotation can be used to modify methods, and it is only valid at compile time and will no longer exist in the compiled class file. The function of this annotation is familiar to all of us, that is, it tells the compiler that the modified method is a method with the same signature in the overridden parent class. The compiler will check this. If it is found that this method does not exist in the parent class If the method or existing method signature is different, an error will be reported.

 Built-in annotation - @Deprecated

The annotation is defined as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

From its definition, we can know that it will be documented, can be retained until runtime, and can modify constructors, properties, local variables, methods, packages, parameters, and types. The purpose of this annotation is to tell the compiler that the modified program element has been "obsolete" and is no longer recommended for users to use.

 Built-in annotations - @SuppressWarnings

We often use this annotation. Let’s first look at its definition:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

The program elements it can modify include types, attributes, methods, parameters, constructors, and local variables. They can only survive in the source code and have a value of String[]. Its function is to tell the compiler to ignore the specified warning message. The values ​​it can take are as follows:

parameter effect Original description
all suppress all warnings to suppress all warnings
boxing Suppress warnings during packing and unboxing operations to suppress warnings relative to boxing/unboxing operations
cast Suppress mapping related warnings to suppress warnings relative to cast operations
dep-ann Suppress warnings for enabled annotations to suppress warnings relative to deprecated annotation
deprecation Suppress expired method warnings to suppress warnings relative to deprecation
fallthrough Suppress warnings about missing breaks in switch to suppress warnings relative to missing breaks in switch statements
finally Suppress warnings about finally modules not returning to suppress warnings relative to finally block that don’t return
hiding Suppress warnings related to locale variables that hide variables to suppress warnings relative to locals that hide variable()
incomplete-switch Ignore incomplete switch statements to suppress warnings relative to missing entries in a switch statement (enum case)
nls Ignore non-nls format characters to suppress warnings relative to non-nls string literals
null Ignore operations on null to suppress warnings relative to null analysis
rawtype Ignore the corresponding type when using generics to suppress warnings relative to un-specific types when using
restriction Suppress warnings related to usage not recommended or forbidden references to suppress warnings relative to usage of discouraged or
serial Ignore serialVersionUID variable not declared in serializable class to suppress warnings relative to missing serialVersionUID field for a serializable class
static-access Suppress incorrect static access mode warnings to suppress warnings relative to incorrect static access
synthetic-access Suppress warnings that subclasses do not access inner classes in an optimal way to suppress warnings relative to unoptimized access from inner classes
unchecked Suppress warnings about no type checking to suppress warnings relative to unchecked operations
unqualified-field-access Suppress warnings for domains you don't have access to to suppress warnings relative to field access unqualified
unused Suppress warnings for unused code to suppress warnings relative to unused code

 meta-annotation

The above definition of built-in annotations uses some meta-annotations (annotation classes for annotation types). JDK 1.5 provides 4 standard meta-annotations: @Target, < /span> and < /span>. , two meta-annotations are provided in JDK 1.8: , @Retention, @Documented@Inherited@Repeatable@Native

 Meta-annotation - @Target

The purpose of the Target annotation is to describe the scope of use of the annotation (ie: where the modified annotation can be used).

Target annotations are used to describe the scope of objects that can be modified by the annotation classes they annotate: annotations can be used to modify packages, types (classes, interfaces, enumerations, annotation classes), class members (methods, constructors, member variables, Enumeration values), method parameters and local variables (such as loop variables, catch parameters), using @Target when defining annotation classes can more clearly know which objects it can be used to modify. Its value range is defined in ElementType Enumeration in progress.

public enum ElementType {
 
    TYPE, // 类、接口、枚举类
 
    FIELD, // 成员变量(包括:枚举常量)
 
    METHOD, // 成员方法
 
    PARAMETER, // 方法参数
 
    CONSTRUCTOR, // 构造方法
 
    LOCAL_VARIABLE, // 局部变量
 
    ANNOTATION_TYPE, // 注解类
 
    PACKAGE, // 可用于修饰:包
 
    TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
 
    TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
 
}
 Meta-annotation - @Retention & @RetentionTarget

The function of the Reteniton annotation is to describe the time range for the annotation to be retained (ie: until when the described annotation can be retained in the class it modifies).

The Reteniton annotation is used to limit how long the annotated classes annotated by it can be retained after being annotated to other classes. There are three strategies in total, defined in the RetentionPolicy enumeration.

public enum RetentionPolicy {
 
    SOURCE,    // 源文件保留
    CLASS,       // 编译期保留,默认值
    RUNTIME   // 运行期保留,可通过反射去获取注解信息
}

In order to verify the difference between the annotation classes applied to these three strategies, each of the three strategies was used to define an annotation class for testing.

@Retention(RetentionPolicy.SOURCE)
public @interface SourcePolicy {
 
}
@Retention(RetentionPolicy.CLASS)
public @interface ClassPolicy {
 
}
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimePolicy {
 
}

Use the three defined annotation classes to annotate a method respectively.

public class RetentionTest {
 
	@SourcePolicy
	public void sourcePolicy() {
	}
 
	@ClassPolicy
	public void classPolicy() {
	}
 
	@RuntimePolicy
	public void runtimePolicy() {
	}
}

The class bytecode content of RetentionTest obtained by executing the javap -verbose RetentionTest command is as follows.

{
  public retention.RetentionTest();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0

  public void sourcePolicy();
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 7: 0

  public void classPolicy();
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 11: 0
    RuntimeInvisibleAnnotations:
      0: #11()

  public void runtimePolicy();
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 15: 0
    RuntimeVisibleAnnotations:
      0: #14()
}

From the bytecode content of RetentionTest we can draw the following two conclusions:

  • The compiler does not record the annotation information of the sourcePolicy() method;
  • The compiler uses the RuntimeInvisibleAnnotations and RuntimeVisibleAnnotations attributes to record the classPolicy() method and runtimePolicy()Annotation information of the method;
 Meta-annotation - @Documented

The role of the Documented annotation is to describe whether to retain its annotation information when using the javadoc tool to generate help documentation for a class.

The following code can generate@TestDocAnnotation annotation information using the Javadoc tool.

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
 
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface TestDocAnnotation {
 
	public String value() default "default";
}
@TestDocAnnotation("myMethodDoc")
public void testDoc() {

}
 Meta-annotation - @Inherited

The role of the Inherited annotation: the Annotation modified by it will have inheritance. If a class uses an Annotation modified by @Inherited, its subclasses will automatically have this annotation.

Let’s test this annotation:

  • defined义@InheritedAnnotation:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface TestInheritedAnnotation {
    String [] values();
    int number();
}
  • Use this annotation
@TestInheritedAnnotation(values = {"value"}, number = 10)
public class Person {
}

class Student extends Person{
	@Test
    public void test(){
        Class clazz = Student.class;
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation.toString());
        }
    }
}
  • output
xxxxxxx.TestInheritedAnnotation(values=[value], number=10)

Even though the Student class is not explicitly annotated@TestInheritedAnnotation, its parent class Person is annotated, and @TestInheritedAnnotation is @InheritedAnnotation, so the Student class automatically has this annotation.

 Meta-annotation - @Repeatable (Java8)

@RepeatableReferenceJava 8 - Duplicate Annotations

 Meta-annotations - @Native (Java8)

Using the @Native annotation to modify a member variable means that the variable can be referenced by local code and is often used by code generation tools. The @Native annotation is not commonly used, just understand it

 Annotation and reflection interface

After defining an annotation, how to obtain the content in the annotation? The AnnotatedElement interface under the reflection package java.lang.reflect provides these methods. Note here: Only after the annotation is defined as RUNTIME can the annotation be visible at runtime. When the class file is loaded, the Annotation saved in the class file will be read by the virtual machine.

The AnnotatedElement interface is the parent interface of all program elements (Class, Method, and Constructor), so after the program obtains the AnnotatedElement object of a certain class through reflection, the program can call the object's method to access the Annotation information. Let’s take a look at the specific interface first.

  • boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)

Determines whether the program element contains annotations of the specified type. If it exists, it returns true, otherwise it returns false. Note: This method ignores the annotation container corresponding to the annotation.

  • <T extends Annotation> T getAnnotation(Class<T> annotationClass)

Returns the annotations of the specified type that exist on the program element, or null if the type annotation does not exist.

  • Annotation[] getAnnotations()

Returns all annotations that exist on the program element. If there are no annotations, returns an array of length 0.

  • <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)

Returns the annotation array of the specified type that exists on the program element. When there is no annotation of the corresponding type, an array with a length of 0 is returned. The caller of this method can modify the returned array at will without any impact on the arrays returned by other callers. The difference between the getAnnotationsByType method and getAnnotation is that getAnnotationsByType will detect duplicate annotation containers corresponding to the annotations. If the program element is a class, and the annotation cannot be found on the current class, and the annotation is inheritable, the corresponding annotation will be detected on the parent class.

  • <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)

Returns all annotations that exist directly on this element. Unlike other methods in this interface, this method ignores inherited annotations. Returns null if no annotation exists directly on this element

  • <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)

Returns all annotations that exist directly on this element. Unlike other methods in this interface, this method ignores inherited annotations

  • Annotation[] getDeclaredAnnotations()

Returns all annotations that exist directly on this element and a container of duplicate annotations corresponding to the annotations. Unlike other methods in this interface, this method ignores inherited annotations. If no annotation exists directly on this element, an array of length zero is returned. The caller of this method can modify the returned array at will without any impact on the arrays returned by other callers.

 Custom annotations

  • Define your own annotations
package com.pdai.java.annotation;

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 MyMethodAnnotation {

    public String title() default "";

    public String description() default "";

}
  • Use annotations
package com.pdai.java.annotation;

import java.io.FileNotFoundException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class TestMethodAnnotation {

    @Override
    @MyMethodAnnotation(title = "toStringMethod", description = "override toString method")
    public String toString() {
        return "Override toString method";
    }

    @Deprecated
    @MyMethodAnnotation(title = "old static method", description = "deprecated old static method")
    public static void oldMethod() {
        System.out.println("old method, don't use it.");
    }

    @SuppressWarnings({"unchecked", "deprecation"})
    @MyMethodAnnotation(title = "test method", description = "suppress warning static method")
    public static void genericsTest() throws FileNotFoundException {
        List l = new ArrayList();
        l.add("abc");
        oldMethod();
    }
}
  • Obtain annotation information using reflection interface

Add the Main method in TestMethodAnnotation for testing:

public static void main(String[] args) {
    try {
        // 获取所有methods
        Method[] methods = TestMethodAnnotation.class.getClassLoader()
                .loadClass(("com.pdai.java.annotation.TestMethodAnnotation"))
                .getMethods();

        // 遍历
        for (Method method : methods) {
            // 方法上是否有MyMethodAnnotation注解
            if (method.isAnnotationPresent(MyMethodAnnotation.class)) {
                try {
                    // 获取并遍历方法上的所有注解
                    for (Annotation anno : method.getDeclaredAnnotations()) {
                        System.out.println("Annotation in Method '"
                                + method + "' : " + anno);
                    }

                    // 获取MyMethodAnnotation对象信息
                    MyMethodAnnotation methodAnno = method
                            .getAnnotation(MyMethodAnnotation.class);

                    System.out.println(methodAnno.title());

                } catch (Throwable ex) {
                    ex.printStackTrace();
                }
            }
        }
    } catch (SecurityException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}
  • test output
Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod()' : @java.lang.Deprecated()
Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod()' : @com.pdai.java.annotation.MyMethodAnnotation(title=old static method, description=deprecated old static method)
old static method
Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.genericsTest() throws java.io.FileNotFoundException' : @com.pdai.java.annotation.MyMethodAnnotation(title=test method, description=suppress warning static method)
test method
Annotation in Method 'public java.lang.String com.pdai.java.annotation.TestMethodAnnotation.toString()' : @com.pdai.java.annotation.MyMethodAnnotation(title=toStringMethod, description=override toString method)
toStringMethod

 Deep understanding of annotations

hint

Next, let’s understand annotations in depth from other perspectives

 What new annotations does Java8 provide?

  • @Repeatable

ReferenceJava 8 - Duplicate Annotations

  • ElementType.TYPE_USE

Reference Java 8 - Category Commentary

  • ElementType.TYPE_PARAMETER

ElementType.TYPE_USE(This type includes type declarations and type parameter declarations to facilitate designers to perform type checking) ContainsElementType.TYPE(declarations of classes, interfaces (including annotation types) and enumerations ) andElementType.TYPE_PARAMETER(type parameter declaration), let’s take a look at another example

// 自定义ElementType.TYPE_PARAMETER注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_PARAMETER)
public @interface MyNotEmpty {
}

// 自定义ElementType.TYPE_USE注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface MyNotNull {
}

// 测试类
public class TypeParameterAndTypeUseAnnotation<@MyNotEmpty T>{

  //使用TYPE_PARAMETER类型,会编译不通过
//		public @MyNotEmpty T test(@MyNotEmpty T a){
//			new ArrayList<@MyNotEmpty String>();
//				return a;
//		}

  //使用TYPE_USE类型,编译通过
  public @MyNotNull T test2(@MyNotNull T a){
    new ArrayList<@MyNotNull String>();
    return a;
  }
}

 Does annotation support inheritance?

Annotations do not support inheritance

You cannot use the keyword extends to inherit an @interface, but after the annotation is compiled, the compiler will automatically inherit the java.lang.annotation.Annotation interface.

Although it is found after decompilation that the annotation inherits the Annotation interface, please remember that even if Java's interface can implement multiple inheritance, you still cannot use the extends keyword to inherit @interface when defining annotations.

Different from the inheritance of annotations, the annotated subclass can use @Inherited to inherit the parent class annotation: If a class uses an Annotation modified by @Inherited, its subclass will automatically have this annotation.

 Annotation application scenarios

hint

Finally, let’s look at some application scenarios of annotations in actual development.

 From configuration to annotation - the evolution of the framework

Spring framework changes from configuration to annotation.

Inherit implementation to annotation implementation - Junit3 to Junit4

Most people implement the encapsulation of a module through patterns such as inheritance and composition, but combining annotations can greatly improve the elegance of the implementation (reduce coupling). The evolution from Junit3 to Junit4 is the best example.

  • Tested class
public class HelloWorld {
 	
 	public void sayHello(){
 		System.out.println("hello....");
 		throw new NumberFormatException();
 	}
 	
 	public void sayWorld(){
 		System.out.println("world....");
 	}
 	
 	public String say(){
 		return "hello world!";
 	}
 	
}
  • Junit 3 implements UT

It is implemented by inheriting TestCase, initialization is performed through the Override parent class method, and the test method is obtained through the prefix method of test.

public class HelloWorldTest extends TestCase{
 	private HelloWorld hw;
 	
 	@Override
 	protected void setUp() throws Exception {
 		super.setUp();
 		hw=new HelloWorld();
 	}
 	
 	//1.测试没有返回值
 	public void testHello(){
 		try {
 			hw.sayHello();
 		} catch (Exception e) {
 			System.out.println("发生异常.....");
 		}
 		
 	}
 	public void testWorld(){
 		hw.sayWorld();
 	}
 	//2.测试有返回值的方法
 	// 返回字符串
 	public void testSay(){
 		assertEquals("测试失败", hw.say(), "hello world!");
 	}
 	//返回对象
 	public void testObj(){
 		assertNull("测试对象不为空", null);
 		assertNotNull("测试对象为空",new String());
 	}
 	@Override
 	protected void tearDown() throws Exception {
 		super.tearDown();
 		hw=null;
 	}	
}
  • Junit 4 implements UT

This is achieved by defining @Before, @Test, @After and other annotations.

public class HelloWorldTest {
 	private HelloWorld hw;
 
 	@Before
 	public void setUp() {
 		hw = new HelloWorld();
 	}
 
 	@Test(expected=NumberFormatException.class)
 	// 1.测试没有返回值,有别于junit3的使用,更加方便
 	public void testHello() {
 		hw.sayHello();
 	}
 	@Test
 	public void testWorld() {
 		hw.sayWorld();
 	}
 	
 	@Test
 	// 2.测试有返回值的方法
 	// 返回字符串
 	public void testSay() {
 		assertEquals("测试失败", hw.say(), "hello world!");
 	}
 	
 	@Test
 	// 返回对象
 	public void testObj() {
 		assertNull("测试对象不为空", null);
 		assertNotNull("测试对象为空", new String());
 	}
 
 	@After
 	public void tearDown() throws Exception {
 		hw = null;
 	}
 
}

Here we find that through annotations, we will be more elegant when implementing unit testing. What if you still want to know how Junit4 works? You can read this article:The operating principle of JUnit4 source code analysis opens in a new window.

 Custom annotations and AOP - decoupling through aspects

The most common one is to use Spring AOP aspects to implementUnified operation log management. I found an example in an open source project here (only Show the main code) and show you how to achieve decoupling through annotations.

  • Custom Log annotations
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 模块 
     */
    public String title() default "";

    /**
     * 功能
     */
    public BusinessType businessType() default BusinessType.OTHER;

    /**
     * 操作人类别
     */
    public OperatorType operatorType() default OperatorType.MANAGE;

    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;
}
  • Implement aspects of the log and intercept the custom annotation Log as a point-cutting point

That is, pointcut interception is performed on methods annotated with @Log.

@Aspect
@Component
public class LogAspect {
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    /**
     * 配置织入点 - 自定义注解的包路径
     * 
     */
    @Pointcut("@annotation(com.xxx.aspectj.lang.annotation.Log)")
    public void logPointCut() {
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleLog(joinPoint, null, jsonResult);
    }

    /**
     * 拦截异常操作
     * 
     * @param joinPoint 切点
     * @param e 异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
        try {
            // 获得注解
            Log controllerLog = getAnnotationLog(joinPoint);
            if (controllerLog == null) {
                return;
            }

            // 获取当前的用户
            User currentUser = ShiroUtils.getSysUser();

            // *========数据库日志=========*//
            OperLog operLog = new OperLog();
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
            // 请求的地址
            String ip = ShiroUtils.getIp();
            operLog.setOperIp(ip);
            // 返回参数
            operLog.setJsonResult(JSONObject.toJSONString(jsonResult));

            operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());
            if (currentUser != null) {
                operLog.setOperName(currentUser.getLoginName());
                if (StringUtils.isNotNull(currentUser.getDept())
                        && StringUtils.isNotEmpty(currentUser.getDept().getDeptName())) {
                    operLog.setDeptName(currentUser.getDept().getDeptName());
                }
            }

            if (e != null) {
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            // 处理设置注解上的参数
            getControllerMethodDescription(controllerLog, operLog);
            // 保存数据库
            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     * 
     * @param log 日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(Log log, OperLog operLog) throws Exception {
        // 设置action动作
        operLog.setBusinessType(log.businessType().ordinal());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().ordinal());
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData()) {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(operLog);
        }
    }

    /**
     * 获取请求的参数,放到log中
     * 
     * @param operLog
     * @param request
     */
    private void setRequestValue(OperLog operLog) {
        Map<String, String[]> map = ServletUtils.getRequest().getParameterMap();
        String params = JSONObject.toJSONString(map);
        operLog.setOperParam(StringUtils.substring(params, 0, 2000));
    }

    /**
     * 是否存在注解,如果存在就获取
     */
    private Log getAnnotationLog(JoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null)
        {
            return method.getAnnotation(Log.class);
        }
        return null;
    }
}
  • Use @Log annotation

Taking a simple CRUD operation as an example, part of the code is shown here: every operation on "department" will generate an operation log and store it in the database.

@Controller
@RequestMapping("/system/dept")
public class DeptController extends BaseController {
    private String prefix = "system/dept";

    @Autowired
    private IDeptService deptService;
    
    /**
     * 新增保存部门
     */
    @Log(title = "部门管理", businessType = BusinessType.INSERT)
    @RequiresPermissions("system:dept:add")
    @PostMapping("/add")
    @ResponseBody
    public AjaxResult addSave(@Validated Dept dept) {
        if (UserConstants.DEPT_NAME_NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) {
            return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
        }
        return toAjax(deptService.insertDept(dept));
    }

    /**
     * 保存
     */
    @Log(title = "部门管理", businessType = BusinessType.UPDATE)
    @RequiresPermissions("system:dept:edit")
    @PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(@Validated Dept dept) {
        if (UserConstants.DEPT_NAME_NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) {
            return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
        } else if(dept.getParentId().equals(dept.getDeptId())) {
            return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
        }
        return toAjax(deptService.updateDept(dept));
    }

    /**
     * 删除
     */
    @Log(title = "部门管理", businessType = BusinessType.DELETE)
    @RequiresPermissions("system:dept:remove")
    @GetMapping("/remove/{deptId}")
    @ResponseBody
    public AjaxResult remove(@PathVariable("deptId") Long deptId) {
        if (deptService.selectDeptCount(deptId) > 0) {
            return AjaxResult.warn("存在下级部门,不允许删除");
        }
        if (deptService.checkDeptExistUser(deptId)) {
            return AjaxResult.warn("部门存在用户,不允许删除");
        }
        return toAjax(deptService.deleteDeptById(deptId));
    }

  // ...
}

Similarly, you can also see that permission management is also implemented through a similar annotation (@RequiresPermissions) mechanism. So we can see that the ultimate goal of annotations + AOP is to achieve module decoupling.


Guess you like

Origin blog.csdn.net/abclyq/article/details/134688638