This is the 8th day of my participation in the First Challenge 2022

Today in the use of mybatis-plus author Of Another project, Lock4J for the project of distributed lock, to solve the multi-instance situation of the interface lock, so that the business of shared resources in a time node will only be executed by a thread.

Lock4j and Mybatis -plus are similar, adhering to the original intention of human use, lock4j is very simple to use, just need to use @lock4j on the interface method to lock and set some simple timeout parameters.

It was discovered that an interface that needed to be locked was a private method (originally ReentrantLock was used in standalone cases), so the modification simply added @lock4J annotations to the interface.

public void saveInfo(String userId){
  // A lot of business logic processing.this.finalSaveEffective(userId);  
}

@Lock4j(keys = {"#userId"}, expire = "18000", acquireTimeout = "10000", keyBuilder = DefaultKeyBuilder.class)
private void finalSaveEffective(String userId) {
  //reentrantLock.lock();
  try{
    // checkInfo(..) ; mapper.insert(..) ; }catch(Exception e){
    log.error(e.getMessage(), e);
    throw new BusinessException(e.getMessage());
  } finally{
    //reentrantLock.unlock();}}Copy the code

This. XXX can not be used as a proxy method for @transaction. This. XXX cannot be used as a proxy method for @transaction.

In order to understand why, instead of just guessing and memorizing, I decided to use the @Transaction annotation to understand the underlying mechanism.

Aop in Spring is basically implemented through dynamic proxies, which are implemented in two ways (JDK dynamic proxies and Cglib dynamic proxies). Here is a brief simulation of the use of two dynamic proxies.

/** * top-level interface *@Author: xiaocainiaoya
 * @Date: 2021/04/16 22:30:50 * * /
public interface UserFacade {

    void insertUserInfo(a);

    void getUserInfo(a);

}

/** * implements *@Author: xiaocainiaoya
 * @Date10:28 PM **/
public class UserService implements UserFacade{

    @Override
    public void insertUserInfo(a) {
        getUserInfo();
        System.out.println("Insert user information");
    }

    @MockAnnotation
    @Override
    public void getUserInfo(a) {
        System.out.println("Search for Book Information"); }}/** * mock comments **@Author: xiaocainiaoya
 * @Date: 2021/04/16 22:32:27 * * /
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MockAnnotation {
}
Copy the code
1. JDK dynamic proxy
/** * JDK dynamic proxy form **@Author: xiaocainiaoya
 * @Date: 2021/04/16 22:33:07 * * /
public class UserFacadeJdkProxy implements InvocationHandler {

    private Object target;

    public Object getProxy(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                                      target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Enter call method");

        Annotation annotation = target.getClass()
          	.getDeclaredMethod(method.getName(), method.getParameterTypes())
            .getAnnotation(MockAnnotation.class);
        if(ObjectUtils.isEmpty(annotation)){
            return method.invoke(target, args);
        }
        System.out.println("Preprocessing of an AOP method");
        Object result = method.invoke(target, args);
        System.out.println("Post-processing of an AOP method");
        return result;
    }

    public static void main(String[] args) {
        UserFacadeJdkProxy proxy = new UserFacadeJdkProxy();
        UserFacade userFacade = (UserFacade) proxy.getProxy(new UserService());
        userFacade.insertUserInfo();
        //userFacade.getUserInfo();}}// Execution result
//> Task :UserFacadeJdkProxy.main()
// enter the call method
// Query the book information
// Insert user information
Copy the code
2. Cglib dynamic proxy
/** * cglib proxy **@Author: xiaocainiaoya
 * @Date: 2021/04/16 22:34:15 * * /
public class UserFacadeCglibProxy implements MethodInterceptor {

    private Object target;

    public Object getProxy(Object target){
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Enter call method");

        Annotation annotation = target.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes())
                .getAnnotation(MockAnnotation.class);
        if(ObjectUtils.isEmpty(annotation)){
            return methodProxy.invoke(target, args);
            //return methodProxy.invokeSuper(o, args);
        }
        System.out.println("Preprocessing of an AOP method");
        Object result = methodProxy.invoke(target, args);
        //Object result = methodProxy.invokeSuper(o, args);
        System.out.println("Post-processing of an AOP method");
        return result;
    }

    public static void main(String[] args) {
        UserFacadeCglibProxy proxy = new UserFacadeCglibProxy();
        UserFacade userFacade = (UserFacade)proxy.getProxy(new UserService());
        //userFacade.getUserInfo();userFacade.insertUserInfo(); }}// Execution result
//> Task :UserFacadeCglibProxy.main()
// enter the call method
// Query the book information
// Insert user information
Copy the code

It follows from the above that no matter which dynamic proxy AOP is implemented, annotations will not work using the this.xxx writing style. And if the annotation method is final or private, it cannot enter the proxy method. The reason is that JDK dynamic proxy is based on interface proxy and Cglib dynamic proxy is based on inheritance. In fact, the proxy object of either dynamic proxy cannot enter target’s private method and final method. Therefore, according to the above dynamic proxy, it can be concluded that calling this class interface in the way of this. XXX is directly calling this class interface through the proxied object instead of the proxy object, so the corresponding invoke method or Intercept method cannot be entered and the corresponding annotation cannot be parsed. If you feel that changing this. XXX to class A calls class B, the code needs to be forced to move to another class. In practice, you can get the proxy object once via ApplicationContextUtil.

// this.getUserInfo(); So let me rewrite it this way
UserFacade userFacade = ApplicationContextUtil.getBean("userService");
userFacade.getUserInfo();
Copy the code

But here’s the fun part: Notice the two lines of comments in the UserFacadeCglibProxy class, which uses methodProxy.invokesuper (O, args); Invoice () invoke.this.xxx invoke.this.xxx invoke.this.xxx

> Task: UserFacadeCglibProxy. The main () to call methods Aop calling methods into a method of pre-processing query book information aop rear handle insert user information of a methodCopy the code

Here is a simple distinction between the two calls:

The entire execution of methodProxy.invoke(target, args) is:

  1. The client called the proxy object’sinsertUserInfo()methods
  2. Of the proxy objectinterceptmethods
  3. throughmethodProxy.invoke(target, args)Of a proxied objectinsertUserInfo()
  4. At this momentthis.getUserInfo()In thethisIs a proxied object, so it is not fired when calledinterceptmethods
  5. End of the call

Methodproxy.invokesuper (O, args) executes as follows:

  1. The client invoked the proxy objectinsertUserInfo()methods
  2. Of the proxy objectinterceptmethods
  3. throughmethodProxy.invokeSuper(o, args)Of the proxied objectinsertUserInfo()
  4. At this momentthis.getUserInfo()In thethisIt’s a proxy object, sogetUserInfo()Will trigger againintercept()
  5. Of the proxied objectgetUserInfo()
  6. End of the call

So ultimately, it’s this, and whether this represents a proxy or a target.