首页 > 技术文章 > spring 自定义事物同步器(一): TransactionSynchronizationManager 解析

yuyutianxia 2017-01-23 15:21 原文

一、.JPA 获取 Hibernate的session 

     try {
            session = entityManager.unwrap(Session.class);
        } catch (Exception e) {// from transactionTemplate?
            //throw new IllegalStateException("No transactional EntityManager available");
            EntityManagerFactory entityManagerFactory = jpaTransactionManager.getEntityManagerFactory();
            EntityManagerHolder holder;
            if(TransactionSynchronizationManager.hasResource(entityManagerFactory)){
                 holder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(entityManagerFactory);
            }else{
                holder = new EntityManagerHolder(entityManagerFactory.createEntityManager());
                TransactionSynchronizationManager.bindResource(entityManagerFactory, holder);
            }
            session = holder.getEntityManager().unwrap(Session.class);
        }

 

二、TransactionSynchronizationManager.getResource()

    /**
     * Retrieve a resource for the given key that is bound to the current thread.
     * @param key the key to check (usually the resource factory)
     * @return a value bound to the current thread (usually the active
     * resource object), or {@code null} if none
     * @see ResourceTransactionManager#getResourceFactory()
     */
    public static Object getResource(Object key) {
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Object value = doGetResource(actualKey);
        if (value != null && logger.isTraceEnabled()) {
            logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
                    Thread.currentThread().getName() + "]");
        }
        return value;
    }

 

三、TransactionSynchronizationUtils.unwrapResourceIfNecessary(key)

    /**
     * Unwrap the given resource handle if necessary; otherwise return
     * the given handle as-is.
     * @see org.springframework.core.InfrastructureProxy#getWrappedObject()
     */
    static Object unwrapResourceIfNecessary(Object resource) {
        Assert.notNull(resource, "Resource must not be null");
        Object resourceRef = resource;
        // unwrap infrastructure proxy
        if (resourceRef instanceof InfrastructureProxy) {
            resourceRef = ((InfrastructureProxy) resourceRef).getWrappedObject();
        }
        if (aopAvailable) {
            // now unwrap scoped proxy
            resourceRef = ScopedProxyUnwrapper.unwrapIfNecessary(resourceRef);
        }
        return resourceRef;
    }

 

四、ScopedProxy

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

<bean id="userManager" class="com.foo.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>

In the preceding example, the singleton bean userManager is injected with a reference to the HTTP Session-scoped bean userPreferences. The salient point here is that the userManager bean is a singleton: it will be instantiated exactly once per container, and its dependencies (in this case only one, the userPreferences bean) are also injected only once. This means that the userManager bean will only operate on the exact same userPreferences object, that is, the one that it was originally injected with.

This is not the behavior you want when injecting a shorter-lived scoped bean into a longer-lived scoped bean, for example injecting an HTTP Session-scoped collaborating bean as a dependency into singleton bean. Rather, you need a single userManager object, and for the lifetime of an HTTP Session, you need a userPreferences object that is specific to said HTTP Session. Thus the container creates an object that exposes the exact same public interface as the UserPreferences class (ideally an object that is a UserPreferences instance) which can fetch the realUserPreferences object from the scoping mechanism (HTTP request, Session, etc.). The container injects this proxy object into the userManagerbean, which is unaware that  this UserPreferences reference is a proxy. In this example, when a UserManager instance invokes a method on the dependency-injected UserPreferences object, it actually is invoking a method on the proxy. The proxy then fetches the real UserPreferences object from (in this case) the HTTP Session, and delegates the method invocation onto the retrieved real UserPreferences object.

引用:spring docs

 


五、InfrastructureProxy 结构代理

  Interface to be implemented by transparent resource proxies that need to be considered as equal to the underlying resource,

for example for consistent lookup key comparisons. Note that this interface does imply such special semantics and does not

constitute a general-purpose mixin!  

   Such wrappers will automatically be unwrapped for key comparisons in TransactionSynchronizationManager. Only fully transparent

proxies, e.g. for redirection or service lookups, are supposed to implement this interface. Proxies that decorate the target object

with new behavior, such as AOP proxies, do not qualify here!

 

被认为等同于底层资源的透明资源代理要实现的接口,例如用于一致的查找的关键字比较。注意,这个接口意味着这样的特殊语义,并不构成一般用途的聚合!

这种包装器将在TransactionSynchronizationManager中自动被拆箱来进行关键字比较。只有完全透明的代理,例如 对于重定向或服务查找,应该实现此接口。

使用新行为来装饰目标对象的代理(例如AOP代理)不适用于此处!

 

推荐阅读