From 9a795a3c45179aeb8effe8e4ad767f684de986ef Mon Sep 17 00:00:00 2001 From: Wu Date: Fri, 28 Feb 2020 18:57:35 +0800 Subject: [PATCH 01/10] fix duplicate proxy of bean. --- .../SeataDataSourceBeanPostProcessor.java | 74 ++++++++++++++++++- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java index f2dfe1ce529..ad9df9a5f00 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java @@ -16,9 +16,11 @@ package io.seata.spring.annotation.datasource; import javax.sql.DataSource; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.stream.Stream; import io.seata.common.exception.ShouldNeverHappenException; import io.seata.rm.datasource.DataSourceProxy; @@ -27,9 +29,11 @@ import net.sf.cglib.proxy.MethodInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.aop.support.AopUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.util.ClassUtils; /** * @author xingfudeshi@gmail.com @@ -45,11 +49,15 @@ public SeataDataSourceBeanPostProcessor(boolean useJdkProxy) { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof DataSource && !(bean instanceof DataSourceProxy)) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Auto proxy of [{}]", beanName); + try { + if (bean instanceof DataSource && !isAutoProxiedBySeata(bean)) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Auto proxy of [{}]", beanName); + } + return proxyDataSource(bean); } - return proxyDataSource(bean); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); } return bean; } @@ -105,4 +113,62 @@ private Object handleMethodProxy(DataSourceProxy dataSourceProxy, Method method, } } } + + /** + * is auto proxied by seata + * + * @param bean + * @return true, if this bean has been auto-proxied by seata + */ + private boolean isAutoProxiedBySeata(Object bean) throws NoSuchFieldException, IllegalAccessException { + if (bean instanceof DataSourceProxy) { + return true; + } + //handle Spring AOP + Object proxyTargetObject = bean; + if (AopUtils.isAopProxy(proxyTargetObject)) { + try { + proxyTargetObject = SpringProxyUtils.getAdvisedSupport(bean).getTargetSource().getTarget(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + Field field = null; + //handle Normal proxy object + if (ClassUtils.isCglibProxy(proxyTargetObject)) { + //CGLIB Proxy + field = proxyTargetObject.getClass().getDeclaredField("CGLIB$CALLBACK_0"); + } else if (Proxy.isProxyClass(proxyTargetObject.getClass())) { + //JDK Proxy + field = proxyTargetObject.getClass().getDeclaredField("h"); + } + return doCheckAutoProxy(field, proxyTargetObject); + } + + /** + * do check auto proxy + * + * @param field + * @param proxiedObject + * @return + * @throws IllegalAccessException + */ + private boolean doCheckAutoProxy(Field field, Object proxiedObject) throws IllegalAccessException { + if (null == field) { + return false; + } + field.setAccessible(true); + Object filedObject = field.get(proxiedObject); + return Stream.of(filedObject.getClass().getDeclaredFields()).anyMatch(f -> { + f.setAccessible(true); + Object targetObject; + try { + targetObject = f.get(filedObject); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + return targetObject instanceof DataSourceProxy; + }); + } + } From 99e4f386a84c3ac7f6fe1bcf7709bccf2b8b59fe Mon Sep 17 00:00:00 2001 From: xingfudeshi Date: Fri, 28 Feb 2020 19:14:08 +0800 Subject: [PATCH 02/10] Update SeataDataSourceBeanPostProcessor.java --- .../datasource/SeataDataSourceBeanPostProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java index ad9df9a5f00..d50a0ee699e 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java @@ -158,12 +158,12 @@ private boolean doCheckAutoProxy(Field field, Object proxiedObject) throws Illeg return false; } field.setAccessible(true); - Object filedObject = field.get(proxiedObject); - return Stream.of(filedObject.getClass().getDeclaredFields()).anyMatch(f -> { + Object fieldObject = field.get(proxiedObject); + return Stream.of(fieldObject.getClass().getDeclaredFields()).anyMatch(f -> { f.setAccessible(true); Object targetObject; try { - targetObject = f.get(filedObject); + targetObject = f.get(fieldObject); } catch (IllegalAccessException e) { throw new RuntimeException(e); } From e31f5a69237bacd98c0f1928e3286a254e15b3c2 Mon Sep 17 00:00:00 2001 From: Wu Date: Fri, 6 Mar 2020 09:46:06 +0800 Subject: [PATCH 03/10] fix review --- .../SeataDataSourceBeanPostProcessor.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java index d50a0ee699e..b1f6ec6a26b 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java @@ -140,7 +140,7 @@ private boolean isAutoProxiedBySeata(Object bean) throws NoSuchFieldException, I field = proxyTargetObject.getClass().getDeclaredField("CGLIB$CALLBACK_0"); } else if (Proxy.isProxyClass(proxyTargetObject.getClass())) { //JDK Proxy - field = proxyTargetObject.getClass().getDeclaredField("h"); + field = proxyTargetObject.getClass().getSuperclass().getDeclaredField("h"); } return doCheckAutoProxy(field, proxyTargetObject); } @@ -157,15 +157,24 @@ private boolean doCheckAutoProxy(Field field, Object proxiedObject) throws Illeg if (null == field) { return false; } - field.setAccessible(true); - Object fieldObject = field.get(proxiedObject); + Object fieldObject; + boolean fieldAccessible = field.isAccessible(); + try { + field.setAccessible(true); + fieldObject = field.get(proxiedObject); + } finally { + field.setAccessible(fieldAccessible); + } return Stream.of(fieldObject.getClass().getDeclaredFields()).anyMatch(f -> { + boolean accessible = f.isAccessible(); f.setAccessible(true); Object targetObject; try { targetObject = f.get(fieldObject); } catch (IllegalAccessException e) { throw new RuntimeException(e); + } finally { + f.setAccessible(accessible); } return targetObject instanceof DataSourceProxy; }); From 20473c347c34b386bd7a2473bc5f920a59f2a357 Mon Sep 17 00:00:00 2001 From: Wu Date: Fri, 13 Mar 2020 14:08:43 +0800 Subject: [PATCH 04/10] implements PriorityOrdered --- .../datasource/SeataDataSourceBeanPostProcessor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java index b1f6ec6a26b..02dfb78a762 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java @@ -33,13 +33,15 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; import org.springframework.util.ClassUtils; /** * @author xingfudeshi@gmail.com * The type seata data source bean post processor */ -public class SeataDataSourceBeanPostProcessor implements BeanPostProcessor { +public class SeataDataSourceBeanPostProcessor implements BeanPostProcessor, PriorityOrdered { private static final Logger LOGGER = LoggerFactory.getLogger(SeataDataSourceBeanPostProcessor.class); private final boolean useJdkProxy; @@ -180,4 +182,8 @@ private boolean doCheckAutoProxy(Field field, Object proxiedObject) throws Illeg }); } + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } } From 01a16aabedd4c137840b0fa97d1877780865d4c4 Mon Sep 17 00:00:00 2001 From: Wu Date: Fri, 13 Mar 2020 16:50:20 +0800 Subject: [PATCH 05/10] use Ordered instead of PriorityOrdered --- .../datasource/SeataDataSourceBeanPostProcessor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java index 02dfb78a762..3e2c49bedd0 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java @@ -34,14 +34,13 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered; -import org.springframework.core.PriorityOrdered; import org.springframework.util.ClassUtils; /** * @author xingfudeshi@gmail.com * The type seata data source bean post processor */ -public class SeataDataSourceBeanPostProcessor implements BeanPostProcessor, PriorityOrdered { +public class SeataDataSourceBeanPostProcessor implements BeanPostProcessor, Ordered { private static final Logger LOGGER = LoggerFactory.getLogger(SeataDataSourceBeanPostProcessor.class); private final boolean useJdkProxy; From a9a844ee59cec5aec6b687dd599ca7e8d74c2792 Mon Sep 17 00:00:00 2001 From: Wu Date: Sun, 15 Mar 2020 12:16:46 +0800 Subject: [PATCH 06/10] using spring to create automatic proxy --- .../autoconfigure/SeataAutoConfiguration.java | 12 +- .../AutoDataSourceProxyRegistrar.java | 10 +- .../SeataAutoDataSourceProxyAdvice.java | 46 +++++ .../SeataAutoDataSourceProxyCreator.java | 90 +++++++++ .../SeataDataSourceBeanPostProcessor.java | 188 ------------------ 5 files changed, 147 insertions(+), 199 deletions(-) create mode 100644 spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java create mode 100644 spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java delete mode 100644 spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java diff --git a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java index 9a2b416a0ab..cae8909382f 100644 --- a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java +++ b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java @@ -16,7 +16,7 @@ package io.seata.spring.boot.autoconfigure; import io.seata.spring.annotation.GlobalTransactionScanner; -import io.seata.spring.annotation.datasource.SeataDataSourceBeanPostProcessor; +import io.seata.spring.annotation.datasource.SeataAutoDataSourceProxyCreator; import io.seata.spring.boot.autoconfigure.properties.SeataProperties; import io.seata.spring.boot.autoconfigure.provider.SpringApplicationContextProvider; import org.slf4j.Logger; @@ -30,7 +30,7 @@ import org.springframework.context.annotation.DependsOn; import static io.seata.common.Constants.BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER; -import static io.seata.spring.annotation.datasource.AutoDataSourceProxyRegistrar.BEAN_NAME_SEATA_DATA_SOURCE_BEAN_POST_PROCESSOR; +import static io.seata.spring.annotation.datasource.AutoDataSourceProxyRegistrar.BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR; /** * @author xingfudeshi@gmail.com @@ -58,10 +58,10 @@ public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataPr return new GlobalTransactionScanner(seataProperties.getApplicationId(), seataProperties.getTxServiceGroup()); } - @Bean(BEAN_NAME_SEATA_DATA_SOURCE_BEAN_POST_PROCESSOR) + @Bean(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR) @ConditionalOnProperty(prefix = StarterConstants.SEATA_PREFIX, name = {"enableAutoDataSourceProxy", "enable-auto-data-source-proxy"}, havingValue = "true", matchIfMissing = true) - @ConditionalOnMissingBean(SeataDataSourceBeanPostProcessor.class) - public SeataDataSourceBeanPostProcessor seataDataSourceBeanPostProcessor(SeataProperties seataProperties) { - return new SeataDataSourceBeanPostProcessor(seataProperties.isUseJdkProxy()); + @ConditionalOnMissingBean(SeataAutoDataSourceProxyCreator.class) + public SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) { + return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy()); } } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java b/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java index 62a19cd5066..193787ea4c9 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java @@ -27,16 +27,16 @@ */ public class AutoDataSourceProxyRegistrar implements ImportBeanDefinitionRegistrar { private static final String ATTRIBUTE_KEY_USE_JDK_PROXY = "useJdkProxy"; - public static final String BEAN_NAME_SEATA_DATA_SOURCE_BEAN_POST_PROCESSOR = "seataDataSourceBeanPostProcessor"; + public static final String BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR = "seataAutoDataSourceProxyCreator"; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { - if (!registry.containsBeanDefinition(BEAN_NAME_SEATA_DATA_SOURCE_BEAN_POST_PROCESSOR)) { - boolean useJdkProxy = Boolean.valueOf(importingClassMetadata.getAnnotationAttributes(EnableAutoDataSourceProxy.class.getName()).get(ATTRIBUTE_KEY_USE_JDK_PROXY).toString()); + if (!registry.containsBeanDefinition(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR)) { + boolean useJdkProxy = Boolean.parseBoolean(importingClassMetadata.getAnnotationAttributes(EnableAutoDataSourceProxy.class.getName()).get(ATTRIBUTE_KEY_USE_JDK_PROXY).toString()); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder - .genericBeanDefinition(SeataDataSourceBeanPostProcessor.class) + .genericBeanDefinition(SeataAutoDataSourceProxyCreator.class) .addConstructorArgValue(useJdkProxy).getBeanDefinition(); - registry.registerBeanDefinition(BEAN_NAME_SEATA_DATA_SOURCE_BEAN_POST_PROCESSOR, beanDefinition); + registry.registerBeanDefinition(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR, beanDefinition); } } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java new file mode 100644 index 00000000000..5a2d8b0c2ca --- /dev/null +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java @@ -0,0 +1,46 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.spring.annotation.datasource; + +import java.lang.reflect.Method; + +import io.seata.rm.datasource.DataSourceProxy; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.beans.BeanUtils; + +/** + * @author xingfudeshi@gmail.com + */ +public class SeataAutoDataSourceProxyAdvice implements MethodInterceptor { + private final DataSourceProxy dataSourceProxy; + + public SeataAutoDataSourceProxyAdvice(DataSourceProxy dataSourceProxy) { + this.dataSourceProxy = dataSourceProxy; + } + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Method method = invocation.getMethod(); + Object[] args = invocation.getArguments(); + Method m = BeanUtils.findDeclaredMethod(DataSourceProxy.class, method.getName(), method.getParameterTypes()); + if (null != m) { + return m.invoke(dataSourceProxy, args); + } else { + return invocation.proceed(); + } + } +} diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java new file mode 100644 index 00000000000..0240104362e --- /dev/null +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java @@ -0,0 +1,90 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.spring.annotation.datasource; + +import javax.sql.DataSource; + +import io.seata.rm.datasource.DataSourceProxy; +import io.seata.spring.util.SpringProxyUtils; +import org.aopalliance.intercept.MethodInterceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.Advisor; +import org.springframework.aop.TargetSource; +import org.springframework.aop.framework.AdvisedSupport; +import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.BeansException; + +/** + * @author xingfudeshi@gmail.com + */ +public class SeataAutoDataSourceProxyCreator extends AbstractAutoProxyCreator { + private static final Logger LOGGER = LoggerFactory.getLogger(SeataAutoDataSourceProxyCreator.class); + private MethodInterceptor advice; + private final boolean useJdkProxy; + + public SeataAutoDataSourceProxyCreator(boolean useJdkProxy) { + this.useJdkProxy = useJdkProxy; + setProxyTargetClass(!this.useJdkProxy); + } + + @Override + protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { + if (bean instanceof DataSource && !isAutoProxiedBySeata(bean)) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Auto proxy of [{}]", beanName); + } + DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) bean); + advice = new SeataAutoDataSourceProxyAdvice(dataSourceProxy); + boolean isProxied = AopUtils.isAopProxy(bean); + if (isProxied) { + try { + AdvisedSupport advisedSupport = SpringProxyUtils.getAdvisedSupport(bean); + advisedSupport.addAdvice(advice); + Advisor[] advisors = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); + for (Advisor advisor : advisors) { + advisedSupport.addAdvisor(0, advisor); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + } else { + bean = super.wrapIfNecessary(bean, beanName, cacheKey); + } + } + return bean; + } + + private boolean isAutoProxiedBySeata(Object bean) { + boolean isProxied = AopUtils.isAopProxy(bean); + if (isProxied) { + try { + AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); + return advised.countAdvicesOfType(SeataAutoDataSourceProxyAdvice.class) > 0; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return false; + } + + @Override + protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource customTargetSource) throws BeansException { + return new Object[]{advice}; + } +} diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java deleted file mode 100644 index 3e2c49bedd0..00000000000 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataDataSourceBeanPostProcessor.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 1999-2019 Seata.io Group. - * - * 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 io.seata.spring.annotation.datasource; - -import javax.sql.DataSource; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.stream.Stream; - -import io.seata.common.exception.ShouldNeverHappenException; -import io.seata.rm.datasource.DataSourceProxy; -import io.seata.spring.util.SpringProxyUtils; -import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.MethodInterceptor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.aop.support.AopUtils; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.core.Ordered; -import org.springframework.util.ClassUtils; - -/** - * @author xingfudeshi@gmail.com - * The type seata data source bean post processor - */ -public class SeataDataSourceBeanPostProcessor implements BeanPostProcessor, Ordered { - private static final Logger LOGGER = LoggerFactory.getLogger(SeataDataSourceBeanPostProcessor.class); - private final boolean useJdkProxy; - - public SeataDataSourceBeanPostProcessor(boolean useJdkProxy) { - this.useJdkProxy = useJdkProxy; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - try { - if (bean instanceof DataSource && !isAutoProxiedBySeata(bean)) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Auto proxy of [{}]", beanName); - } - return proxyDataSource(bean); - } - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException(e); - } - return bean; - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) { - if (bean instanceof DataSourceProxy) { - throw new ShouldNeverHappenException("Auto proxy of DataSource can't be enabled as you've created a DataSourceProxy bean." + - "Please consider removing DataSourceProxy bean or disabling auto proxy of DataSource."); - } - return bean; - } - - /** - * proxy data source - * - * @param originBean - * @return proxied datasource - */ - private Object proxyDataSource(Object originBean) { - DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) originBean); - if (this.useJdkProxy) { - return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), SpringProxyUtils.getAllInterfaces(originBean), (proxy, method, args) -> handleMethodProxy(dataSourceProxy, method, args, originBean)); - } else { - return Enhancer.create(originBean.getClass(), (MethodInterceptor) (proxy, method, args, methodProxy) -> handleMethodProxy(dataSourceProxy, method, args, originBean)); - } - - } - - /** - * handle method proxy - * - * @param dataSourceProxy - * @param method - * @param args - * @param originBean - * @return proxied datasource - * @throws InvocationTargetException - * @throws IllegalAccessException - */ - private Object handleMethodProxy(DataSourceProxy dataSourceProxy, Method method, Object[] args, Object originBean) throws InvocationTargetException, IllegalAccessException { - Method m = BeanUtils.findDeclaredMethod(DataSourceProxy.class, method.getName(), method.getParameterTypes()); - if (null != m) { - return m.invoke(dataSourceProxy, args); - } else { - boolean oldAccessible = method.isAccessible(); - try { - method.setAccessible(true); - return method.invoke(originBean, args); - } finally { - //recover the original accessible for security reason - method.setAccessible(oldAccessible); - } - } - } - - /** - * is auto proxied by seata - * - * @param bean - * @return true, if this bean has been auto-proxied by seata - */ - private boolean isAutoProxiedBySeata(Object bean) throws NoSuchFieldException, IllegalAccessException { - if (bean instanceof DataSourceProxy) { - return true; - } - //handle Spring AOP - Object proxyTargetObject = bean; - if (AopUtils.isAopProxy(proxyTargetObject)) { - try { - proxyTargetObject = SpringProxyUtils.getAdvisedSupport(bean).getTargetSource().getTarget(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - Field field = null; - //handle Normal proxy object - if (ClassUtils.isCglibProxy(proxyTargetObject)) { - //CGLIB Proxy - field = proxyTargetObject.getClass().getDeclaredField("CGLIB$CALLBACK_0"); - } else if (Proxy.isProxyClass(proxyTargetObject.getClass())) { - //JDK Proxy - field = proxyTargetObject.getClass().getSuperclass().getDeclaredField("h"); - } - return doCheckAutoProxy(field, proxyTargetObject); - } - - /** - * do check auto proxy - * - * @param field - * @param proxiedObject - * @return - * @throws IllegalAccessException - */ - private boolean doCheckAutoProxy(Field field, Object proxiedObject) throws IllegalAccessException { - if (null == field) { - return false; - } - Object fieldObject; - boolean fieldAccessible = field.isAccessible(); - try { - field.setAccessible(true); - fieldObject = field.get(proxiedObject); - } finally { - field.setAccessible(fieldAccessible); - } - return Stream.of(fieldObject.getClass().getDeclaredFields()).anyMatch(f -> { - boolean accessible = f.isAccessible(); - f.setAccessible(true); - Object targetObject; - try { - targetObject = f.get(fieldObject); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } finally { - f.setAccessible(accessible); - } - return targetObject instanceof DataSourceProxy; - }); - } - - @Override - public int getOrder() { - return Ordered.HIGHEST_PRECEDENCE; - } -} From ae3d7563c9b8d1598a440782e9fb8fae3bad5c10 Mon Sep 17 00:00:00 2001 From: Wu Date: Sun, 15 Mar 2020 19:04:59 +0800 Subject: [PATCH 07/10] using spring to create automatic proxy --- .../SeataAutoDataSourceProxyCreator.java | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java index 0240104362e..a24cc22b30a 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java @@ -44,43 +44,45 @@ public SeataAutoDataSourceProxyCreator(boolean useJdkProxy) { @Override protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { - if (bean instanceof DataSource && !isAutoProxiedBySeata(bean)) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Auto proxy of [{}]", beanName); - } - DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) bean); - advice = new SeataAutoDataSourceProxyAdvice(dataSourceProxy); + if (bean instanceof DataSource) { boolean isProxied = AopUtils.isAopProxy(bean); - if (isProxied) { - try { - AdvisedSupport advisedSupport = SpringProxyUtils.getAdvisedSupport(bean); - advisedSupport.addAdvice(advice); - Advisor[] advisors = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); - for (Advisor advisor : advisors) { - advisedSupport.addAdvisor(0, advisor); + if (!isProxied || !isAutoProxiedBySeata(bean)) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Auto proxy of [{}]", beanName); + } + DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) bean); + advice = new SeataAutoDataSourceProxyAdvice(dataSourceProxy); + if (isProxied) { + try { + AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); + Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); + for (Advisor avr : advisor) { + advised.addAdvisor(0, avr); + } + } catch (Exception e) { + throw new RuntimeException(e); } - } catch (Exception e) { - throw new RuntimeException(e); + } else { + bean = super.wrapIfNecessary(bean, beanName, cacheKey); } - - } else { - bean = super.wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } + /** + * Whether this bean has been proxied by Seata + * + * @param bean + * @return true if this bean has been proxied by Seata + */ private boolean isAutoProxiedBySeata(Object bean) { - boolean isProxied = AopUtils.isAopProxy(bean); - if (isProxied) { - try { - AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); - return advised.countAdvicesOfType(SeataAutoDataSourceProxyAdvice.class) > 0; - } catch (Exception e) { - throw new RuntimeException(e); - } + try { + AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); + return advised.countAdvicesOfType(SeataAutoDataSourceProxyAdvice.class) > 0; + } catch (Exception e) { + throw new RuntimeException(e); } - return false; } @Override From 9672d295df60d8694c77b77eeb280fdba91cb300 Mon Sep 17 00:00:00 2001 From: Wu Date: Sun, 15 Mar 2020 19:48:49 +0800 Subject: [PATCH 08/10] add exclude --- script/client/spring/application.properties | 1 + script/client/spring/application.yml | 3 ++- .../boot/autoconfigure/SeataAutoConfiguration.java | 2 +- .../autoconfigure/properties/SeataProperties.java | 13 +++++++++++++ .../datasource/AutoDataSourceProxyRegistrar.java | 6 +++++- .../datasource/EnableAutoDataSourceProxy.java | 6 ++++++ .../datasource/SeataAutoDataSourceProxyCreator.java | 12 +++++++----- 7 files changed, 35 insertions(+), 8 deletions(-) diff --git a/script/client/spring/application.properties b/script/client/spring/application.properties index 37d2e5e3dbf..49eec2d3b82 100755 --- a/script/client/spring/application.properties +++ b/script/client/spring/application.properties @@ -15,6 +15,7 @@ # seata.enabled=true +seata.exclude-for-auto-proxying=firstClassNameForExclude,secondClassNameForExclude seata.application-id=applicationName seata.tx-service-group=my_test_tx_group seata.enable-auto-data-source-proxy=true diff --git a/script/client/spring/application.yml b/script/client/spring/application.yml index f2b99361eca..2d37935e286 100755 --- a/script/client/spring/application.yml +++ b/script/client/spring/application.yml @@ -4,6 +4,7 @@ seata: tx-service-group: my_test_tx_group enable-auto-data-source-proxy: true use-jdk-proxy: false + exclude-for-auto-proxying: firstClassNameForExclude,secondClassNameForExclude client: rm: async-commit-buffer-limit: 1000 @@ -97,4 +98,4 @@ seata: session-timeout: 6000 connect-timeout: 2000 username: "" - password: "" \ No newline at end of file + password: "" diff --git a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java index cae8909382f..c037fc7e6c2 100644 --- a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java +++ b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java @@ -62,6 +62,6 @@ public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataPr @ConditionalOnProperty(prefix = StarterConstants.SEATA_PREFIX, name = {"enableAutoDataSourceProxy", "enable-auto-data-source-proxy"}, havingValue = "true", matchIfMissing = true) @ConditionalOnMissingBean(SeataAutoDataSourceProxyCreator.class) public SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) { - return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy()); + return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(),seataProperties.getExcludeForAutoProxying()); } } diff --git a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/SeataProperties.java b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/SeataProperties.java index fd7d2c30eda..ba0481671c5 100644 --- a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/SeataProperties.java +++ b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/SeataProperties.java @@ -47,6 +47,10 @@ public class SeataProperties { * Whether use JDK proxy instead of CGLIB proxy */ private boolean useJdkProxy = false; + /** + * Specifies which datasource bean are not eligible for auto-proxying + */ + private String[] excludeForAutoProxying = {}; @Autowired private SpringCloudAlibabaConfiguration springCloudAlibabaConfiguration; @@ -101,4 +105,13 @@ public SeataProperties setUseJdkProxy(boolean useJdkProxy) { this.useJdkProxy = useJdkProxy; return this; } + + public String[] getExcludeForAutoProxying() { + return excludeForAutoProxying; + } + + public SeataProperties setExcludeForAutoProxying(String[] excludeForAutoProxying) { + this.excludeForAutoProxying = excludeForAutoProxying; + return this; + } } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java b/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java index 193787ea4c9..426189bfc3b 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java @@ -27,15 +27,19 @@ */ public class AutoDataSourceProxyRegistrar implements ImportBeanDefinitionRegistrar { private static final String ATTRIBUTE_KEY_USE_JDK_PROXY = "useJdkProxy"; + private static final String ATTRIBUTE_KEY_EXCLUDE = "exclude"; public static final String BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR = "seataAutoDataSourceProxyCreator"; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR)) { boolean useJdkProxy = Boolean.parseBoolean(importingClassMetadata.getAnnotationAttributes(EnableAutoDataSourceProxy.class.getName()).get(ATTRIBUTE_KEY_USE_JDK_PROXY).toString()); + String[] exclude = (String[]) importingClassMetadata.getAnnotationAttributes(EnableAutoDataSourceProxy.class.getName()).get(ATTRIBUTE_KEY_EXCLUDE); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(SeataAutoDataSourceProxyCreator.class) - .addConstructorArgValue(useJdkProxy).getBeanDefinition(); + .addConstructorArgValue(useJdkProxy) + .addConstructorArgValue(exclude) + .getBeanDefinition(); registry.registerBeanDefinition(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR, beanDefinition); } } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java b/spring/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java index 61b14e7237f..2b1d9b12126 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java @@ -38,4 +38,10 @@ * @return useJdkProxy */ boolean useJdkProxy() default false; + + /** + * Specifies which datasource bean are not eligible for auto-proxying + * @return + */ + String[] exclude() default {}; } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java index a24cc22b30a..e368c30457b 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java @@ -16,6 +16,7 @@ package io.seata.spring.annotation.datasource; import javax.sql.DataSource; +import java.util.stream.Stream; import io.seata.rm.datasource.DataSourceProxy; import io.seata.spring.util.SpringProxyUtils; @@ -35,16 +36,17 @@ public class SeataAutoDataSourceProxyCreator extends AbstractAutoProxyCreator { private static final Logger LOGGER = LoggerFactory.getLogger(SeataAutoDataSourceProxyCreator.class); private MethodInterceptor advice; - private final boolean useJdkProxy; + private final String[] exclude; - public SeataAutoDataSourceProxyCreator(boolean useJdkProxy) { - this.useJdkProxy = useJdkProxy; - setProxyTargetClass(!this.useJdkProxy); + public SeataAutoDataSourceProxyCreator(boolean useJdkProxy, String[] exclude) { + this.exclude = exclude; + setProxyTargetClass(!useJdkProxy); } @Override protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { - if (bean instanceof DataSource) { + String className = bean.getClass().getName(); + if (bean instanceof DataSource && Stream.of(exclude).noneMatch(s -> s.equals(className))) { boolean isProxied = AopUtils.isAopProxy(bean); if (!isProxied || !isAutoProxiedBySeata(bean)) { if (LOGGER.isInfoEnabled()) { From acdbdfce486f64817a8fcd0dc5537d30e9a00c8f Mon Sep 17 00:00:00 2001 From: Wu Date: Thu, 19 Mar 2020 22:13:10 +0800 Subject: [PATCH 09/10] use Spring AOP to create auto-proxy --- script/client/spring/application.properties | 2 +- script/client/spring/application.yml | 2 +- .../autoconfigure/SeataAutoConfiguration.java | 2 +- .../properties/SeataProperties.java | 10 +-- .../datasource/EnableAutoDataSourceProxy.java | 3 +- .../SeataAutoDataSourceProxyAdvice.java | 9 ++- .../SeataAutoDataSourceProxyCreator.java | 68 ++++++++----------- .../annotation/datasource/SeataProxy.java | 22 ++++++ 8 files changed, 69 insertions(+), 49 deletions(-) create mode 100644 spring/src/main/java/io/seata/spring/annotation/datasource/SeataProxy.java diff --git a/script/client/spring/application.properties b/script/client/spring/application.properties index 49eec2d3b82..743827f6c87 100755 --- a/script/client/spring/application.properties +++ b/script/client/spring/application.properties @@ -15,7 +15,7 @@ # seata.enabled=true -seata.exclude-for-auto-proxying=firstClassNameForExclude,secondClassNameForExclude +seata.excludes-for-auto-proxying=firstClassNameForExclude,secondClassNameForExclude seata.application-id=applicationName seata.tx-service-group=my_test_tx_group seata.enable-auto-data-source-proxy=true diff --git a/script/client/spring/application.yml b/script/client/spring/application.yml index 2d37935e286..a1123274d9c 100755 --- a/script/client/spring/application.yml +++ b/script/client/spring/application.yml @@ -4,7 +4,7 @@ seata: tx-service-group: my_test_tx_group enable-auto-data-source-proxy: true use-jdk-proxy: false - exclude-for-auto-proxying: firstClassNameForExclude,secondClassNameForExclude + excludes-for-auto-proxying: firstClassNameForExclude,secondClassNameForExclude client: rm: async-commit-buffer-limit: 1000 diff --git a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java index c037fc7e6c2..6cc03d20133 100644 --- a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java +++ b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/SeataAutoConfiguration.java @@ -62,6 +62,6 @@ public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataPr @ConditionalOnProperty(prefix = StarterConstants.SEATA_PREFIX, name = {"enableAutoDataSourceProxy", "enable-auto-data-source-proxy"}, havingValue = "true", matchIfMissing = true) @ConditionalOnMissingBean(SeataAutoDataSourceProxyCreator.class) public SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) { - return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(),seataProperties.getExcludeForAutoProxying()); + return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(),seataProperties.getExcludesForAutoProxying()); } } diff --git a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/SeataProperties.java b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/SeataProperties.java index ba0481671c5..f63ca6125b1 100644 --- a/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/SeataProperties.java +++ b/seata-spring-boot-starter/src/main/java/io/seata/spring/boot/autoconfigure/properties/SeataProperties.java @@ -50,7 +50,7 @@ public class SeataProperties { /** * Specifies which datasource bean are not eligible for auto-proxying */ - private String[] excludeForAutoProxying = {}; + private String[] excludesForAutoProxying = {}; @Autowired private SpringCloudAlibabaConfiguration springCloudAlibabaConfiguration; @@ -106,12 +106,12 @@ public SeataProperties setUseJdkProxy(boolean useJdkProxy) { return this; } - public String[] getExcludeForAutoProxying() { - return excludeForAutoProxying; + public String[] getExcludesForAutoProxying() { + return excludesForAutoProxying; } - public SeataProperties setExcludeForAutoProxying(String[] excludeForAutoProxying) { - this.excludeForAutoProxying = excludeForAutoProxying; + public SeataProperties setExcludesForAutoProxying(String[] excludesForAutoProxying) { + this.excludesForAutoProxying = excludesForAutoProxying; return this; } } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java b/spring/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java index 2b1d9b12126..280968fb38f 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/EnableAutoDataSourceProxy.java @@ -41,7 +41,8 @@ /** * Specifies which datasource bean are not eligible for auto-proxying + * * @return */ - String[] exclude() default {}; + String[] excludes() default {}; } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java index 5a2d8b0c2ca..04b8a5f84f7 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java @@ -20,12 +20,13 @@ import io.seata.rm.datasource.DataSourceProxy; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.IntroductionInfo; import org.springframework.beans.BeanUtils; /** * @author xingfudeshi@gmail.com */ -public class SeataAutoDataSourceProxyAdvice implements MethodInterceptor { +public class SeataAutoDataSourceProxyAdvice implements MethodInterceptor, IntroductionInfo { private final DataSourceProxy dataSourceProxy; public SeataAutoDataSourceProxyAdvice(DataSourceProxy dataSourceProxy) { @@ -43,4 +44,10 @@ public Object invoke(MethodInvocation invocation) throws Throwable { return invocation.proceed(); } } + + @Override + public Class[] getInterfaces() { + return new Class[]{SeataProxy.class}; + } + } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java index e368c30457b..db493d6882c 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java @@ -16,7 +16,7 @@ package io.seata.spring.annotation.datasource; import javax.sql.DataSource; -import java.util.stream.Stream; +import java.util.Arrays; import io.seata.rm.datasource.DataSourceProxy; import io.seata.spring.util.SpringProxyUtils; @@ -28,6 +28,7 @@ import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; import org.springframework.aop.support.AopUtils; +import org.springframework.aop.support.DefaultIntroductionAdvisor; import org.springframework.beans.BeansException; /** @@ -36,59 +37,48 @@ public class SeataAutoDataSourceProxyCreator extends AbstractAutoProxyCreator { private static final Logger LOGGER = LoggerFactory.getLogger(SeataAutoDataSourceProxyCreator.class); private MethodInterceptor advice; - private final String[] exclude; + private final String[] excludes; - public SeataAutoDataSourceProxyCreator(boolean useJdkProxy, String[] exclude) { - this.exclude = exclude; + public SeataAutoDataSourceProxyCreator(boolean useJdkProxy, String[] excludes) { + this.excludes = excludes; setProxyTargetClass(!useJdkProxy); } @Override protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { - String className = bean.getClass().getName(); - if (bean instanceof DataSource && Stream.of(exclude).noneMatch(s -> s.equals(className))) { - boolean isProxied = AopUtils.isAopProxy(bean); - if (!isProxied || !isAutoProxiedBySeata(bean)) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Auto proxy of [{}]", beanName); - } - DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) bean); - advice = new SeataAutoDataSourceProxyAdvice(dataSourceProxy); - if (isProxied) { - try { - AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); - Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); - for (Advisor avr : advisor) { - advised.addAdvisor(0, avr); - } - } catch (Exception e) { - throw new RuntimeException(e); + if (!shouldSkip(bean.getClass(), beanName)) { + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Auto proxy of [{}]", beanName); + } + DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) bean); + advice = new SeataAutoDataSourceProxyAdvice(dataSourceProxy); + if (AopUtils.isAopProxy(bean)) { + try { + AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); + Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); + for (Advisor avr : advisor) { + advised.addAdvisor(0, avr); } - } else { - bean = super.wrapIfNecessary(bean, beanName, cacheKey); + } catch (Exception e) { + throw new RuntimeException(e); } + } else { + bean = super.wrapIfNecessary(bean, beanName, cacheKey); } } + return bean; } - /** - * Whether this bean has been proxied by Seata - * - * @param bean - * @return true if this bean has been proxied by Seata - */ - private boolean isAutoProxiedBySeata(Object bean) { - try { - AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); - return advised.countAdvicesOfType(SeataAutoDataSourceProxyAdvice.class) > 0; - } catch (Exception e) { - throw new RuntimeException(e); - } + @Override + protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource customTargetSource) throws BeansException { + return new Object[]{new DefaultIntroductionAdvisor(advice)}; } @Override - protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource customTargetSource) throws BeansException { - return new Object[]{advice}; + protected boolean shouldSkip(Class beanClass, String beanName) { + return SeataProxy.class.isAssignableFrom(beanClass) || + !DataSource.class.isAssignableFrom(beanClass) || + Arrays.asList(excludes).contains(beanClass.getName()); } } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataProxy.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataProxy.java new file mode 100644 index 00000000000..39622d0f610 --- /dev/null +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataProxy.java @@ -0,0 +1,22 @@ +/* + * Copyright 1999-2019 Seata.io Group. + * + * 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 io.seata.spring.annotation.datasource; + +/** + * @author xingfudeshi@gmail.com + */ +public interface SeataProxy { +} From c46c6d62c64a53cfaf1a568b8c00a724c424389b Mon Sep 17 00:00:00 2001 From: Wu Date: Fri, 20 Mar 2020 22:39:26 +0800 Subject: [PATCH 10/10] remove wrapIfNecessary --- .../AutoDataSourceProxyRegistrar.java | 6 +-- .../SeataAutoDataSourceProxyAdvice.java | 7 +--- .../SeataAutoDataSourceProxyCreator.java | 38 +++---------------- 3 files changed, 10 insertions(+), 41 deletions(-) diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java b/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java index 426189bfc3b..e86b02097c4 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/AutoDataSourceProxyRegistrar.java @@ -27,18 +27,18 @@ */ public class AutoDataSourceProxyRegistrar implements ImportBeanDefinitionRegistrar { private static final String ATTRIBUTE_KEY_USE_JDK_PROXY = "useJdkProxy"; - private static final String ATTRIBUTE_KEY_EXCLUDE = "exclude"; + private static final String ATTRIBUTE_KEY_EXCLUDES = "excludes"; public static final String BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR = "seataAutoDataSourceProxyCreator"; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR)) { boolean useJdkProxy = Boolean.parseBoolean(importingClassMetadata.getAnnotationAttributes(EnableAutoDataSourceProxy.class.getName()).get(ATTRIBUTE_KEY_USE_JDK_PROXY).toString()); - String[] exclude = (String[]) importingClassMetadata.getAnnotationAttributes(EnableAutoDataSourceProxy.class.getName()).get(ATTRIBUTE_KEY_EXCLUDE); + String[] excludes = (String[]) importingClassMetadata.getAnnotationAttributes(EnableAutoDataSourceProxy.class.getName()).get(ATTRIBUTE_KEY_EXCLUDES); AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder .genericBeanDefinition(SeataAutoDataSourceProxyCreator.class) .addConstructorArgValue(useJdkProxy) - .addConstructorArgValue(exclude) + .addConstructorArgValue(excludes) .getBeanDefinition(); registry.registerBeanDefinition(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR, beanDefinition); } diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java index 04b8a5f84f7..3dcd9da4296 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyAdvice.java @@ -15,6 +15,7 @@ */ package io.seata.spring.annotation.datasource; +import javax.sql.DataSource; import java.lang.reflect.Method; import io.seata.rm.datasource.DataSourceProxy; @@ -27,14 +28,10 @@ * @author xingfudeshi@gmail.com */ public class SeataAutoDataSourceProxyAdvice implements MethodInterceptor, IntroductionInfo { - private final DataSourceProxy dataSourceProxy; - - public SeataAutoDataSourceProxyAdvice(DataSourceProxy dataSourceProxy) { - this.dataSourceProxy = dataSourceProxy; - } @Override public Object invoke(MethodInvocation invocation) throws Throwable { + DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) invocation.getThis()); Method method = invocation.getMethod(); Object[] args = invocation.getArguments(); Method m = BeanUtils.findDeclaredMethod(DataSourceProxy.class, method.getName(), method.getParameterTypes()); diff --git a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java index db493d6882c..d30874dbbce 100644 --- a/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java +++ b/spring/src/main/java/io/seata/spring/annotation/datasource/SeataAutoDataSourceProxyCreator.java @@ -18,16 +18,11 @@ import javax.sql.DataSource; import java.util.Arrays; -import io.seata.rm.datasource.DataSourceProxy; -import io.seata.spring.util.SpringProxyUtils; -import org.aopalliance.intercept.MethodInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.Advisor; import org.springframework.aop.TargetSource; -import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; -import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.DefaultIntroductionAdvisor; import org.springframework.beans.BeansException; @@ -36,43 +31,20 @@ */ public class SeataAutoDataSourceProxyCreator extends AbstractAutoProxyCreator { private static final Logger LOGGER = LoggerFactory.getLogger(SeataAutoDataSourceProxyCreator.class); - private MethodInterceptor advice; private final String[] excludes; + private final Advisor advisor = new DefaultIntroductionAdvisor(new SeataAutoDataSourceProxyAdvice()); public SeataAutoDataSourceProxyCreator(boolean useJdkProxy, String[] excludes) { this.excludes = excludes; setProxyTargetClass(!useJdkProxy); } - @Override - protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { - if (!shouldSkip(bean.getClass(), beanName)) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info("Auto proxy of [{}]", beanName); - } - DataSourceProxy dataSourceProxy = DataSourceProxyHolder.get().putDataSource((DataSource) bean); - advice = new SeataAutoDataSourceProxyAdvice(dataSourceProxy); - if (AopUtils.isAopProxy(bean)) { - try { - AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean); - Advisor[] advisor = buildAdvisors(beanName, getAdvicesAndAdvisorsForBean(null, null, null)); - for (Advisor avr : advisor) { - advised.addAdvisor(0, avr); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - } else { - bean = super.wrapIfNecessary(bean, beanName, cacheKey); - } - } - - return bean; - } - @Override protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource customTargetSource) throws BeansException { - return new Object[]{new DefaultIntroductionAdvisor(advice)}; + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Auto proxy of [{}]", beanName); + } + return new Object[]{advisor}; } @Override