修复Javassist无法生成已经被Spring增强过的类
This commit is contained in:
parent
3b1af2fa37
commit
d765386e05
|
|
@ -25,10 +25,11 @@
|
|||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>3.20.0-GA</version>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>1.8.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ai.cloud</groupId>
|
||||
|
|
@ -39,7 +40,7 @@
|
|||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>compile</scope>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
|
|
@ -47,12 +48,6 @@
|
|||
<version>3.2.0.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<version>1.8.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ai.cloud</groupId>
|
||||
<artifactId>skywalking-auth</artifactId>
|
||||
|
|
@ -77,11 +72,7 @@
|
|||
<version>2.1.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package com.ai.cloud.skywalking.plugin.spring;
|
||||
|
||||
import com.ai.cloud.skywalking.buriedpoint.LocalBuriedPointSender;
|
||||
import com.ai.cloud.skywalking.model.Identification;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
|
||||
public class TracingAspect {
|
||||
|
||||
public Object doTracing(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
|
||||
LocalBuriedPointSender _sender = new LocalBuriedPointSender();
|
||||
try {
|
||||
StringBuilder viewPoint = new StringBuilder();
|
||||
viewPoint.append(proceedingJoinPoint.getTarget().getClass().getName()
|
||||
+ "(");
|
||||
boolean first = true;
|
||||
for (Object arg : proceedingJoinPoint.getArgs()) {
|
||||
if (!first) {
|
||||
viewPoint.append(",");
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
viewPoint.append(arg.getClass().getName());
|
||||
}
|
||||
viewPoint.append(")");
|
||||
_sender.beforeSend(Identification.newBuilder().viewPoint(viewPoint.toString()).spanType("M").build());
|
||||
return proceedingJoinPoint.proceed();
|
||||
} catch (Throwable e) {
|
||||
_sender.handleException(e);
|
||||
throw e;
|
||||
} finally {
|
||||
_sender.afterSend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,12 @@
|
|||
package com.ai.cloud.skywalking.plugin.spring;
|
||||
|
||||
import com.ai.cloud.skywalking.buriedpoint.LocalBuriedPointSender;
|
||||
import com.ai.cloud.skywalking.conf.AuthDesc;
|
||||
import com.ai.cloud.skywalking.model.Identification;
|
||||
import com.ai.cloud.skywalking.plugin.spring.util.ConcurrentHashSet;
|
||||
|
||||
import javassist.*;
|
||||
import javassist.Modifier;
|
||||
import javassist.bytecode.AnnotationsAttribute;
|
||||
import javassist.bytecode.ConstPool;
|
||||
|
||||
import org.springframework.aop.aspectj.AspectInstanceFactory;
|
||||
import org.springframework.aop.aspectj.AspectJAroundAdvice;
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
import org.springframework.aop.aspectj.SimpleAspectInstanceFactory;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
|
|
@ -18,21 +15,29 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
|||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class TracingEnhanceProcessor implements DisposableBean,
|
||||
BeanPostProcessor, BeanFactoryPostProcessor, ApplicationContextAware {
|
||||
private static ClassPool pool = ClassPool.getDefault();
|
||||
|
||||
private final Set<TracingPattern> beanSet = new ConcurrentHashSet<TracingPattern>();
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(
|
||||
ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
beanSet.addAll(applicationContext.getBeansOfType(TracingPattern.class)
|
||||
.values());
|
||||
for (TracingPattern tracingPattern : applicationContext.getBeansOfType(TracingPattern.class)
|
||||
.values()) {
|
||||
|
||||
AspectJExpressionPointcut packageMatcher = new AspectJExpressionPointcut();
|
||||
packageMatcher.setExpression("within(" + tracingPattern.getPackageExpression() + ")");
|
||||
tracingPattern.setPackageMatcher(packageMatcher);
|
||||
|
||||
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
|
||||
pointcut.setExpression("execution(* " + tracingPattern.getPackageExpression().substring(0, tracingPattern.getPackageExpression().length() - 1) + tracingPattern.getClassExpression() + ".*(..))");
|
||||
tracingPattern.setPointcut(pointcut);
|
||||
|
||||
beanSet.add(tracingPattern);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -49,229 +54,36 @@ public class TracingEnhanceProcessor implements DisposableBean,
|
|||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public enum MatchType {
|
||||
METHOD, PACKAGE, CLASS;
|
||||
}
|
||||
|
||||
private boolean checkMatch(String value, String pattern, MatchType matchType) {
|
||||
boolean result;
|
||||
if ("*".equals(pattern)) {
|
||||
return true;
|
||||
}
|
||||
if (matchType == MatchType.PACKAGE) {
|
||||
if (pattern.endsWith(".*")) {
|
||||
String newPattern = pattern.substring(0,
|
||||
pattern.lastIndexOf(".*"));
|
||||
result = value.startsWith(newPattern);
|
||||
} else {
|
||||
result = value.equals(pattern);
|
||||
}
|
||||
} else {
|
||||
if (pattern.endsWith("*")) {
|
||||
String newPattern = pattern.substring(0,
|
||||
pattern.lastIndexOf("*"));
|
||||
result = value.startsWith(newPattern);
|
||||
} else {
|
||||
result = value.equals(pattern);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if(!AuthDesc.isAuth()){
|
||||
return bean;
|
||||
}
|
||||
|
||||
String packageName;
|
||||
if (bean.getClass().getPackage() == null) {
|
||||
packageName = "";
|
||||
} else {
|
||||
packageName = bean.getClass().getPackage().getName();
|
||||
}
|
||||
String className = bean.getClass().getSimpleName();
|
||||
TracingPattern matchClassBean = null;
|
||||
boolean isMatch = false;
|
||||
for (TracingPattern tracingPattern : beanSet) {
|
||||
if (checkMatch(packageName, tracingPattern.getPackageName(),
|
||||
MatchType.PACKAGE)
|
||||
&& checkMatch(className, tracingPattern.getClassName(),
|
||||
MatchType.CLASS)) {
|
||||
isMatch = true;
|
||||
matchClassBean = tracingPattern;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!isMatch || matchClassBean == null) {
|
||||
if (!AuthDesc.isAuth()) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
// 符合规范
|
||||
try {
|
||||
pool.appendClassPath(new ClassClassPath(bean.getClass()));
|
||||
CtClass ctSource = pool.get(bean.getClass().getName());
|
||||
CtClass ctDestination = pool.makeClass(
|
||||
generateProxyClassName(bean), ctSource);
|
||||
for (CtClass interfaceCtClass : ctSource.getInterfaces()) {
|
||||
ctDestination.addInterface(interfaceCtClass);
|
||||
for (TracingPattern tracingPattern : beanSet) {
|
||||
if (tracingPattern.getPackageMatcher().matches(bean.getClass()) && matchClassName(tracingPattern.getClassExpression(), bean
|
||||
.getClass().getSimpleName())) {
|
||||
ProxyFactory proxyFactory = new ProxyFactory(bean);
|
||||
proxyFactory.setProxyTargetClass(true);
|
||||
AspectInstanceFactory aspectInstanceFactory = new SimpleAspectInstanceFactory(TracingAspect.class);
|
||||
AspectJAroundAdvice advised = new AspectJAroundAdvice(TracingAspect.class.getDeclaredMethods()[0],
|
||||
tracingPattern.getPointcut(), aspectInstanceFactory);
|
||||
proxyFactory.addAdvice(advised);
|
||||
|
||||
return proxyFactory.getProxy();
|
||||
}
|
||||
// 拷贝所有的注解
|
||||
copyClassAnnotation(ctSource, ctDestination);
|
||||
// 拷贝所有的方法,并增强
|
||||
ConstPool cp = ctDestination.getClassFile().getConstPool();
|
||||
for (CtMethod m : ctSource.getDeclaredMethods()) {
|
||||
|
||||
if (m.getModifiers() == Modifier.PRIVATE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CtMethod newm = CtNewMethod.delegator(m, ctDestination);
|
||||
copyMethodAnnotation(cp, m, newm);
|
||||
// 是否符合规范,符合则增强
|
||||
if (checkMatch(m.getName(), matchClassBean.getMethod(),
|
||||
MatchType.METHOD)) {
|
||||
enhanceMethod(bean, newm);
|
||||
}
|
||||
ctDestination.addMethod(newm);
|
||||
}
|
||||
|
||||
Class<?> generateClass = ctDestination.toClass();
|
||||
Object newBean = generateClass.newInstance();
|
||||
Class currentClass = newBean.getClass().getSuperclass();
|
||||
Class valueClass = bean.getClass();
|
||||
Field tmpField = null;
|
||||
while (true) {
|
||||
if (currentClass.getName().equals(Object.class.getName())) {
|
||||
break;
|
||||
}
|
||||
for (Field field : currentClass.getDeclaredFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())){
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
tmpField = valueClass.getDeclaredField(field.getName());
|
||||
tmpField.setAccessible(true);
|
||||
field.set(newBean, tmpField.get(bean));
|
||||
}
|
||||
currentClass = currentClass.getSuperclass();
|
||||
valueClass = valueClass.getSuperclass();
|
||||
}
|
||||
return newBean;
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalStateException("Class ["
|
||||
+ beanName.getClass().getName() + "] cannot be found", e);
|
||||
} catch (CannotCompileException e) {
|
||||
throw new IllegalStateException("Class ["
|
||||
+ beanName.getClass().getName() + "] cannot be compile", e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new IllegalStateException("Failed to instance class["
|
||||
+ beanName.getClass().getName() + "]", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("Failed to access class["
|
||||
+ beanName.getClass().getName() + "]", e);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new IllegalStateException("Failed to access class["
|
||||
+ beanName.getClass().getName() + "]", e);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void copyMethodAnnotation(ConstPool cp, CtMethod m, CtMethod newm) {
|
||||
AnnotationsAttribute invAnn = (AnnotationsAttribute) m.getMethodInfo()
|
||||
.getAttribute(AnnotationsAttribute.invisibleTag);
|
||||
AnnotationsAttribute visAnn = (AnnotationsAttribute) m.getMethodInfo()
|
||||
.getAttribute(AnnotationsAttribute.visibleTag);
|
||||
if (invAnn != null) {
|
||||
newm.getMethodInfo().addAttribute(invAnn.copy(cp, null));
|
||||
private boolean matchClassName(String className, String simpleName) {
|
||||
if ("*".equals(className)) {
|
||||
return true;
|
||||
} else if (className.endsWith("*")) {
|
||||
return simpleName.startsWith(className);
|
||||
}
|
||||
if (visAnn != null) {
|
||||
newm.getMethodInfo().addAttribute(visAnn.copy(cp, null));
|
||||
}
|
||||
}
|
||||
|
||||
private String generateProxyClassName(Object bean) {
|
||||
return bean.getClass().getName() + "$EnhanceBySWTracing$"
|
||||
+ ThreadLocalRandom.current().nextInt(100);
|
||||
}
|
||||
|
||||
private void copyAllFields(CtClass ctSource, CtClass ctDestination)
|
||||
throws CannotCompileException, NotFoundException {
|
||||
// copy fields
|
||||
ConstPool cp = ctDestination.getClassFile().getConstPool();
|
||||
for (CtField ctSourceField : ctSource.getDeclaredFields()) {
|
||||
CtClass fieldTypeClass = ClassPool.getDefault().get(
|
||||
ctSourceField.getType().getName());
|
||||
CtField ctField = new CtField(fieldTypeClass,
|
||||
ctSourceField.getName(), ctDestination);
|
||||
// with annotations
|
||||
copyAllFieldAnnotation(cp, ctSourceField, ctField);
|
||||
ctDestination.addField(ctField);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyAllFieldAnnotation(ConstPool cp, CtField ctSourceField,
|
||||
CtField ctDestinationField) throws CannotCompileException {
|
||||
AnnotationsAttribute invAnn = (AnnotationsAttribute) ctSourceField
|
||||
.getFieldInfo().getAttribute(AnnotationsAttribute.invisibleTag);
|
||||
AnnotationsAttribute visAnn = (AnnotationsAttribute) ctSourceField
|
||||
.getFieldInfo().getAttribute(AnnotationsAttribute.visibleTag);
|
||||
|
||||
if (invAnn != null) {
|
||||
ctDestinationField.getFieldInfo().addAttribute(
|
||||
invAnn.copy(cp, null));
|
||||
}
|
||||
if (visAnn != null) {
|
||||
ctDestinationField.getFieldInfo().addAttribute(
|
||||
visAnn.copy(cp, null));
|
||||
}
|
||||
}
|
||||
|
||||
private void copyClassAnnotation(CtClass ctSource, CtClass ctDestination) {
|
||||
ConstPool cp = ctDestination.getClassFile().getConstPool();
|
||||
AnnotationsAttribute invAnn = (AnnotationsAttribute) ctSource
|
||||
.getClassFile().getAttribute(AnnotationsAttribute.invisibleTag);
|
||||
AnnotationsAttribute visAnn = (AnnotationsAttribute) ctSource
|
||||
.getClassFile().getAttribute(AnnotationsAttribute.visibleTag);
|
||||
if (invAnn != null) {
|
||||
ctDestination.getClassFile().addAttribute(invAnn.copy(cp, null));
|
||||
}
|
||||
if (visAnn != null) {
|
||||
ctDestination.getClassFile().addAttribute(visAnn.copy(cp, null));
|
||||
}
|
||||
}
|
||||
|
||||
protected void enhanceMethod(Object bean, CtMethod method)
|
||||
throws CannotCompileException, NotFoundException {
|
||||
ClassPool cp = method.getDeclaringClass().getClassPool();
|
||||
method.addLocalVariable("___sender",
|
||||
cp.get(LocalBuriedPointSender.class.getName()));
|
||||
method.insertBefore("___sender = new "
|
||||
+ LocalBuriedPointSender.class.getName()
|
||||
+ "();\n___sender.beforeSend"
|
||||
+ generateBeforeSendParameter(bean, method) + "\n");
|
||||
method.addCatch("new " + LocalBuriedPointSender.class.getName()
|
||||
+ "().handleException(e);throw e;", ClassPool.getDefault()
|
||||
.getCtClass(Throwable.class.getName()), "e");
|
||||
method.insertAfter("new " + LocalBuriedPointSender.class.getName()
|
||||
+ "().afterSend();", true);
|
||||
}
|
||||
|
||||
private String generateBeforeSendParameter(Object bean, CtMethod method)
|
||||
throws NotFoundException {
|
||||
StringBuilder builder = new StringBuilder("("
|
||||
+ Identification.class.getName() + ".newBuilder().viewPoint(\""
|
||||
+ bean.getClass().getName() + "." + method.getName());
|
||||
builder.append("(");
|
||||
for (CtClass param : method.getParameterTypes()) {
|
||||
builder.append(param.getSimpleName() + ",");
|
||||
}
|
||||
if (method.getParameterTypes().length > 0) {
|
||||
builder = builder.delete(builder.length() - 1, builder.length());
|
||||
}
|
||||
builder.append(")");
|
||||
builder.append("\").spanType(\"M\").build());");
|
||||
return builder.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,31 +1,42 @@
|
|||
package com.ai.cloud.skywalking.plugin.spring;
|
||||
|
||||
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
|
||||
|
||||
public class TracingPattern {
|
||||
private String packageName = "";
|
||||
private String className = "";
|
||||
private String method = "";
|
||||
private String packageExpression = "";
|
||||
private String classExpression = "";
|
||||
private AspectJExpressionPointcut packageMatcher;
|
||||
private AspectJExpressionPointcut pointcut;
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
public String getPackageExpression() {
|
||||
return packageExpression;
|
||||
}
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
}
|
||||
public void setPackageExpression(String packageExpression) {
|
||||
this.packageExpression = packageExpression;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
public String getClassExpression() {
|
||||
return classExpression;
|
||||
}
|
||||
|
||||
public void setClassName(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
public void setClassExpression(String classExpression) {
|
||||
this.classExpression = classExpression;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
public AspectJExpressionPointcut getPackageMatcher() {
|
||||
return packageMatcher;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
public void setPackageMatcher(AspectJExpressionPointcut packageMatcher) {
|
||||
this.packageMatcher = packageMatcher;
|
||||
}
|
||||
|
||||
public AspectJExpressionPointcut getPointcut() {
|
||||
return pointcut;
|
||||
}
|
||||
|
||||
public void setPointcut(AspectJExpressionPointcut pointcut) {
|
||||
this.pointcut = pointcut;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@
|
|||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="traceType">
|
||||
<xsd:attribute name="packageName" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="className" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="method" type="xsd:string" use="optional"
|
||||
default="*" />
|
||||
<xsd:attribute name="packageExpression" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="classExpression" type="xsd:string" use="optional" default="*" />
|
||||
</xsd:complexType>
|
||||
</xsd:schema>
|
||||
|
|
@ -6,13 +6,17 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
|
||||
@Service
|
||||
@Transactional
|
||||
public class TestBean {
|
||||
public class TestBean/* implements TestInterface*/ {
|
||||
private String value;
|
||||
|
||||
public void testPrintln(String value) {
|
||||
System.out.println(value);
|
||||
}
|
||||
|
||||
private void testPrintln2(String value) {
|
||||
System.out.println(value);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws IllegalAccessException {
|
||||
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath*:springConfig-common.xml");
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@
|
|||
<context:component-scan base-package="com.ai.cloud.skywalking.plugin.spring.test"/>
|
||||
<bean id="jdbcConfig"
|
||||
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="order" value="1" />
|
||||
<property name="ignoreUnresolvablePlaceholders" value="true" />
|
||||
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
|
||||
<property name="locations" value="classpath:jdbc.properties" />
|
||||
<property name="order" value="1"/>
|
||||
<property name="ignoreUnresolvablePlaceholders" value="true"/>
|
||||
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
|
||||
<property name="locations" value="classpath:jdbc.properties"/>
|
||||
</bean>
|
||||
|
||||
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
|
||||
|
|
@ -45,6 +45,6 @@
|
|||
</tx:attributes>
|
||||
</tx:advice>
|
||||
|
||||
<skywalking:trace className="TestBean*" method="*" packageName="*"/>
|
||||
<skywalking:trace packageExpression="com.ai.cloud.skywalking.plugin.spring.test.*" classExpression="*"/>
|
||||
|
||||
</beans>
|
||||
Loading…
Reference in New Issue