完成部分Spring插件功能

This commit is contained in:
zhangxin10 2015-12-18 21:50:16 +08:00
parent 93693ab029
commit f71642eb64
13 changed files with 352 additions and 174 deletions

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:3.8.1" level="project" />
</component>
</module>

View File

@ -40,6 +40,12 @@
<version>3.2.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.0.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
@ -75,5 +81,10 @@
<artifactId>spring-web</artifactId>
<version>3.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.0.RELEASE</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,31 @@
package com.ai.cloud.skywalking.plugin.spring;
public class TracingClassBean {
private String packageName;
private String className;
private String method;
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}

View File

@ -2,164 +2,187 @@ package com.ai.cloud.skywalking.plugin.spring;
import com.ai.cloud.skywalking.buriedpoint.LocalBuriedPointSender;
import com.ai.cloud.skywalking.model.Identification;
import com.ai.cloud.skywalking.plugin.spring.util.ConcurrentHashSet;
import javassist.*;
import javassist.bytecode.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.logging.Level;
import java.util.logging.Logger;
public class TracingEnhanceProcessor implements BeanPostProcessor {
public class TracingEnhanceProcessor implements DisposableBean, BeanPostProcessor, BeanFactoryPostProcessor, ApplicationContextAware {
private Logger logger = Logger.getLogger(TracingEnhanceProcessor.class.getName());
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
List<Method> methods = new ArrayList<Method>();
for (Method method : bean.getClass().getMethods()) {
if (method.isAnnotationPresent(Tracing.class)) {
methods.add(method);
}
}
if (methods.size() > 0) {
try {
ClassPool mPool = ClassPool.getDefault();
mPool.appendClassPath(new ClassClassPath(bean.getClass()));
//创建代理类
CtClass mCtc = createProxyClass(bean, mPool);
//拷贝类上注解
copyClassesAnnotations(bean, mPool, mCtc);
//代理类继承所有的被代理的接口
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));
CtMethod dest = CtMethod.make(result.toString(), mCtc);
private final Set<TracingClassBean> beanSet = new ConcurrentHashSet<TracingClassBean>();
private ApplicationContext applicationContext;
// 拷贝方法上的注解
CtMethod origin = convertMethod2CtMethod(bean, mPool, method);
copyAllAnnotation(origin.getMethodInfo(), dest.getMethodInfo());
mCtc.addMethod(dest);
}
mCtc.addConstructor(CtNewConstructor.make("public " + mCtc.getSimpleName() + "(" + 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) {
logger.log(Level.ALL, "Failed to create the instance of the class[" + beanName + "]", e);
} catch (InstantiationException e) {
logger.log(Level.ALL, "Failed to create the instance of the class[" + beanName + "]", e);
} catch (IllegalAccessException e) {
logger.log(Level.ALL, "Failed to create the instance of the class[" + beanName + "]", e);
} catch (NoSuchMethodException e) {
logger.log(Level.ALL, "Failed to create the instance of the class[" + beanName + "]", e);
} catch (InvocationTargetException e) {
logger.log(Level.ALL, "Failed to create the instance of the class[" + beanName + "]", e);
} catch (NotFoundException e) {
logger.log(Level.ALL, "Failed to create the instance of the class[" + beanName + "]", e);
}
}
return bean;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
applicationContext.getBeansOfType(TracingClassBean.class);
//beanSet.addAll(beanFactory.getBeansOfType(TracingClassBean.class).values());
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private CtMethod convertMethod2CtMethod(Object bean, ClassPool mPool, Method method) throws NotFoundException {
int i = 0;
CtClass ctClass = mPool.get(bean.getClass().getName());
CtClass[] parameterClass = new CtClass[method.getParameterTypes().length];
for (Class methodClass : method.getParameterTypes()) {
parameterClass[i++] = mPool.get(methodClass.getName());
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
boolean isMatch = false;
TracingClassBean matchClassBean = null;
for (TracingClassBean tracingClassBean : beanSet) {
//不符合符合规则
//报名只支持精确匹配类名支持*号模糊
if (isPackageMatch(bean.getClass().getPackage().getName(), tracingClassBean.getPackageName())
|| isClassNameMatch(bean, tracingClassBean.getClassName())) {
isMatch = true;
matchClassBean = tracingClassBean;
continue;
}
}
return ctClass.getDeclaredMethod(method.getName(), parameterClass);
if (!isMatch || matchClassBean == null) {
return bean;
}
//符合规范
try {
ClassPool pool = ClassPool.getDefault();
CtClass ctSource = pool.get(bean.getClass().getName());
CtClass ctDestination = pool.makeClass(generateProxyClassName(bean), ctSource);
//拷贝所有的方法所有的属性以及注解
copy(ctSource, ctDestination);
String methodPrefix = matchClassBean.getMethod();
if (matchClassBean.getMethod().indexOf("*") != -1) {
methodPrefix = methodPrefix.substring(0, matchClassBean.getMethod().indexOf("*"));
}
List<CtMethod> methods = new ArrayList<CtMethod>();
for (CtMethod method : ctDestination.getDeclaredMethods()) {
if (methodPrefix.length() <= 0 || method.getName().startsWith(methodPrefix)) {
methods.add(method);
}
}
for (CtMethod method : methods) {
enhanceMethod(bean, method);
}
// 判断是否存在无参的构造函数如果没有
// 生成实例构造函数
Class generateClass = ctDestination.toClass();
Object newBean = generateClass.newInstance();
BeanUtils.copyProperties(bean, newBean);
return newBean;
} catch (NotFoundException e) {
logger.log(Level.ALL, "Class [" + beanName.getClass().getName() + "] cannot be found");
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) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return bean;
}
private void copyClassesAnnotations(Object bean, ClassPool mPool, CtClass mCtc) throws NotFoundException {
// 拷贝类上注解
CtClass originCtClass = mPool.get(bean.getClass().getName());
AnnotationsAttribute annotations = (AnnotationsAttribute) originCtClass.getClassFile().
getAttribute(AnnotationsAttribute.visibleTag);
AttributeInfo newAnnotations = annotations.copy(mCtc.getClassFile().getConstPool(), Collections.EMPTY_MAP);
mCtc.getClassFile().addAttribute(newAnnotations);
private String generateProxyClassName(Object bean) {
return bean.getClass().getName() + "$EnhanceBySWTracing$" + ThreadLocalRandom.current().nextInt(100);
}
private void copyAllAnnotation(MethodInfo origin, MethodInfo dest) {
AnnotationsAttribute annotations = (AnnotationsAttribute) origin.getAttribute(AnnotationsAttribute.visibleTag);
ParameterAnnotationsAttribute pannotations = (ParameterAnnotationsAttribute) origin.getAttribute(ParameterAnnotationsAttribute.visibleTag);
ExceptionsAttribute exAt = (ExceptionsAttribute) origin.getAttribute(ExceptionsAttribute.tag);
SignatureAttribute sigAt = (SignatureAttribute) origin.getAttribute(SignatureAttribute.tag);
if (annotations != null) {
AttributeInfo newAnnotations = annotations.copy(dest.getConstPool(), Collections.EMPTY_MAP);
dest.addAttribute(newAnnotations);
}
if (pannotations != null) {
AttributeInfo newAnnotations = pannotations.copy(dest.getConstPool(), Collections.EMPTY_MAP);
dest.addAttribute(newAnnotations);
}
if (sigAt != null) {
AttributeInfo newAnnotations = sigAt.copy(dest.getConstPool(), Collections.EMPTY_MAP);
dest.addAttribute(newAnnotations);
}
if (exAt != null) {
AttributeInfo newAnnotations = exAt.copy(dest.getConstPool(), Collections.EMPTY_MAP);
dest.addAttribute(newAnnotations);
private void copy(CtClass ctSource, CtClass ctDestination) {
try {
// copy fields
ConstPool cp = ctDestination.getClassFile().getConstPool();
for (CtField f : ctSource.getDeclaredFields()) {
CtClass fieldTypeClass = ClassPool.getDefault().get(f.getType().getName());
//with annotations
AnnotationsAttribute invAnn = (AnnotationsAttribute) f.getFieldInfo().getAttribute(
AnnotationsAttribute.invisibleTag);
AnnotationsAttribute visAnn = (AnnotationsAttribute) f.getFieldInfo().getAttribute(
AnnotationsAttribute.visibleTag);
CtField ctField = new CtField(fieldTypeClass, f.getName(), ctDestination);
if (invAnn != null) {
ctField.getFieldInfo().addAttribute(invAnn.copy(cp, null));
}
if (visAnn != null) {
ctField.getFieldInfo().addAttribute(visAnn.copy(cp, null));
}
ctDestination.addField(ctField);
}
// copy methods
for (CtMethod m : ctSource.getDeclaredMethods()) {
//copy the method prefixing it with the source class name
CtMethod newm = CtNewMethod.copy(m, /*ctSource.getSimpleName() + "_" + */m.getName(), ctDestination, null);
// with annotations
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));
}
if (visAnn != null) {
newm.getMethodInfo().addAttribute(visAnn.copy(cp, null));
}
ctDestination.addMethod(newm);
}
// copy annotation
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));
}
} catch (Exception e) {
logger.log(Level.ALL, "Failed to generate proxy class ");
throw new IllegalStateException("Failed to generate proxy class");
}
}
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();
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() + "();___sender.beforeSend"
+ generateBeforeSendParameter(bean, method));
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 generateBeforeSendParamter(Object bean, Method method) {
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 (Class<?> param : method.getParameterTypes()) {
for (CtClass param : method.getParameterTypes()) {
builder.append(param.getSimpleName() + ",");
}
if (method.getGenericParameterTypes().length > 0) {
if (method.getParameterTypes().length > 0) {
builder = builder.delete(builder.length() - 1, builder.length());
}
builder.append(")");
@ -167,64 +190,27 @@ public class TracingEnhanceProcessor implements BeanPostProcessor {
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++) + ",");
private boolean isClassNameMatch(Object bean, String tracingClassBean) {
//
String classNamePrefix = tracingClassBean;
if (tracingClassBean.endsWith("*")) {
classNamePrefix = tracingClassBean.substring(0, tracingClassBean.indexOf('*'));
}
if (method.getParameterTypes().length > 0) {
result = result.delete(result.length() - 1, result.length());
}
result.append(");");
return result.toString();
return bean.getClass().getSimpleName().startsWith(classNamePrefix);
}
private String generateAfterTracing() {
return " this.buriedPoint.afterSend();";
private boolean isPackageMatch(Object bean, String packageName) {
return bean.getClass().getPackage().equals(packageName);
}
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();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
private String generateMethodParameter(Method method) {
StringBuilder result = new StringBuilder();
result.append("(");
int index = 0;
for (Class<?> param : method.getParameterTypes()) {
result.append(param.getName() + " " + param.getSimpleName().
toLowerCase() + "$" + (index++) + ",");
}
if (method.getGenericParameterTypes().length > 0) {
result = result.delete(result.length() - 1, result.length());
}
result.append(")");
return result.toString();
}
@Override
public void destroy() throws Exception {
private String generateMethodHead(Method method) {
return "public " + method.getReturnType().getName() + " " + method.getName();
}
private CtClass createProxyClass(Object bean, ClassPool mPool) {
return mPool.makeClass(bean.getClass().getName() + "$EnhanceBySWTracing$" + 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()));
}
}
}
}

View File

@ -0,0 +1,13 @@
package com.ai.cloud.skywalking.plugin.spring;
public class TracingPackageBean {
private String packageName;
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
}

View File

@ -0,0 +1,4 @@
package com.ai.cloud.skywalking.plugin.spring.extansion;
public class SpringExtensionFactory {
}

View File

@ -0,0 +1,21 @@
package com.ai.cloud.skywalking.plugin.spring.parser;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
public class TracingBeanDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
String toBeTracingBeanClassName = element.getAttribute("name");
String methodPattern = element.getAttribute("method");
//校验入参
//
return beanDefinition;
}
}

View File

@ -0,0 +1,18 @@
package com.ai.cloud.skywalking.plugin.spring.parser;
import com.ai.cloud.skywalking.plugin.spring.TracingEnhanceProcessor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
public class TracingBeanPostProcessorParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
//
beanDefinition.setBeanClass(TracingEnhanceProcessor.class);
return beanDefinition;
}
}

View File

@ -0,0 +1,13 @@
package com.ai.cloud.skywalking.plugin.spring.parser;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
public class TracingPackageDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
return null;
}
}

View File

@ -0,0 +1,16 @@
package com.ai.cloud.skywalking.plugin.spring.schema;
import com.ai.cloud.skywalking.plugin.spring.parser.TracingBeanDefinitionParser;
import com.ai.cloud.skywalking.plugin.spring.parser.TracingBeanPostProcessorParser;
import com.ai.cloud.skywalking.plugin.spring.parser.TracingPackageDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class SWNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("tracing-class", new TracingBeanDefinitionParser());
registerBeanDefinitionParser("tracing-package", new TracingPackageDefinitionParser());
registerBeanDefinitionParser("tracing", new TracingBeanPostProcessorParser());
}
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://code.alibabatech.com/schema/dubbo"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://code.alibabatech.com/schema/dubbo">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
<xsd:annotation>
<xsd:documentation><![CDATA[ Namespace support for the skywalking tracing class. ]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType name="tracingClassType">
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The class name that need to tracing. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="method" type="xsd:wildcard">
<xsd:annotation>
<xsd:documentation><![CDATA[ The method name that need to tracing. ]]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="tracingPackageType">
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="tracing-class" type="tracingClassType">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service argument config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="tracing-package" type="tracingPackageType">
<xsd:annotation>
<xsd:documentation><![CDATA[ The service argument config ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="tracing"/>
</xsd:schema>

View File

@ -0,0 +1 @@
http\://cloud.asiainfo.com/schema/skywalking=com.ai.cloud.skywalking.plugin.spring.schema.SWNamespaceHandler

View File

@ -0,0 +1 @@
http\://cloud.asiainfo.com/schema/skywalking/skywalking.xsd=META-INF/skywalking.xsd