com.arto.core.intercepter.TxMessageAspect.java Source code

Java tutorial

Introduction

Here is the source code for com.arto.core.intercepter.TxMessageAspect.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
 * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
 * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.arto.core.intercepter;

import com.arto.core.bootstrap.MqClient;
import com.arto.core.event.MqEvent;
import com.arto.core.exception.MqClientException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.Ordered;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import javax.sql.DataSource;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.List;

/**
 * ??jdbc???????.
 * (?????????)
 *
 * Created by xiong.j on 2017/2/15.
 */
@Component
@Aspect
public class TxMessageAspect implements Ordered, ResourceLoaderAware, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private ResourceLoader resourceLoader;

    @Autowired
    private DataSourceTransactionManager transactionManager;

    //?TXAction
    @Pointcut("@annotation(com.arto.core.annotation.TxMessage)")
    public void pointcutTxMessage() {
    }

    @Around("pointcutTxMessage()")
    public Object doTransactionMessage(ProceedingJoinPoint pjp) throws Throwable {
        // ???
        DataSource dataSource = transactionManager.getDataSource();
        // ??
        assertTransactional(dataSource);
        // ??
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        final Connection conn = conHolder.getConnection();
        // ???
        Method setterMethod = ReflectionUtils.findMethod(ConnectionHolder.class, "setConnection", Connection.class);
        ReflectionUtils.makeAccessible(setterMethod);
        // ?commit
        ReflectionUtils.invokeMethod(setterMethod, conHolder, Proxy.newProxyInstance(
                resourceLoader.getClassLoader(), new Class<?>[] { Connection.class }, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            return method.invoke(conn, args);
                        } finally {
                            // ?commit??????
                            if ("commit".equals(method.getName())) {
                                List<MqEvent> txMessages = TxMessageContextHolder.getTxMessages();
                                if (txMessages != null) {
                                    MqEvent message;
                                    for (int i = 0; i < txMessages.size(); i++) {
                                        message = txMessages.get(i);
                                        MqClient.getPipeline(message.getType()).offer(message);
                                    }
                                }
                                TxMessageContextHolder.clear();
                            } else if ("rollback".equals(method.getName())) {
                                TxMessageContextHolder.clear();
                            }
                        }
                    }
                }));
        // ?
        return pjp.proceed();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }

    private void assertTransactional(DataSource dataSource) throws Throwable {
        Assert.notNull(dataSource, "Can't get datasource from bean 'transactionManager'.");
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        if (conHolder == null || conHolder.getConnectionHandle() == null
                || !conHolder.isSynchronizedWithTransaction()) {
            throw new MqClientException("It's not in spring jdbc transaction.");
        }
    }
}