diff --git a/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/annotation/TableId.java b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/annotation/TableId.java
new file mode 100644
index 000000000..c4c999c1d
--- /dev/null
+++ b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/annotation/TableId.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2011-2019, hubin (jobob@qq.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
+ *
+ * https://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.codingapi.txlcn.tc.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * @author dinghuang123@gmail.com
+ * @since 2019/7/29
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface TableId {
+
+ /**
+ * 字段值(驼峰命名方式,该值可无)
+ */
+ String value() default "";
+
+}
diff --git a/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/annotation/TableName.java b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/annotation/TableName.java
new file mode 100644
index 000000000..f11909f29
--- /dev/null
+++ b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/annotation/TableName.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011-2019, hubin (jobob@qq.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
+ *
+ * https://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.codingapi.txlcn.tc.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * @author dinghuang123@gmail.com
+ * @since 2019/7/29
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface TableName {
+
+ /**
+ * 实体对应的表名
+ */
+ String value() default "";
+}
diff --git a/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/PrimaryKeyListVisitor.java b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/PrimaryKeyListVisitor.java
index 620ec5f22..6821c1814 100644
--- a/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/PrimaryKeyListVisitor.java
+++ b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/PrimaryKeyListVisitor.java
@@ -76,8 +76,14 @@ private Map newKeyValues(List expressions) {
Map keyValues = new HashMap<>();
for (int i = 0; i < columns.size(); i++) {
columns.get(i).setTable(table);
+ //解决Long类型字段作为主键空指针异常
if (primaryKeys.contains(columns.get(i).getFullyQualifiedName())) {
- Object expression = expressions.get(i).getASTNode().jjtGetValue();
+ Object expression = null;
+ if (expressions.get(i).getASTNode() != null) {
+ expressions.get(i).getASTNode().jjtGetValue();
+ } else {
+ expression = expressions.get(i);
+ }
keyValues.put(columns.get(i).getFullyQualifiedName(),
Reflection.invokeN(expression.getClass(), "getValue", expression, new Object[0]));
}
diff --git a/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/PrimaryKeyListVisitorHandler.java b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/PrimaryKeyListVisitorHandler.java
new file mode 100644
index 000000000..95a192b7f
--- /dev/null
+++ b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/PrimaryKeyListVisitorHandler.java
@@ -0,0 +1,35 @@
+package com.codingapi.txlcn.tc.core.transaction.txc.analy;
+
+import com.codingapi.txlcn.tc.core.transaction.txc.analy.def.PrimaryKeysProvider;
+import com.codingapi.txlcn.tc.core.transaction.txc.analy.util.AnnotationUtils;
+import com.codingapi.txlcn.tc.core.transaction.txc.analy.util.ClassUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author dinghuang123@gmail.com
+ * @since 2019/7/28
+ */
+@Component
+public class PrimaryKeyListVisitorHandler implements PrimaryKeysProvider {
+
+ @Value("${tx-lcn.primary-key-package}")
+ private String primaryKeyPackage;
+
+ @Override
+ public Map> provide() {
+ if (primaryKeyPackage != null) {
+ //扫描所有注解,把主键拿进来
+ // 获取特定包下所有的类(包括接口和类)
+ List> clsList = ClassUtils.getClasses(primaryKeyPackage);
+ //输出所有使用了特定注解的类的注解值
+ return AnnotationUtils.getPrimaryKeyList(clsList);
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/util/AnnotationUtils.java b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/util/AnnotationUtils.java
new file mode 100644
index 000000000..4b7828d43
--- /dev/null
+++ b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/util/AnnotationUtils.java
@@ -0,0 +1,66 @@
+package com.codingapi.txlcn.tc.core.transaction.txc.analy.util;
+
+import com.codingapi.txlcn.tc.annotation.TableId;
+import com.codingapi.txlcn.tc.annotation.TableName;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author dinghuang123@gmail.com
+ * @since 2019/7/29
+ */
+public class AnnotationUtils {
+
+ private static Pattern humpPattern = Pattern.compile("[A-Z]");
+
+ public static Map> getPrimaryKeyList(List> clsList) {
+ if (clsList != null && clsList.size() > 0) {
+ Map> map = new HashMap<>(clsList.size());
+ for (Class> cls : clsList) {
+ //获取类中的所有的方法
+ boolean classHasAnnotation = cls.isAnnotationPresent(TableName.class);
+ if (classHasAnnotation) {
+ TableName tableName = cls.getAnnotation(TableName.class);
+ if (tableName.value().length() != 0) {
+ Field[] fields = cls.getDeclaredFields();
+ String tableIdVale = null;
+ for (Field field : fields) {
+ boolean fieldHasAnnotation = field.isAnnotationPresent(TableId.class);
+ if (fieldHasAnnotation) {
+ TableId tableId = field.getAnnotation(TableId.class);
+ //输出注解属性
+ if (tableId.value().length() != 0) {
+ tableIdVale = tableId.value();
+ } else {
+ //转驼峰
+ tableIdVale = humpToLine(field.getName());
+ }
+ break;
+ }
+ }
+ map.put(tableName.value(), Collections.singletonList(tableIdVale));
+ }
+ }
+ }
+ return map;
+ } else {
+ return null;
+ }
+ }
+
+ public static String humpToLine(String str) {
+ Matcher matcher = humpPattern.matcher(str);
+ StringBuffer sb = new StringBuffer();
+ while (matcher.find()) {
+ matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+}
diff --git a/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/util/ClassUtils.java b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/util/ClassUtils.java
new file mode 100644
index 000000000..f50e7cb4c
--- /dev/null
+++ b/txlcn-tc/src/main/java/com/codingapi/txlcn/tc/core/transaction/txc/analy/util/ClassUtils.java
@@ -0,0 +1,116 @@
+package com.codingapi.txlcn.tc.core.transaction.txc.analy.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * @author dinghuang123@gmail.com
+ * @since 2019/7/29
+ */
+public class ClassUtils {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtils.class);
+
+ private static final String FILE = "file";
+ private static final String JAR = "jar";
+ private static final String CLASS_SIGN = ".class";
+
+ /**
+ * 从包package中获取所有的Class
+ *
+ * @param packageName packageName
+ * @return List
+ */
+ public static List> getClasses(String packageName) {
+ List> classes = new ArrayList<>();
+ boolean recursive = true;
+ String packageDirName = packageName.replace('.', '/');
+ Enumeration dirs;
+ try {
+ dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
+ while (dirs.hasMoreElements()) {
+ URL url = dirs.nextElement();
+ String protocol = url.getProtocol();
+ if (FILE.equals(protocol)) {
+ String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
+ findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
+ } else if (JAR.equals(protocol)) {
+ JarFile jar;
+ try {
+ jar = ((JarURLConnection) url.openConnection()).getJarFile();
+ Enumeration entries = jar.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ String name = entry.getName();
+ if (name.charAt(0) == '/') {
+ name = name.substring(1);
+ }
+ if (name.startsWith(packageDirName)) {
+ int idx = name.lastIndexOf('/');
+ if (idx != -1) {
+ packageName = name.substring(0, idx).replace('/', '.');
+ }
+ if (idx != -1) {
+ if (name.endsWith(CLASS_SIGN) && !entry.isDirectory()) {
+ // 去掉后面的".class" 获取真正的类名
+ String className = name.substring(packageName.length() + 1, name.length() - 6);
+ try {
+ classes.add(Class.forName(packageName + '.' + className));
+ } catch (ClassNotFoundException e) {
+ LOGGER.error(e.getMessage());
+ }
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.error(e.getMessage());
+ }
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.error(e.getMessage());
+ }
+ return classes;
+ }
+
+ /**
+ * 以文件的形式来获取包下的所有Class
+ *
+ * @param packageName packageName
+ * @param packagePath packagePath
+ * @param recursive recursive
+ * @param classes classes
+ */
+ private static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List> classes) {
+ File dir = new File(packagePath);
+ if (!dir.exists() || !dir.isDirectory()) {
+ return;
+ }
+ File[] dirFiles = dir.listFiles(file -> (recursive && file.isDirectory()) || (file.getName().endsWith(CLASS_SIGN)));
+ assert dirFiles != null;
+ for (File file : dirFiles) {
+ if (file.isDirectory()) {
+ findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
+ } else {
+ String className = file.getName().substring(0, file.getName().length() - 6);
+ try {
+ classes.add(Class.forName(packageName + '.' + className));
+ } catch (ClassNotFoundException e) {
+ LOGGER.error(e.getMessage());
+ }
+ }
+ }
+ }
+}
diff --git a/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/TracingAutoConfiguration.java b/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/TracingAutoConfiguration.java
index 7719c5ddc..bc2212d18 100644
--- a/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/TracingAutoConfiguration.java
+++ b/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/TracingAutoConfiguration.java
@@ -15,6 +15,8 @@
*/
package com.codingapi.txlcn.tracing;
+import com.codingapi.txlcn.tracing.spring.TracingSpringContextUtils;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@@ -28,4 +30,8 @@
@ComponentScan
public class TracingAutoConfiguration {
+ @Bean
+ public TracingSpringContextUtils tracingSpringContextUtils() {
+ return new TracingSpringContextUtils();
+ }
}
diff --git a/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/http/ribbon/TxlcnZoneAvoidanceRule.java b/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/http/ribbon/TxlcnZoneAvoidanceRule.java
index 744e232d2..9a1329f53 100644
--- a/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/http/ribbon/TxlcnZoneAvoidanceRule.java
+++ b/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/http/ribbon/TxlcnZoneAvoidanceRule.java
@@ -17,6 +17,8 @@
import com.alibaba.fastjson.JSONObject;
import com.codingapi.txlcn.tracing.TracingContext;
+import com.codingapi.txlcn.tracing.spring.SpringConfig;
+import com.codingapi.txlcn.tracing.spring.TracingSpringContextUtils;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import lombok.extern.slf4j.Slf4j;
@@ -57,7 +59,19 @@ public Server choose(Object key) {
// 1. 自己加入此事务组调用链
assert Objects.nonNull(registration);
- TracingContext.tracing().addApp(registration.getServiceId(), registration.getHost() + ":" + registration.getPort());
+ //兼容springBoot 2.0以下版本
+ Boolean exitHost;
+ try {
+ exitHost = registration.getHost() != null;
+ } catch (NoSuchMethodError noSuchMethodError) {
+ exitHost = false;
+ }
+ if (!exitHost) {
+ SpringConfig springConfig = (SpringConfig) TracingSpringContextUtils.getContext().getBean("springConfig");
+ TracingContext.tracing().addApp(registration.getServiceId(), springConfig.getHost() + ":" + springConfig.getPort());
+ } else {
+ TracingContext.tracing().addApp(registration.getServiceId(), registration.getHost() + ":" + registration.getPort());
+ }
// 2. 获取所有要访问服务的实例
List servers = getLoadBalancer().getAllServers();
diff --git a/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/spring/SpringConfig.java b/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/spring/SpringConfig.java
new file mode 100644
index 000000000..28a639135
--- /dev/null
+++ b/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/spring/SpringConfig.java
@@ -0,0 +1,49 @@
+package com.codingapi.txlcn.tracing.spring;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * @author dinghuang123@gmail.com
+ * @since 2019/7/29
+ */
+@Component
+public class SpringConfig {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SpringConfig.class);
+
+ @Value("${server.port}")
+ public Integer port;
+
+ @Value("${spring.application.name}")
+ public String serviceName;
+
+ public String host;
+
+ public Integer getPort() {
+ return this.port;
+ }
+
+ public String getServiceName() {
+ return this.serviceName;
+ }
+
+ public String getHost() {
+ if (this.host == null) {
+ InetAddress address = null;
+ try {
+ address = InetAddress.getLocalHost();
+ } catch (UnknownHostException e) {
+ LOGGER.info(e.getMessage());
+ }
+ this.host = address.getHostAddress();
+ }
+ return this.host;
+ }
+
+}
diff --git a/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/spring/TracingSpringContextUtils.java b/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/spring/TracingSpringContextUtils.java
new file mode 100644
index 000000000..6e4f8d784
--- /dev/null
+++ b/txlcn-tracing/src/main/java/com/codingapi/txlcn/tracing/spring/TracingSpringContextUtils.java
@@ -0,0 +1,45 @@
+package com.codingapi.txlcn.tracing.spring;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.support.AbstractRefreshableApplicationContext;
+import org.springframework.context.support.GenericApplicationContext;
+
+/**
+ * spring工厂调用辅助类
+ *
+ * @author dinghuang123@gmail.com
+ * @since 2019/2/26
+ */
+public class TracingSpringContextUtils implements ApplicationContextAware {
+ private static final Logger LOGGER = LoggerFactory.getLogger(TracingSpringContextUtils.class);
+
+ private static DefaultListableBeanFactory springFactory;
+
+ private static ApplicationContext context;
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ LOGGER.info("设置Spring上下文");
+ context = applicationContext;
+ if (applicationContext instanceof AbstractRefreshableApplicationContext) {
+ AbstractRefreshableApplicationContext springContext =
+ (AbstractRefreshableApplicationContext) applicationContext;
+ springFactory = (DefaultListableBeanFactory) springContext.getBeanFactory();
+ } else if (applicationContext instanceof GenericApplicationContext) {
+ GenericApplicationContext springContext = (GenericApplicationContext) applicationContext;
+ springFactory = springContext.getDefaultListableBeanFactory();
+ }
+ }
+
+ public static DefaultListableBeanFactory getSpringFactory() {
+ return springFactory;
+ }
+
+ public static ApplicationContext getContext() {
+ return context;
+ }
+}
diff --git a/txlcn-tracing/src/main/resources/META-INF/spring.factories b/txlcn-tracing/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..74f9765f1
--- /dev/null
+++ b/txlcn-tracing/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.codingapi.txlcn.tracing.TracingAutoConfiguration
\ No newline at end of file