Java tutorial
/* * Copyright (C) 2008 feilong (venusdrogon@163.com) * * Licensed 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.feilong.spring.jdbc.datasource; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import net.sf.json.JSONException; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttributeSource; import com.feilong.commons.core.date.DateExtensionUtil; import com.feilong.commons.core.lang.ThreadUtil; import com.feilong.commons.core.tools.json.JsonUtil; import com.feilong.commons.core.util.Validator; import com.feilong.spring.aop.AbstractAspect; import com.feilong.spring.aop.ProceedingJoinPointUtil; import com.feilong.spring.transaction.interceptor.TransactionAttributeUtil; /** * ,/?,??()?. * * <h3>??:</h3> * * <blockquote> * <ol> * <li> * controller AManager.a(); A adatabase</li> * <li></li> * <li></li> * <li> * * <pre> * ? AManager.a(){ BManager.b();} , * A adatabase * B bdatabase * controller A ?, MultipleGroupReadWriteStatusHolder.setMultipleDataSourceGroupName(adatabase); * A.a(){ B.b();} * ?,? * </pre> * * </li> * </ol> * </blockquote> * * @author <a href="mailto:venusdrogon@163.com">feilong</a> * @version 1.1.1 201545 ?6:59:04 * @see com.feilong.spring.aop.AbstractAspect * @see org.aspectj.lang.annotation.Aspect * @see org.aspectj.lang.annotation.Around * @see org.springframework.transaction.interceptor.TransactionAttributeSource * @see org.springframework.core.Ordered * @see "loxia.aspect.ReadWriteDataSourceAspect" * @see org.springframework.transaction.annotation.Transactional * @since 1.1.1 */ @Aspect public class MultipleGroupReadWriteDataSourceAspect extends AbstractAspect { /** The Constant logger. */ private static final Logger log = LoggerFactory.getLogger(MultipleGroupReadWriteDataSourceAspect.class); /** The transaction attribute souce. */ @Autowired(required = false) private TransactionAttributeSource transactionAttributeSouce; /** * Point. * * @param proceedingJoinPoint * the proceeding join point * @return the object * @throws Throwable * the throwable */ @Around("this(loxia.dao.ReadWriteSupport)") public Object point(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Signature signature = proceedingJoinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; // TransactionAttribute transactionAttribute = null; if (null != transactionAttributeSouce) { transactionAttribute = transactionAttributeSouce.getTransactionAttribute(methodSignature.getMethod(), proceedingJoinPoint.getTarget().getClass()); } MultipleGroupDataSource multipleGroupDataSourceAnnotation = getAnnotation(proceedingJoinPoint, MultipleGroupDataSource.class); //?? String groupName; //?multipleGroupDataSourceAnnotation //? ? if (null == multipleGroupDataSourceAnnotation || Validator.isNullOrEmpty(multipleGroupDataSourceAnnotation.value())) { //nothing to do groupName = null; } else { groupName = multipleGroupDataSourceAnnotation.value(); } return this.proceed(proceedingJoinPoint, transactionAttribute, groupName); } /** * Proceed. * * @param proceedingJoinPoint * the proceeding join point * @param transactionAttribute * the transaction attribute * @param groupName * the group name * @return the object * @throws Throwable * the throwable * @since 1.1.1 */ private Object proceed(ProceedingJoinPoint proceedingJoinPoint, TransactionAttribute transactionAttribute, String groupName) throws Throwable { //?holder String previousDataSourceNameHolder = MultipleGroupReadWriteStatusHolder.getMultipleDataSourceGroupName(); String currentThreadInfo = JsonUtil.format(ThreadUtil.getCurrentThreadMapForLog()); if (log.isInfoEnabled()) { Map<String, Object> mapForLog = new LinkedHashMap<String, Object>(); mapForLog.put("groupName", groupName); mapForLog.put("previousDataSourceNameHolder", previousDataSourceNameHolder); mapForLog.put("transactionAttribute:", TransactionAttributeUtil.getMapForLog(transactionAttribute)); log.info("before determine datasource :[{}],proceedingJoinPoint info:[{}],current thread info:[{}]", JsonUtil.format(mapForLog), getProceedingJoinPointJsonInfoExcludeJsonException(proceedingJoinPoint), currentThreadInfo); } boolean isSetHolder = isSetHolder(transactionAttribute, groupName); //*************************************************************************** if (isSetHolder) { //read or write String readWriteSupport = this.getReadWriteSupport(transactionAttribute); String targetDataSourcesKey = MultipleGroupReadWriteUtil.getTargetDataSourcesKey(groupName, readWriteSupport); log.info("set targetDataSourcesKey:[{}],current thread info:[{}]", targetDataSourcesKey, currentThreadInfo); MultipleGroupReadWriteStatusHolder.setMultipleDataSourceGroupName(targetDataSourcesKey); } try { return this.proceed(proceedingJoinPoint); } catch (Throwable e) { throw e; } finally { if (Validator.isNotNullOrEmpty(previousDataSourceNameHolder)) { log.info("Back to previous Read/Write Status:[{}],current thread info:[{}]", previousDataSourceNameHolder, currentThreadInfo); //?,? MultipleGroupReadWriteStatusHolder.setMultipleDataSourceGroupName(previousDataSourceNameHolder); } //TODO ?? loxia? //?previousDataSourceNameHolder, else { log.info( "previousDataSourceNameHolder is NullOrEmpty,Clear Read/Write Status:[{}],current thread info:[{}]", MultipleGroupReadWriteStatusHolder.getMultipleDataSourceGroupName(), currentThreadInfo); MultipleGroupReadWriteStatusHolder.clearMultipleDataSourceGroupName(); } } } /** * ???. * * @param transactionAttribute * the transaction attribute * @param groupName * the group name * @return true, if checks if is set holder * @since 1.1.1 */ private boolean isSetHolder(TransactionAttribute transactionAttribute, String groupName) { if (Validator.isNotNullOrEmpty(groupName)) { return true; } else { if (null == transactionAttribute) { return true; } int propagationBehavior = transactionAttribute.getPropagationBehavior(); //TODO ?? loxia? return propagationBehavior != TransactionDefinition.PROPAGATION_REQUIRES_NEW; } } /** * ?,<br> * {@link org.springframework.transaction.interceptor.TransactionAttribute}?. * * @param transactionAttribute * the transaction attribute * @return the read write support * @see "org.postgresql.jdbc2.AbstractJdbc2Connection#setReadOnly(boolean)" * @since 1.1.1 */ private String getReadWriteSupport(TransactionAttribute transactionAttribute) { String readWriteSupport = ""; if (null == transactionAttribute) { return loxia.dao.ReadWriteSupport.READ; } boolean mustWrite = false; int propagationBehavior = transactionAttribute.getPropagationBehavior(); switch (propagationBehavior) { //? case TransactionDefinition.PROPAGATION_REQUIRED: break; //,????,??????? case TransactionDefinition.PROPAGATION_REQUIRES_NEW: mustWrite = true; break; //? ??? case TransactionDefinition.PROPAGATION_SUPPORTS: break; //????,????? case TransactionDefinition.PROPAGATION_NOT_SUPPORTED: break; //???IllegalTransactionStateException case TransactionDefinition.PROPAGATION_MANDATORY: break; //?? ?IllegalTransactionStateException case TransactionDefinition.PROPAGATION_NEVER: break; //? case TransactionDefinition.PROPAGATION_NESTED: break; default: throw new UnsupportedOperationException( "propagationBehavior:[" + propagationBehavior + "] not support!"); } if (mustWrite) { log.info("New writable connection is required for new transaction."); readWriteSupport = loxia.dao.ReadWriteSupport.WRITE; } else { //see "org.postgresql.jdbc2.AbstractJdbc2Connection#setReadOnly(boolean)" boolean readOnly = transactionAttribute.isReadOnly(); readWriteSupport = readOnly ? loxia.dao.ReadWriteSupport.READ : loxia.dao.ReadWriteSupport.WRITE; } return readWriteSupport; } /** * Proceed. * * @param proceedingJoinPoint * the proceeding join point * @return the object * @throws Throwable * the throwable * @since 1.1.1 */ private Object proceed(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object[] args = proceedingJoinPoint.getArgs(); String format = getProceedingJoinPointJsonInfoExcludeJsonException(proceedingJoinPoint); if (log.isInfoEnabled()) { log.info("begin proceed ,ProceedingJoinPoint info:[{}],Thread info:{}", format, JsonUtil.format(ThreadUtil.getCurrentThreadMapForLog())); } Date beginDate = new Date(); //*********************************************************** Object returnValue = proceedingJoinPoint.proceed(args); //*********************************************************** Date endDate = new Date(); if (log.isInfoEnabled()) { log.info("end proceed:[{}],thread info:[{}],time:{},return:[{}]", format, JsonUtil.format(ThreadUtil.getCurrentThreadMapForLog()), DateExtensionUtil.getIntervalForView(beginDate, endDate), returnValue); } return returnValue; } /** * ??,request??json ?, ???, . * * @param proceedingJoinPoint * the proceeding join point * @return the proceeding join point json info exclude json exception * @since 1.1.1 */ private String getProceedingJoinPointJsonInfoExcludeJsonException(ProceedingJoinPoint proceedingJoinPoint) { String format = ""; try { format = JsonUtil.format(ProceedingJoinPointUtil.getMapForLog(proceedingJoinPoint)); } catch (JSONException e) { format = e.getMessage(); log.error("", e); } return format; } }