diff --git a/skywalking-sdk-plugin/pom.xml b/skywalking-sdk-plugin/pom.xml index 2cafbda58..e92dbf455 100644 --- a/skywalking-sdk-plugin/pom.xml +++ b/skywalking-sdk-plugin/pom.xml @@ -7,6 +7,7 @@ 1.0-SNAPSHOT ../skywalking-api/skywalking-sdk-plugin + spring-plugin pom diff --git a/skywalking-sdk-plugin/spring-plugin/pom.xml b/skywalking-sdk-plugin/spring-plugin/pom.xml index 32386e78a..ad274276c 100644 --- a/skywalking-sdk-plugin/spring-plugin/pom.xml +++ b/skywalking-sdk-plugin/spring-plugin/pom.xml @@ -1,6 +1,4 @@ - - skywalking-sdk-plugin @@ -10,6 +8,55 @@ 4.0.0 spring-plugin + jar + spring-plugin + http://maven.apache.org - \ No newline at end of file + + UTF-8 + + + + + junit + junit + 3.8.1 + compile + + + org.springframework + spring-beans + 3.2.0.RELEASE + compile + + + org.springframework + spring-context + 3.2.0.RELEASE + test + + + org.springframework + spring-aop + 3.2.0.RELEASE + test + + + org.aspectj + aspectjweaver + 1.8.7 + test + + + org.javassist + javassist + 3.20.0-GA + + + com.ai.cloud + skywalking-api + 1.0-SNAPSHOT + + + diff --git a/skywalking-sdk-plugin/spring-plugin/src/main/java/com/ai/cloud/skywalking/plugin/spring/SpringBuriedPoint.java b/skywalking-sdk-plugin/spring-plugin/src/main/java/com/ai/cloud/skywalking/plugin/spring/SpringBuriedPoint.java deleted file mode 100644 index 6cee452a8..000000000 --- a/skywalking-sdk-plugin/spring-plugin/src/main/java/com/ai/cloud/skywalking/plugin/spring/SpringBuriedPoint.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.ai.cloud.skywalking.plugin.spring; - -/** - * Created by astraea on 2015/11/9. - */ -public class SpringBuriedPoint { -} diff --git a/skywalking-sdk-plugin/spring-plugin/src/main/java/com/ai/cloud/skywalking/plugin/spring/Tracing.java b/skywalking-sdk-plugin/spring-plugin/src/main/java/com/ai/cloud/skywalking/plugin/spring/Tracing.java new file mode 100644 index 000000000..30ce1217a --- /dev/null +++ b/skywalking-sdk-plugin/spring-plugin/src/main/java/com/ai/cloud/skywalking/plugin/spring/Tracing.java @@ -0,0 +1,11 @@ +package com.ai.cloud.skywalking.plugin.spring; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Tracing { +} diff --git a/skywalking-sdk-plugin/spring-plugin/src/main/java/com/ai/cloud/skywalking/plugin/spring/TracingEnhanceProcessor.java b/skywalking-sdk-plugin/spring-plugin/src/main/java/com/ai/cloud/skywalking/plugin/spring/TracingEnhanceProcessor.java new file mode 100644 index 000000000..fcfe91376 --- /dev/null +++ b/skywalking-sdk-plugin/spring-plugin/src/main/java/com/ai/cloud/skywalking/plugin/spring/TracingEnhanceProcessor.java @@ -0,0 +1,175 @@ +package com.ai.cloud.skywalking.plugin.spring; + +import com.ai.cloud.skywalking.buriedpoint.LocalBuriedPointSender; +import com.ai.cloud.skywalking.model.SendData; +import javassist.*; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class TracingEnhanceProcessor implements BeanPostProcessor { + + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + List methods = new ArrayList(); + for (Method method : bean.getClass().getMethods()) { + if (method.isAnnotationPresent(Tracing.class)) { + methods.add(method); + } + } + if (methods.size() > 0) { + try { + ClassPool mPool = new ClassPool(true); + //创建代理类 + CtClass mCtc = createProxyClass(bean, mPool); + //代理类继承所有的被代理的接口 + inheritanceAllInterfaces(bean, mPool, mCtc); + // + mCtc.setSuperclass(mPool.get(bean.getClass().getName())); + //添加字段 + mCtc.addField(CtField.make("private " + LocalBuriedPointSender.class.getName() + " buriedPoint;", mCtc)); + mCtc.addField(CtField.make("private " + bean.getClass().getName() + " realBean;", mCtc)); + // 校验方法 + for (Method method : methods) { + // 生成方法头 + StringBuilder result = new StringBuilder(); + result.append(generateMethodHead(method)); + // 生成方法参数 + result.append(generateMethodParameter(method)); + // 生成抛出异常 + result.append(generateException(method)); + // 生成方法体 + result.append(generateMethodBody(bean, method)); + // TODO 注解 + // TODO 参数注解 + mCtc.addMethod(CtMethod.make(result.toString(), mCtc)); + } + mCtc.addConstructor(CtNewConstructor.make("public " + bean.getClass().getSimpleName() + "Proxy(" + LocalBuriedPointSender.class + .getName() + " buriedPoint, " + bean.getClass().getName() + " realBean){" + + "this.buriedPoint = buriedPoint;this.realBean = realBean;}", mCtc)); + mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc)); + Class classes = mCtc.toClass(); + Constructor constructor = classes.getConstructor(LocalBuriedPointSender.class, bean.getClass()); + return constructor.newInstance(new LocalBuriedPointSender(), bean); + } catch (CannotCompileException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NotFoundException e) { + e.printStackTrace(); + } + } + + return bean; + } + + private String generateMethodBody(Object bean, Method method) { + StringBuilder result = new StringBuilder(); + result.append("{"); + result.append(" try{ this.buriedPoint.beforeSend"); + result.append(generateBeforeSendParamter(bean, method)); + if (!"void".equals(method.getReturnType().getSimpleName())) { + result.append(" return "); + } + result.append("this.realBean." + method.getName()); + result.append(generateMethodInvokeParameters(method)); + result.append("}catch(Throwable e){"); + result.append(" this.buriedPoint.handleException(e);"); + result.append(" throw e;"); + result.append("}finally{"); + result.append(generateAfterTracing()); + result.append("}}"); + return result.toString(); + } + + private String generateBeforeSendParamter(Object bean, Method method) { + StringBuilder builder = new StringBuilder("(" + SendData.class.getName() + ".newBuilder().viewPoint(\"" + + bean.getClass().getName() + "." + method.getName()); + builder.append("("); + for (Class param : method.getParameterTypes()) { + builder.append(param.getSimpleName() + ","); + } + if (method.getGenericParameterTypes().length > 0) { + builder = builder.delete(builder.length() - 1, builder.length()); + } + builder.append(")"); + builder.append("\").build());"); + return builder.toString(); + } + + private String generateMethodInvokeParameters(Method method) { + StringBuilder result = new StringBuilder(); + int index; + result.append("("); + index = 0; + for (Class parameter : method.getParameterTypes()) { + result.append(parameter.getSimpleName().toLowerCase() + "$" + (index++) + ","); + } + if (method.getParameterTypes().length > 0) { + result = result.delete(result.length() - 1, result.length()); + } + result.append(");"); + return result.toString(); + } + + private String generateAfterTracing() { + return " this.buriedPoint.afterSend();"; + } + + private String generateException(Method method) { + StringBuilder resultB = new StringBuilder(" throws "); + for (Class exceptionClasses : method.getExceptionTypes()) { + resultB.append(exceptionClasses.getName() + ","); + } + if (method.getExceptionTypes().length > 0) { + resultB.delete(resultB.length() - 1, resultB.length()); + } else { + resultB = new StringBuilder(); + } + return resultB.toString(); + } + + private String generateMethodParameter(Method method) { + StringBuilder result = new StringBuilder(); + result.append("("); + int index = 0; + for (Class param : method.getParameterTypes()) { + result.append(param.getSimpleName() + " " + param.getSimpleName(). + toLowerCase() + "$" + (index++) + ","); + } + if (method.getGenericParameterTypes().length > 0) { + result = result.delete(result.length() - 1, result.length()); + } + result.append(")"); + return result.toString(); + } + + private String generateMethodHead(Method method) { + return "public " + method.getReturnType().getSimpleName() + " " + method.getName(); + } + + private CtClass createProxyClass(Object bean, ClassPool mPool) { + return mPool.makeClass(bean.getClass().getSimpleName() + "Proxy$" + ThreadLocalRandom.current().nextInt(100)); + } + + private void inheritanceAllInterfaces(Object bean, ClassPool mPool, CtClass mCtc) throws NotFoundException { + for (Class classes : bean.getClass().getInterfaces()) { + mCtc.addInterface(mPool.get(classes.getClass().getName())); + } + } + + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } +} \ No newline at end of file diff --git a/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/aspect/AspectBean.java b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/aspect/AspectBean.java new file mode 100644 index 000000000..adf2ff3ce --- /dev/null +++ b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/aspect/AspectBean.java @@ -0,0 +1,11 @@ +package com.ai.cloud.skywalking.plugin.spring.aspect; + +public class AspectBean { + public void before() { + System.out.println("before aspect"); + } + + public void after() { + System.out.println("after aspect"); + } +} diff --git a/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/aspect/TestAspect.java b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/aspect/TestAspect.java new file mode 100644 index 000000000..9ba0f5a00 --- /dev/null +++ b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/aspect/TestAspect.java @@ -0,0 +1,13 @@ +package com.ai.cloud.skywalking.plugin.spring.aspect; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class TestAspect { + + public static void main(String[] args) { + ClassPathXmlApplicationContext classPathXmlApplicationContext = + new ClassPathXmlApplicationContext("classpath*:springConfig-aspect.xml"); + TracingAspectBean testBuriedPointBean = classPathXmlApplicationContext.getBean(TracingAspectBean.class); + testBuriedPointBean.doBusiness(); + } +} diff --git a/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/aspect/TracingAspectBean.java b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/aspect/TracingAspectBean.java new file mode 100644 index 000000000..300f59e0b --- /dev/null +++ b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/aspect/TracingAspectBean.java @@ -0,0 +1,12 @@ +package com.ai.cloud.skywalking.plugin.spring.aspect; + +import com.ai.cloud.skywalking.plugin.spring.Tracing; + +public class TracingAspectBean { + + @Tracing + public String doBusiness() { + System.out.println("do business"); + return "Hello World"; + } +} diff --git a/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/common/TestBuriedPoint.java b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/common/TestBuriedPoint.java new file mode 100644 index 000000000..e26793737 --- /dev/null +++ b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/common/TestBuriedPoint.java @@ -0,0 +1,13 @@ +package com.ai.cloud.skywalking.plugin.spring.common; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class TestBuriedPoint { + + public static void main(String[] args) { + ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath*:springConfig-common.xml"); + TestBuriedPointBean testBuriedPointBean = classPathXmlApplicationContext.getBean(TestBuriedPointBean.class); + testBuriedPointBean.sayTest(); + System.out.println(testBuriedPointBean.addStr(1)); + } +} diff --git a/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/common/TestBuriedPointBean.java b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/common/TestBuriedPointBean.java new file mode 100644 index 000000000..65810c041 --- /dev/null +++ b/skywalking-sdk-plugin/spring-plugin/src/test/java/com/ai/cloud/skywalking/plugin/spring/common/TestBuriedPointBean.java @@ -0,0 +1,27 @@ +package com.ai.cloud.skywalking.plugin.spring.common; + +import com.ai.cloud.skywalking.plugin.spring.Tracing; + + +public class TestBuriedPointBean { + @Tracing + public void sayTest() throws RuntimeException, IllegalStateException { + System.out.println("say Test"); + } + + @Tracing + public static void sayTest1() { + System.out.println("xx"); + } + + @Tracing + public String addStr(int a, String c) { + throw new RuntimeException("aa"); + } + + @Tracing + public String addStr(int a) { + throw new RuntimeException("aa"); + } + +} diff --git a/skywalking-sdk-plugin/spring-plugin/src/test/resources/springConfig-aspect.xml b/skywalking-sdk-plugin/spring-plugin/src/test/resources/springConfig-aspect.xml new file mode 100644 index 000000000..09ad933a0 --- /dev/null +++ b/skywalking-sdk-plugin/spring-plugin/src/test/resources/springConfig-aspect.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/skywalking-sdk-plugin/spring-plugin/src/test/resources/springConfig-common.xml b/skywalking-sdk-plugin/spring-plugin/src/test/resources/springConfig-common.xml new file mode 100644 index 000000000..1b289fda9 --- /dev/null +++ b/skywalking-sdk-plugin/spring-plugin/src/test/resources/springConfig-common.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file