Skip to content

Commit

Permalink
增加 Spring 插件
Browse files Browse the repository at this point in the history
  • Loading branch information
daviyang35 committed Dec 14, 2021
1 parent 754e441 commit 13153f7
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 20 deletions.
1 change: 1 addition & 0 deletions bin/package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ cp ${PROJECT_ROOT_DIR}/repeater-agent/repeater-plugins/okhttp-plugin/target/okht
cp ${PROJECT_ROOT_DIR}/repeater-agent/repeater-plugins/dubbo-plugin/target/dubbo-plugin-*-jar-with-dependencies.jar ${REPEATER_TARGET_DIR}/plugins/dubbo-plugin.jar
cp ${PROJECT_ROOT_DIR}/repeater-agent/repeater-plugins/mybatis-plus-plugin/target/mybatis-plus-plugin-*-jar-with-dependencies.jar ${REPEATER_TARGET_DIR}/plugins/mybatis-plus-plugin.jar
cp ${PROJECT_ROOT_DIR}/repeater-agent/repeater-plugins/openfeign-plugin/target/openfeign-plugin-*-jar-with-dependencies.jar ${REPEATER_TARGET_DIR}/plugins/openfeign-plugin.jar
cp ${PROJECT_ROOT_DIR}/repeater-agent/repeater-plugins/spring-plugin/target/spring-plugin-*-jar-with-dependencies.jar ${REPEATER_TARGET_DIR}/plugins/spring-plugin.jar
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ private void closeOnJDK6() {
}

/**
* 如果是JDK7+的版本, URLClassLoader实现了Closeable接口,直接调用即可
* 如果是JDK7+的版本, URLClassLoader实现了Closeable接口,直接调用即可S
*/
private boolean closeOnJDK7AndPlus() {
if (this instanceof Closeable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public void notFoundAction(String identity) {
},
;

private String name;
private final String name;

Matcher(String name) {
this.name = name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class InvokeType implements java.io.Serializable {
public static final InvokeType JPA = new InvokeType("jpa");

public static final InvokeType SOCKETIO = new InvokeType("socketio");
public static final InvokeType SPRING = new InvokeType("spring");

public static final InvokeType OKHTTP = new InvokeType("okhttp");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.alibaba.jvm.sandbox.repeater.plugin.domain.InvokeType;
import com.alibaba.jvm.sandbox.repeater.plugin.spi.Repeater;
import lombok.experimental.UtilityClass;

import java.util.HashMap;
import java.util.List;
Expand All @@ -14,10 +13,12 @@
*
* @author zhaoyb1990
*/
@UtilityClass
public class RepeaterBridge {
public final class RepeaterBridge {

private final Map<InvokeType, Repeater> cached = new HashMap<InvokeType, Repeater>();
private RepeaterBridge() {
}

private volatile Map<InvokeType, Repeater> cached = new HashMap<InvokeType, Repeater>();

public static RepeaterBridge instance() {
return RepeaterBridge.LazyInstanceHolder.INSTANCE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,23 @@ private void watchBehavior(IBuildingForBehavior behavior, Event.Type[] watchType
private IBuildingForBehavior buildBehavior(EnhanceModel em) {
IBuildingForBehavior behavior = null;
IBuildingForClass builder4Class = new EventWatchBuilder(watcher).onClass(em.getClassPattern());
if (em.getClassAnnotations() != null) {
builder4Class.hasAnnotationTypes(em.getClassAnnotations());
}
if (em.isIncludeSubClasses()) {
builder4Class = builder4Class.includeSubClasses();
}
for (EnhanceModel.MethodPattern mp : em.getMethodPatterns()) {
behavior = builder4Class.onBehavior(mp.getMethodName());
if (ArrayUtils.isNotEmpty(mp.getParameterType())) {
behavior.withParameterTypes(mp.getParameterType());
}
if (ArrayUtils.isNotEmpty(mp.getAnnotationTypes())) {
behavior.hasAnnotationTypes(mp.getAnnotationTypes());
if (null == em.getMethodPatterns()) {
builder4Class.onAnyBehavior();
} else {
for (EnhanceModel.MethodPattern mp : em.getMethodPatterns()) {
behavior = builder4Class.onBehavior(mp.getMethodName());
if (ArrayUtils.isNotEmpty(mp.getParameterType())) {
behavior.withParameterTypes(mp.getParameterType());
}
if (ArrayUtils.isNotEmpty(mp.getAnnotationTypes())) {
behavior.hasAnnotationTypes(mp.getAnnotationTypes());
}
}
}
return behavior;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ private void clearContext(Event event) {
* @return true/false
*/
protected boolean isEntranceFinish(Event event) {
if (null == Tracer.getContext()) {
log.error("Tracer.getContext() is null. invokeType={},event={}", this.invokeType, event.type);
return false;
}
return event.type != Type.BEFORE
// 开启trace的类型负责清理
&& Tracer.getContext().getInvokeType() == invokeType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class EnhanceModel {
*/
private String classPattern;

private String[] classAnnotations;

/**
* 增强方法表达式,,支持通配符
*/
Expand All @@ -49,9 +51,10 @@ public class EnhanceModel {
*/
private boolean includeSubClasses;

@ConstructorProperties({"classPattern", "methodPatterns", "watchTypes", "includeSubClasses"})
EnhanceModel(String classPattern, EnhanceModel.MethodPattern[] methodPatterns, Type[] watchTypes, boolean includeSubClasses) {
@ConstructorProperties({"classPattern", "classAnnotations", "methodPatterns", "watchTypes", "includeSubClasses"})
EnhanceModel(String classPattern, String[] classAnnotations, EnhanceModel.MethodPattern[] methodPatterns, Type[] watchTypes, boolean includeSubClasses) {
this.classPattern = classPattern;
this.classAnnotations = classAnnotations;
this.methodPatterns = methodPatterns;
this.watchTypes = watchTypes;
this.includeSubClasses = includeSubClasses;
Expand Down Expand Up @@ -80,6 +83,10 @@ public String getClassPattern() {
return this.classPattern;
}

public String[] getClassAnnotations() {
return this.classAnnotations;
}

public EnhanceModel.MethodPattern[] getMethodPatterns() {
return this.methodPatterns;
}
Expand All @@ -97,6 +104,7 @@ public static class EnhanceModelBuilder {
private EnhanceModel.MethodPattern[] methodPatterns;
private Type[] watchTypes;
private boolean includeSubClasses;
private String[] classAnnotations;

EnhanceModelBuilder() {
}
Expand All @@ -106,6 +114,11 @@ public EnhanceModel.EnhanceModelBuilder classPattern(String classPattern) {
return this;
}

public EnhanceModel.EnhanceModelBuilder classAnnotations(String[] classAnnotations) {
this.classAnnotations = classAnnotations;
return this;
}

public EnhanceModel.EnhanceModelBuilder methodPatterns(EnhanceModel.MethodPattern[] methodPatterns) {
this.methodPatterns = methodPatterns;
return this;
Expand All @@ -122,7 +135,7 @@ public EnhanceModel.EnhanceModelBuilder includeSubClasses(boolean includeSubClas
}

public EnhanceModel build() {
return new EnhanceModel(this.classPattern, this.methodPatterns, this.watchTypes, this.includeSubClasses);
return new EnhanceModel(this.classPattern, this.classAnnotations, this.methodPatterns, this.watchTypes, this.includeSubClasses);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public static String getTraceId() {
public static void end() {
final TraceContext context = getContext();
if (context != null && log.isDebugEnabled()) {
log.debug("[Tracer] stop trace success,type={},traceId={},cost={}ms", context.getInvokeType(), context.getTraceId(), System.currentTimeMillis() - context.getTimestamp());
log.debug("[Tracer] stop trace success,type={},traceId={},cost={}ms", context.getInvokeType(), context.getTraceId(), System.currentTimeMillis() - context.getTimestamp());
}
getContextCarrie().remove();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.apache.commons.lang3.reflect.MethodUtils;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;

/**
Expand All @@ -32,9 +33,10 @@ public Identity assembleIdentity(BeforeEvent event) {
Object command = field.get(mapperMethod);
Object name = MethodUtils.invokeMethod(command, "getName");
Object type = MethodUtils.invokeMethod(command, "getType");
return new Identity(InvokeType.MYBATIS_PLUS.name(), type.toString(), name.toString(), new HashMap<String, String>(1));

return new Identity(InvokeType.MYBATIS_PLUS.name(), type.toString(), name.toString(), Collections.EMPTY_MAP);
} catch (Exception e) {
return new Identity(InvokeType.MYBATIS_PLUS.name(), "Unknown", "Unknown", new HashMap<String, String>(1));
return new Identity(InvokeType.MYBATIS_PLUS.name(), "Unknown", "Unknown", Collections.EMPTY_MAP);
}
}

Expand Down
1 change: 1 addition & 0 deletions repeater-agent/repeater-plugins/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<module>eh-cache-plugin</module>
<module>guava-cache-plugin</module>
<module>okhttp-plugin</module>
<module>spring-plugin</module>
</modules>

<dependencies>
Expand Down
27 changes: 27 additions & 0 deletions repeater-agent/repeater-plugins/spring-plugin/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>repeater-plugins</artifactId>
<groupId>com.alibaba.jvm.sandbox</groupId>
<version>1.1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-plugin</artifactId>
<name>repeater-agent::plugins::spring</name>

<dependencies>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.alibaba.jvm.sandbox.repeater.plugin.spring;

import com.google.common.collect.Maps;

import java.lang.reflect.Proxy;
import java.util.Map;

/**
* {@link JavaInstanceCache}
* <p>
* Java实例缓存,作用是将拦截到的JavaEntrance缓存起来,作为{@link JavaRepeater}获取java运行实例的补充
* <p>
* 该方法的局限性在于,必须该实例的埋点被采样到
* </p>
*
* @author zhaoyb1990
*/
class JavaInstanceCache {

/**
* key : className
* value : instance
*/
private static Map<String, Object> CACHED = Maps.newConcurrentMap();


/**
* 根据实例的类名缓存
* <p>
* 注意问题:
* 1. 多实例问题可能导致回放失败
* </p>
*
* @param instance 实例
*/
static void cacheInstance(Object instance) {
if (instance != null) {
Class<?> clazz;
if (Proxy.isProxyClass(instance.getClass())) {
clazz = Proxy.getInvocationHandler(instance).getClass();
} else {
clazz = instance.getClass();
}
CACHED.put(clazz.getCanonicalName(), instance);
}
}

/**
* 通过类找到实例
* <p>
* 注意问题:
* 1. 实例的缓存时机是被回放的埋点被采样到(否则sandbox无法感知到实例)
* </p>
*
* @param className 类全名
* @return 实例
*/
static Object getInstance(String className) {
return CACHED.get(className);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.alibaba.jvm.sandbox.repeater.plugin.spring;

import com.alibaba.jvm.sandbox.api.event.Event;
import com.alibaba.jvm.sandbox.repeater.plugin.api.InvocationProcessor;
import com.alibaba.jvm.sandbox.repeater.plugin.core.impl.AbstractInvokePluginAdapter;
import com.alibaba.jvm.sandbox.repeater.plugin.core.model.EnhanceModel;
import com.alibaba.jvm.sandbox.repeater.plugin.domain.InvokeType;
import com.alibaba.jvm.sandbox.repeater.plugin.domain.RepeaterConfig;
import com.alibaba.jvm.sandbox.repeater.plugin.exception.PluginLifeCycleException;
import com.alibaba.jvm.sandbox.repeater.plugin.spi.InvokePlugin;
import com.google.common.collect.Lists;
import org.kohsuke.MetaInfServices;

import java.util.List;

@MetaInfServices(InvokePlugin.class)
public class SpringPlugin extends AbstractInvokePluginAdapter {
@Override
protected List<EnhanceModel> getEnhanceModels() {
EnhanceModel springService = EnhanceModel.builder()
.classPattern("com.aos.*")
.classAnnotations(new String[]{
"org.springframework.stereotype.Service"
})
.methodPatterns(EnhanceModel.MethodPattern.transform("*"))
.watchTypes(Event.Type.BEFORE, Event.Type.RETURN, Event.Type.THROWS)
.build();

EnhanceModel springController = EnhanceModel.builder()
.classPattern("com.aos.*")
.classAnnotations(new String[]{
"org.springframework.stereotype.Controller"
})
.methodPatterns(EnhanceModel.MethodPattern.transform("*"))
.watchTypes(Event.Type.BEFORE, Event.Type.RETURN, Event.Type.THROWS)
.build();

EnhanceModel springRestController = EnhanceModel.builder()
.classPattern("com.aos.*")
.classAnnotations(new String[]{
"org.springframework.web.bind.annotation.RestController"
})
.methodPatterns(EnhanceModel.MethodPattern.transform("*"))
.watchTypes(Event.Type.BEFORE, Event.Type.RETURN, Event.Type.THROWS)
.build();

return Lists.newArrayList(springService, springController, springRestController);
}

@Override
protected InvocationProcessor getInvocationProcessor() {
return new SpringProcessor(getType());
}

@Override
public InvokeType getType() {
return InvokeType.SPRING;
}

@Override
public String identity() {
return "spring";
}

@Override
public boolean isEntrance() {
return false;
}

@Override
public void onConfigChange(RepeaterConfig config) throws PluginLifeCycleException {
super.onConfigChange(config);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.alibaba.jvm.sandbox.repeater.plugin.spring;

import com.alibaba.jvm.sandbox.api.event.BeforeEvent;
import com.alibaba.jvm.sandbox.repeater.plugin.core.impl.api.DefaultInvocationProcessor;
import com.alibaba.jvm.sandbox.repeater.plugin.domain.Identity;
import com.alibaba.jvm.sandbox.repeater.plugin.domain.InvokeType;

import java.util.Collections;

public class SpringProcessor extends DefaultInvocationProcessor {
public SpringProcessor(InvokeType type) {
super(type);
}

@Override
public Identity assembleIdentity(BeforeEvent event) {
return new Identity(InvokeType.JAVA.name(), event.javaClassName, event.javaMethodName + "~" + event.javaMethodDesc, Collections.EMPTY_MAP);
}
}
Loading

0 comments on commit 13153f7

Please sign in to comment.