Nested calls inside spring aop

background

In discussing the importance of naming, we have explained some issues

But it seems that there is no small partner to pick on me~ In fact, the way to go wrong is this

public TmPrintCount getPrintCountIfNull(TmPrintCount printCount) {    String idOwnOrg = WxbStatic.getOrg();
    TmPrintCount tmPrintCount = this.getPrintCountByBillId(printCount);
    if (null == tmPrintCount){
        printCount.setPkId(baseService.getUUid());
        printCount.setPrintCount(1);
        printCount.setIdOwnOrg(idOwnOrg);
        this.addPrintCount(printCount);
        return printCount;
    }
    return tmPrintCount;
}

Has anyone found out that the addprintCount method is actually called? ? ? According to reason, shouldn't it be Required here?

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="getBillNo" read-only="false" propagation="REQUIRES_NEW"
                   timeout="20"/>
        <tx:method name="addBranch" read-only="false" rollback-for="java.lang.Exception"
                   timeout="100"/>
        <tx:method name="load*" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="find*" read-only="true" propagation="SUPPORTS"
                   timeout="20"/>
        <tx:method name="get*" read-only="true" propagation="SUPPORTS"
                   timeout="10"/>
        <tx:method name="select*" read-only="true" propagation="SUPPORTS"
                   timeout="10"/>
        <tx:method name="query*" read-only="true" propagation="SUPPORTS"
                   timeout="10"/>
        <tx:method name="is*" read-only="true" propagation="SUPPORTS"
                   timeout="5"/>
        <tx:method name="list*" read-only="true" propagation="SUPPORTS"
                   timeout="50"/>
        <tx:method name="loginCheck" read-only="true" propagation="SUPPORTS"
                   timeout="10"/>
        <tx:method name="selectDiff*" read-only="true" propagation="SUPPORTS"
                   timeout="120"/>
        <tx:method name="importBatch*" propagation="REQUIRED" rollback-for="java.lang.Exception"
                   timeout="180"/>
        <tx:method name="fastImport*" propagation="REQUIRED" rollback-for="java.lang.Exception"
                   timeout="180"/>
        <tx:method name="createMonitor*" propagation="REQUIRED" rollback-for="java.lang.Exception"
                   timeout="500"/>
        <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception"
                   timeout="50"/>
    </tx:attributes>

If everything here is OK, wouldn't it be a delight~ Wouldn't the previous problem be gone? ? ? On the importance of naming

analyze

With this question, let's take a look at how Spring implements transactions~

In fact, Spring implements many business enhancements through proxies

Proxy

The way the dynamic proxy is implemented is actually very simple and can be understood as follows

public class UserImpl implements Iuser {
  @Override
  public void eat(String s) {
    System.out.println("我要吃"+s);
  }
}
public class UserProxy implements Iuser {
  private Iuser user = new UserImpl();
  @Override
  public void eat(String s) {
    System.out.println("代理前置内容");
    user.eat(s);
    System.out.println("代理后置内容");
  }
}

From here, we can see that in fact, the eat called in userProxy is ultimately the method to call to UserImpl's eat ===" The key is that this at this time is UserImpl 

It can be seen from the above that the transaction is the same. If you write this.XXXX, then this here refers to UserImpl [so it will not become UserProxy] and the real transaction weaving is in the Aop object, so the transaction naturally cannot be embedded set of calls to pass

CGLIB

Friends who love to learn must have thought that we often say that java proxy is based on interfaces, so cglib can be based on ordinary classes [how good is cglib]

From the learning of Java, we usually know that according to the polymorphism theory, it is completely possible to call the implementation method of the subclass [that is, the proxy object]. Does this mean that in Spring, if we choose cglib, we can implement the embedding of internal affairs What about sets of calls?

Essentially, this is not wrong. However, Spring aop does not use the same routine as we imagined~

This is what we imagine

public class UserImpl implements Iuser {
  @Override
  public void eat(String s) {
    System.out.println("我要吃"+s);
       drink("水");
  }
    @Override
  public void drink(String s) {
    System.out.println("我要喝"+s);
  }
}
public class UserProxy implements Iuser {
  private Iuser user = new UserImpl();
  @Override
  public void eat(String s) {
    System.out.println("代理前置内容");
       System.out.println("我要吃"+s);
       drink("水");
       System.out.println("代理后置内容");
  }
    @Override
  public void drink(String s) {
       System.out.println("代理前置内容");
    System.out.println("我要喝"+s);
        System.out.println("代理后置内容");
  }
}

Then if you can call the drink's proxy in the scenario of calling eat at the same time ===" print out the proxy content

But in essence, it is not possible to call the inner nested in Spring

see source code 

Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
 
Object var15;
try {
    if (this.advised.exposeProxy) {
        oldProxy = AopContext.setCurrentProxy(proxy);
        setProxyContext = true;
    }
 
    target = this.getTarget();
    if (target != null) {
        targetClass = target.getClass();
    }
 
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    Object retVal;
    if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
        Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
        retVal = methodProxy.invoke(target, argsToUse);
    } else {
        retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
    }
 
    retVal = CglibAopProxy.processReturnType(proxy, target, method, retVal);
    var15 = retVal;
} finally {
    if (target != null) {
        this.releaseTarget(target);
    }
 
    if (setProxyContext) {
        AopContext.setCurrentProxy(oldProxy);
    }
 
}
 
return var15;

can be seen

target = this.getTarget();

In fact, when the call is made, the target===" is obtained, which is the UserImpl above.

So in fact, it is the same as the principle of jdk proxy, because the final call is the actual target===", which is the proxy object

private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
 
   private final MethodProxy methodProxy;
 
   private boolean protectedMethod;
 
   public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
         Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
      super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
      this.methodProxy = methodProxy;
      this.protectedMethod = Modifier.isProtected(method.getModifiers());
   }
 
   /**
    * Gives a marginal performance improvement versus using reflection to
    * invoke the target when invoking public methods.
    */
   @Override
   protected Object invokeJoinpoint() throws Throwable {
      if (this.protectedMethod) {
         return super.invokeJoinpoint();
      }
      else {
         return this.methodProxy.invoke(this.target, this.arguments);
      }
   }
}

You can see that it is called when the method is protected

/**
 * Invoke the joinpoint using reflection.
 * Subclasses can override this to use custom invocation.
 * @return the return value of the joinpoint
 * @throws Throwable if invoking the joinpoint resulted in an exception
 */
protected Object invokeJoinpoint() throws Throwable {
   return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}

Otherwise call

return this.methodProxy.invoke(this.target, this.arguments);

Is there any small partner who considers this no private call?

solve a case

As analyzed above, if the addPrintCount method is called in the getPrintCountIfNull method, then the aop aspect cannot be executed at this time, so naturally a new transaction cannot be obtained.

solution

For the above problem, it is obvious that this cannot be used, so there are several simple solutions

  1. To avoid such problems, call methods in other classes [most of them should be avoided]
  2. Using AopProxy

    AopContext.currentProxy()

     

  3. This method needs to open expose-proxy="true"

  4. By some means  http://fyting.iteye.com/blog/109236

  5. In Spring 4.3, self inject was officially launched (previously it was not possible to inject itself or it would cause circular dependencies) due to special treatment, it cannot be injected through @Resource and must be injected through @Autowired   https://github.com/spring-projects/spring-framework/commit /4a0fa69ce469cae2e8c8a1a45f0b43f74a74481d
    like 

    /**
     * Created by qixiaobo on 2018-02-01.
     */
    @Service
    @Transactional(rollbackFor = Exception.class, timeout = 1)
    public class TsInsuranceAttachmentServiceImpl extends AbstractService<TsInsuranceAttachment, TsInsuranceAttachmentVo, TsInsuranceAttachmentSo, Integer> implements TsInsuranceAttachmentService {
        @Resource
        private TsInsuranceAttachmentMapper tsInsuranceAttachmentMapper;
     
        @Resource
        private TsInsuranceAttachmentService self;
    }

     

other problems

  1. Can cglib replace java proxy?
  2. We use Spring aop to also introduce a bunch of aspectj jars, so can we use aspectj? We have been talking about cglib and dynamic proxy. Can you remove these jars if you don't use aspectj, and if you use aspectj, have you used aspectj in production?

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325296312&siteId=291194637