SPRING explore a declarative transaction failure

wedge

The number now using declarative transactions in the Spring development process is much greater than the programmatic transaction, all thanks declarative transaction Let us free from the complex transaction. It will automatically help us to get a connection, the connection is closed, the transaction commit, rollback, exception handling and other operations. Because of all this Spring automatically help us to complete, so we are more likely to fall into the trap of some very low-level.

In this paper we look through a practical example of a trap of some declarative transaction.

text

First, let's look at the following code have committed mistakes which

@Service
public class DelayTradeService {
    ...

    /**
     * 将延时交易信息放入延时队列中
     */
    public void putTradeInfoToQueue() {
        // 从数据库中取出消息数据
        this.takeOutTradeList(int size);
        //TODO 将消息数据放到本地内存队列中
    }

    /**
     * 采用阻塞的方式从数据库中读取消息数据
     * @param size
     * @return
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    private List<DelayTradeInfo> takeOutTradeList(int size) {
        //TODO 在数据库中采用for update的方式获取前size 数量的记录
    }
}

Problem we may see is the first visit of the type of private affairs program

private List<DelayTradeInfo> takeOutTradeList(int size)

Here is the first question affairs in common use:

1. Spring 要求使用@Transactional 注解的方法必须是public类型

Besides the reasons behind here. In fact, another pit also hidden in this code, if not see where there is a problem, we can think about why Spring requires the declaration of a transaction methods must be public type.

Here skip 5 minutes ...

The second problem with this code is here

this.takeOutTradeList(int size);

The wording here is putTradeInfoToQueue method to invoke takeOutTradeList method by means of an internal call.

We all know that the implementation mechanism @Transactional by Spring AOP is achieved, then the second question which in fact can be abstracted as

2.Spring AOP 是不会拦截对象内部方法间的调用

Why is this so? This may not have to revert to implement the logic of AOP. The AOP is essentially a dynamic proxy model, is simply injected through the target object to be called to InvocationHandler (to instantiate a proxy object via the Proxy.newProxyInstance), the proxy object and then calls a new method in the proxy object (again by calling method reflected in the target object) to implement the aspect function. So the key is in effect AOP is whether the request can go to Method Acting class.

That two issues here again“一个类的代理类是什么时候生成的?“、”又是谁将调用目标类中的方法转向了它代理类中的方法?“。

First, Spring ApplicationContext relevant when using bean implementation class is loaded, it will for all singleton bean and not lazy loading, when constructing ApplicationContext will be created these bean, rather than wait until the time was used to create. This is the default non-singleton bean lazy loading applications
Spring after the bean is instantiated will call implements BeanPostProcessorinterface postProcessAfterInitializationmethods to perform some operations after some of the bean initialization
that we look at some of AOP package AnnotationAwareAspectJAutoProxyCreator class (which implements the BeanPostProcessor) interface.

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

// wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // 如果该类有advice则创建proxy,
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 1.通过方法名也能简单猜测到,这个方法就是把bean包装为proxy的主要方法,
        Object proxy = createProxy(
        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());

        // 2.返回该proxy代替原来的bean
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

At this point I can answer two questions above

Agent class is a class Spring bean generated after instantiation, and the proxy class bean will replace the original class bean. It has been used proxyBeanName.method Spring perpetrating a fraud So we call a target class using springBeanName.method.

现在对于为什么通过this调用函数内部方法的形式无法触发AOP的拦截已经是显而易见的了。this调用内部方法是直接使用的是原始对象来调用,已经绕开了Spring的管理所以肯定不会触发AOP。

Why then to require the use of Spring @Transactional annotation type methods must be public, in fact, this method can be abstracted as all that needs to be blocked AOP must be defined as public. Because Spring is not going to manage these private methods.

to sum up

Later total sum up, if you want to use @Transactional or AOP intercept method to be used must comply with the rules a little

  • The type of objective function must be public
  • Call the objective function method must be called by the form springBeanName.method can not use this method called directly inside
发布了418 篇原创文章 · 获赞 745 · 访问量 126万+

Guess you like

Origin blog.csdn.net/u013467442/article/details/98865455