1. An overview of the

In a normal system, when we do some important operations, such as logging in to the system, adding users, deleting users and so on, we need to keep these actions persistent. In this article, we implement log insertion using Spring AOP and Java’s custom annotations. This scheme has less intrusion to original services and more flexible implementation

2. Define log classes

We abstract logs into the following two classes: Function module and operation Type Use enumeration classes to define function module types, such as student and user modules

public enum ModuleType {
    DEFAULT("1"), // Default value STUDENT("2"),// Student module TEACHER("3"); Private ModuleType(String index){this.module = index; } private String module; public StringgetModule() {returnthis.module; }}Copy the code

Use enumeration classes to define the type of the operation: EventType. Such as login, add, delete, update, delete and so on

public enum EventType {
    DEFAULT("1"."default"), ADD("2"."add"), UPDATE("3"."update"), DELETE_SINGLE("4"."delete-single"),
    LOGIN("10"."login"),LOGIN_OUT("11"."login_out");

    private EventType(String index, String name){
        this.name = name;
        this.event = index;
    }
    private String event;
    private String name;
    public String getEvent() {return this.event;
    }

    public String getName() {
        returnname; }}Copy the code

3. Define log annotations

3.1. @ LogEnable

Here we define the value of logging on/off. Logging in this class is enabled only if this value is true

@documented @Retention(RetentionPolicy.runtime) @target ({elementtype.type}) public @interface LogEnable {/** * iftrue, the LogEvent below the class starts, otherwise * @ is ignoredreturn
     */
    boolean logEnable() default true;
}
Copy the code

3.2. @ LogEvent

This is where the log details are defined. If the annotation is on a class, this parameter is the default value for all methods of the class. If the annotation is on a method, it only applies to that method

@Documented @Retention(RetentionPolicy.RUNTIME) @Target({java.lang.annotation.ElementType.METHOD, ElementType.TYPE}) public @interface LogEvent { ModuleType module() default ModuleType.DEFAULT; EventType Event () default eventType.default; // Log event type String desc() default""; // Description}Copy the code

3.3. @ LogKey

If the annotation is on a method, the parameters of the entire method are saved to the log in JSON format. If the annotation is annotated on both a method and a class, the annotation on the method overrides the value on the class.

@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogKey {
     String keyName() default ""; // Name of key Boolean isUserId() defaultfalse; // Whether this field is the userId of the operation. Boolean isLog() defaulttrue; // Add to log}Copy the code

4. Define log processing classes

4.1. LogAdmModel

Define a class that holds log information

public class LogAdmModel { private Long id; private String userId; Private String userName; private String admModel; // module private String admEvent; Private Date createDate; // Private String admOptContent; Private String desc; / / noteset/ get slightly}Copy the code

4.2. ILogManager

ILogManager defines the interface class for logging processing. We can store logs to the database, and we can also send logs to open middleware, such as Redis, MQ, etc. Each logging class is an implementation class for this interface

Public interface ILogManager {/** * log processing module * @param paramLogAdmBean */ void dealLog(LogAdmModel paramLogAdmBean); }Copy the code

4.3. DBLogManager

ILogManager implements the log class to the library. This is just simulated warehousing

@Service public class DBLogManager implements ILogManager { @Override public void dealLog(LogAdmModel paramLogAdmBean) {  System.out.println("Save the log to the database as follows:"+ JSON.toJSONString(paramLogAdmBean)); }}Copy the code

5. AOP configuration

5.1. LogAspect defines AOP classes

  • Annotate this class with @aspect
  • Use @pointcut to define the package and class methods to intercept
  • We use @around to define the method
@Component
@Aspect
public class LogAspect {
    @Autowired
    private LogInfoGeneration logInfoGeneration;

    @Autowired
    private ILogManager logManager;

    @Pointcut("execution(* com.hry.spring.mvc.aop.log.service.. *. * (..) )")
    public void managerLogPoint() {
    }

    @Around("managerLogPoint()"Public Object aroundManagerLogPoint(ProceedingJoinPoint JP) throws Throwable {... .}}Copy the code

AroundManagerLogPoint: Main business process of the main method 1. Check whether the intercepting method class is annotated by @logenable. If so, log logic, otherwise normal logic 2. Check whether the interception method is @logEvent. If so, follow the log logic. Otherwise, follow the normal logic 3. Some parameters of the log are generated based on the value of @logEvent in the obtaining method. Defined on a class of the @ LogEvent value as the default value is 4. Call logInfoGeneration processingManagerLogMessage fill other parameters in the log, a method about 5 behind us. 6. If the execution is successful, the logManager performs the processing of the log (we only log the success of the execution, you can also define the failure of the log).

	    @Around("managerLogPoint()") public Object aroundManagerLogPoint(ProceedingJoinPoint jp) throws Throwable { Class target = jp.getTarget().getClass(); // Get LogEnable LogEnablelogEnable = (LogEnable) target.getAnnotation(LogEnable.class);
	        if(logEnable == null || !logEnable.logEnable()){
	            returnjp.proceed(); } // Get the class LogEvent as the default LogEventlogEventClass = (LogEvent) target.getAnnotation(LogEvent.class);
	        Method method = getInvokedMethod(jp);
	        if(method == null){
	            returnjp.proceed(); } // Get LogEvent LogEvent on the methodlogEventMethod = method.getAnnotation(LogEvent.class);
	        if(logEventMethod == null){
	            return jp.proceed();
	        }
	
	        String optEvent = logEventMethod.event().getEvent();
	        String optModel = logEventMethod.module().getModule();
	        String desc = logEventMethod.desc();
	
	        if(logEventClass ! OptEvent = optevent.equals (eventType.default)?logEventClass.event().getEvent() : optEvent;
	            optModel = optModel.equals(ModuleType.DEFAULT) ? logEventClass.module().getModule() : optModel;
	        }
	
	        LogAdmModel logBean = new LogAdmModel();
	        logBean.setAdmModel(optModel);
	        logBean.setAdmEvent(optEvent);
	        logBean.setDesc(desc);
	        logBean.setCreateDate(new Date());
	        logInfoGeneration.processingManagerLogMessage(jp,
	                logBean, method);
	        Object returnObj = jp.proceed();
	
	        if(optevent.equals (eventtype.login)){//TODO If the LOGIN succeeds, check whether the LOGIN succeeds according to the return value. If the LOGIN succeeds, add the log. It's a little bit easier hereif(returnObj ! = null) { this.logManager.dealLog(logBean); }}else {
	            this.logManager.dealLog(logBean);
	        }
	        return returnObj; } /** * get the request method ** @param jp * @return*/ public Method getInvokedMethod(JoinPoint jp) {List classList = new ArrayList();for(Object obj : jp.getArgs()) { classList.add(obj.getClass()); } Class[] argsCls = (Class[]) classList.toArray(new Class[0]); // called methodName String methodName = jp.getsignature ().getname (); Method method = null; try { method = jp.getTarget().getClass().getMethod(methodName, argsCls); } catch (NoSuchMethodException e) { e.printStackTrace(); }returnmethod; }}Copy the code

6. The scheme to apply the above scheme in practice

Here we simulate a student-operated business and apply the above annotations to it and intercept the log

6.1. IStudentService

Business interface class that performs general CRUD

public interface IStudentService {

    void deleteById(String id, String a);

    int save(StudentModel studentModel);

    void update(StudentModel studentModel);

    void queryById(String id);
}
Copy the code

6.2. StudentServiceImpl:

  • LogEnable: Enable log interception
  • Class @logEvent defines all modules
  • The @logeven method defines other information about the log
@service@logenable // Enables log intercepting @logEvent (Module = moduleType. STUDENT) public class StudentServiceImpl implements IStudentService { @Override @LogEvent(event = EventType.DELETE_SINGLE, desc ="Delete record"Public void deleteById(@logKey (keyName =)"id") String id, String a) {
        System.out.printf(this.getClass() +  "deleteById id = " + id);
    }

    @Override
    @LogEvent(event = EventType.ADD, desc = "Keep records"Public int save(StudentModel StudentModel) {system.out.printf (this.getClass() +"save save = " + JSON.toJSONString(studentModel));
        return 1;
    }

    @Override
    @LogEvent(event = EventType.UPDATE, desc = "Update record"Public void update(StudentModel StudentModel) {system.out.printf (this.getClass() +"save update = "+ JSON.toJSONString(studentModel)); } @override public void queryById(String id) {system.out.printf (this.getClass() +"queryById id = "+ id); }}Copy the code

Execute the test class and print the following information to indicate that our log annotation configuration is working:

Save the log to the database. The log content is as follows: {"admEvent":"4"."admModel":"1"."admOptContent":"{\"id\":\"1\"}"."createDate": 1525779738111,"desc":"Delete record"}
Copy the code

7. Code

Please use tag V0.21 instead of master as I can’t guarantee that the master code will always be the same