From 761de45694a1c147228e8364a555e68ede6c725c Mon Sep 17 00:00:00 2001 From: longzhe Date: Tue, 29 Mar 2016 13:48:43 +0800 Subject: [PATCH] init project --- .gitignore | 32 ++++++ README.md | 2 + agent/pom.xml | 98 +++++++++++++++++++ .../com/dragon/study/bytebuddy/MyAgent.java | 37 +++++++ .../com/dragon/study/bytebuddy/Trace.java | 67 +++++++++++++ .../context/ApplicationContextHolder.java | 86 ++++++++++++++++ .../httpclient/HttpClientInterceptor.java | 76 ++++++++++++++ .../httpclient/HttpClientTransformer.java | 34 +++++++ .../bytebuddy/redis/RedisInterceptor.java | 49 ++++++++++ .../bytebuddy/redis/RedisTransformer.java | 34 +++++++ app/pom.xml | 54 ++++++++++ .../dragon/study/bytebuddy/Application.java | 19 ++++ .../dragon/study/bytebuddy/bean/Person.java | 34 +++++++ .../bytebuddy/config/PersonConfiguration.java | 22 +++++ .../study/bytebuddy/timer/TimerPerson.java | 48 +++++++++ pom.xml | 18 ++++ run.sh | 3 + 17 files changed, 713 insertions(+) create mode 100755 .gitignore create mode 100644 agent/pom.xml create mode 100644 agent/src/main/java/com/dragon/study/bytebuddy/MyAgent.java create mode 100644 agent/src/main/java/com/dragon/study/bytebuddy/Trace.java create mode 100644 agent/src/main/java/com/dragon/study/bytebuddy/context/ApplicationContextHolder.java create mode 100644 agent/src/main/java/com/dragon/study/bytebuddy/httpclient/HttpClientInterceptor.java create mode 100644 agent/src/main/java/com/dragon/study/bytebuddy/httpclient/HttpClientTransformer.java create mode 100644 agent/src/main/java/com/dragon/study/bytebuddy/redis/RedisInterceptor.java create mode 100644 agent/src/main/java/com/dragon/study/bytebuddy/redis/RedisTransformer.java create mode 100644 app/pom.xml create mode 100644 app/src/main/java/com/dragon/study/bytebuddy/Application.java create mode 100644 app/src/main/java/com/dragon/study/bytebuddy/bean/Person.java create mode 100644 app/src/main/java/com/dragon/study/bytebuddy/config/PersonConfiguration.java create mode 100644 app/src/main/java/com/dragon/study/bytebuddy/timer/TimerPerson.java create mode 100644 pom.xml create mode 100644 run.sh diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..fcd44be --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +.idea +*.iml +*.ipr +*.iws +*/*.iml +*/logs +~ +~/* +infer-out +out +.DS_Store +*.classpath +*.project +*/*.classpath +*/*.project +*/*/*.project +*/*/*.classpath +*/*/*.iml +*/.settings +*/*/.settings +logs +csv-log/target +csv-log/src/test +csv-log/dependency-reduced-pom.xml +dependency-reduced-pom.xml +.idea/* +target +src/test +*.csv +/csv-log-save/shell/Untitled 3.py +/csv-log-save/shell/ceate_offline_table.sh +version.yml diff --git a/README.md b/README.md index 8b840dd..9d33ddc 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ # byte-buddy-agent-example +简单的使用byte-buddy来做成javaagent, 来修改springboot启动的任务 +里面修改了httpclient和redis的调用方法 diff --git a/agent/pom.xml b/agent/pom.xml new file mode 100644 index 0000000..395a851 --- /dev/null +++ b/agent/pom.xml @@ -0,0 +1,98 @@ + + + 4.0.0 + + + com.dragon.study.bytebuddy + agent-example + 1.0.0-SNAPSHOT + + + agent + 1.0.0-SNAPSHOT + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.5.5 + + + jar-with-dependencies + + + true + + true + true + + + com.dragon.study.bytebuddy.MyAgent + + + + + + make-assembly + package + + single + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + 128m + 512m + true + + + + + + + + net.bytebuddy + byte-buddy + 1.3.5 + + + + org.springframework.boot + spring-boot-starter + 1.3.0.RELEASE + provided + + + + org.apache.httpcomponents + httpclient + 4.3.2 + provided + + + + redis.clients + jedis + 2.8.0 + provided + + + + com.dragon.study.bytebuddy + app + 1.0.0-SNAPSHOT + provided + + + + + diff --git a/agent/src/main/java/com/dragon/study/bytebuddy/MyAgent.java b/agent/src/main/java/com/dragon/study/bytebuddy/MyAgent.java new file mode 100644 index 0000000..6fbf93e --- /dev/null +++ b/agent/src/main/java/com/dragon/study/bytebuddy/MyAgent.java @@ -0,0 +1,37 @@ +package com.dragon.study.bytebuddy; + +import com.dragon.study.bytebuddy.httpclient.HttpClientTransformer; +import com.dragon.study.bytebuddy.redis.RedisTransformer; + +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.ClassFileLocator; +import net.bytebuddy.pool.TypePool; + +import java.lang.instrument.Instrumentation; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +/** + * Created by dragon on 16/3/29. + */ +public class MyAgent { + + public static void premain(String arg, Instrumentation instrumentation) { + + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + ClassFileLocator.Compound compound = new ClassFileLocator.Compound(ClassFileLocator.ForClassLoader.of(classLoader), ClassFileLocator.ForClassLoader.ofClassPath()); + + String httpInterceptor = "com.dragon.study.bytebuddy.httpclient.HttpClientInterceptor"; + String redisInterceptor = "com.dragon.study.bytebuddy.redis.RedisInterceptor"; + + new AgentBuilder.Default() + .type(named("org.apache.http.impl.client.CloseableHttpClient")) + .transform(new HttpClientTransformer(TypePool.Default.of(compound).describe(httpInterceptor).resolve())) + .type(named("redis.clients.jedis.Client")) + .transform(new RedisTransformer(TypePool.Default.of(compound).describe(redisInterceptor).resolve())) + .installOn(instrumentation); + + + } +} diff --git a/agent/src/main/java/com/dragon/study/bytebuddy/Trace.java b/agent/src/main/java/com/dragon/study/bytebuddy/Trace.java new file mode 100644 index 0000000..fcafbc4 --- /dev/null +++ b/agent/src/main/java/com/dragon/study/bytebuddy/Trace.java @@ -0,0 +1,67 @@ +package com.dragon.study.bytebuddy; + +/** + * Created by dragon on 16/3/28. + */ +public class Trace { + private String url; + private int statusCode; + private long cost; + private Exception e; + + public Trace() { + } + + public Trace(String url, int statusCode, long cost, Exception e) { + this.url = url; + this.statusCode = statusCode; + this.cost = cost; + this.e = e; + } + + public Trace(String url, int statusCode, long cost) { + this(url, statusCode, cost, null); + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public long getCost() { + return cost; + } + + public void setCost(long cost) { + this.cost = cost; + } + + public Exception getE() { + return e; + } + + public void setE(Exception e) { + this.e = e; + } + + @Override + public String toString() { + return "Trace{" + + "url='" + url + '\'' + + ", statusCode=" + statusCode + + ", cost=" + cost + + ", e=" + e + + '}'; + } +} diff --git a/agent/src/main/java/com/dragon/study/bytebuddy/context/ApplicationContextHolder.java b/agent/src/main/java/com/dragon/study/bytebuddy/context/ApplicationContextHolder.java new file mode 100644 index 0000000..99e8b34 --- /dev/null +++ b/agent/src/main/java/com/dragon/study/bytebuddy/context/ApplicationContextHolder.java @@ -0,0 +1,86 @@ +package com.dragon.study.bytebuddy.context; + + +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by Reilost on 14-3-4. + */ +public class ApplicationContextHolder implements ApplicationContextAware { + private static ApplicationContext _context; + + private static Map mockBeans; + + public static ApplicationContext getApplicatioinContext() { + return _context; + } + + @Override + public void setApplicationContext(ApplicationContext context) { + _context = context; + } + + /** + * 将该对象中的带有Autowired annotation的属性自动注入 + */ + public static void autowireBean(Object obj) { + if (_context != null) { + AutowireCapableBeanFactory factory = _context.getAutowireCapableBeanFactory(); + factory.autowireBean(obj); + } + } + + @SuppressWarnings("unchecked") + public static T getBean(String name) { + return (T) _context.getBean(name); + } + + @SuppressWarnings("unchecked") + public static T getBean(Class clazz) { + T bean = null; + if (mockBeans != null) { + bean = (T) mockBeans.get(clazz); + } + if (bean != null) { + return bean; + } + + + String[] names = _context.getBeanNamesForType(clazz); + if (names == null || names.length == 0) { + return null; + } + return (T) _context.getBean(names[0]); + } + + public static List getBeans(Class clazz) { + List ret = new ArrayList(); + if (_context == null) { + return ret; + } + String[] names = _context.getBeanNamesForType(clazz); + if (names == null || names.length == 0) { + return ret; + } + for (String name : names) { + ret.add((T) _context.getBean(name)); + } + return ret; + } + + public static void setMockBean(Class clazz, Object object) { + if (mockBeans == null) { + mockBeans = new HashMap(); + } + mockBeans.put(clazz, object); + } + + +} diff --git a/agent/src/main/java/com/dragon/study/bytebuddy/httpclient/HttpClientInterceptor.java b/agent/src/main/java/com/dragon/study/bytebuddy/httpclient/HttpClientInterceptor.java new file mode 100644 index 0000000..13a2aba --- /dev/null +++ b/agent/src/main/java/com/dragon/study/bytebuddy/httpclient/HttpClientInterceptor.java @@ -0,0 +1,76 @@ +package com.dragon.study.bytebuddy.httpclient; + +import com.dragon.study.bytebuddy.Trace; +import com.dragon.study.bytebuddy.bean.Person; +import com.dragon.study.bytebuddy.context.ApplicationContextHolder; + +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.SuperCall; + +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.RequestLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; + +import java.util.concurrent.Callable; + +/** + * Created by dragon on 16/3/29. + */ +public class HttpClientInterceptor { + + public static CloseableHttpResponse doExecute( + @SuperCall + Callable client, + @AllArguments + Object[] args) throws Exception { + Person person = ApplicationContextHolder.getBean(Person.class); + Trace trace = new Trace(); + long start = System.currentTimeMillis(); + try { + trace.setUrl(extractRequestUrl(args)); + CloseableHttpResponse response = client.call(); + trace.setCost(System.currentTimeMillis() - start); + trace.setStatusCode(response.getStatusLine().getStatusCode()); + System.out.println("trace is " + trace + ", person is " + person.toString()); + return response; + } catch (Exception e) { + trace.setCost(System.currentTimeMillis() - start); + trace.setE(e); + trace.setStatusCode(-1); + System.out.println("exception trace is " + trace + ", person is " + person.toString()); + throw e; + } + } + + private static String extractRequestUrl(Object[] args) { + if (args[0] instanceof HttpHost) { + HttpHost host = (HttpHost) args[0]; + HttpRequest request = (HttpRequest) args[1]; + return getRequestUrl(host, request); + } else if (args[0] instanceof HttpUriRequest) { + HttpUriRequest request = (HttpUriRequest) args[0]; + if (request != null && request.getURI() != null) { + StringBuilder builder = new StringBuilder(1024); + builder.append(request.getMethod()).append(" ").append(request.getURI().toString()); + return builder.toString(); + } else { + return ""; + } + } else { + return ""; + } + } + + private static String getRequestUrl(HttpHost host, HttpRequest request) { + StringBuilder builder = new StringBuilder(1024); + if (request != null && request.getRequestLine() != null && host != null) { + RequestLine requestLine = request.getRequestLine(); + builder.append(requestLine.getMethod()).append(" ").append(host.toURI()) + .append(requestLine.getUri()); + } + return builder.toString(); + } + +} diff --git a/agent/src/main/java/com/dragon/study/bytebuddy/httpclient/HttpClientTransformer.java b/agent/src/main/java/com/dragon/study/bytebuddy/httpclient/HttpClientTransformer.java new file mode 100644 index 0000000..c7b1a0d --- /dev/null +++ b/agent/src/main/java/com/dragon/study/bytebuddy/httpclient/HttpClientTransformer.java @@ -0,0 +1,34 @@ +package com.dragon.study.bytebuddy.httpclient; + +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.implementation.MethodDelegation; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; + +/** + * Created by dragon on 16/3/29. + */ +public class HttpClientTransformer implements AgentBuilder.Transformer { + + private final TypeDescription delegator; + + public HttpClientTransformer(TypeDescription delegator) { + this.delegator = delegator; + } + + @Override + public DynamicType.Builder transform(DynamicType.Builder builder, + TypeDescription typeDescription, ClassLoader classLoader) { + try { + return builder.method(named("execute") + .and(returns(named("org.apache.http.client.methods.CloseableHttpResponse")))) + .intercept(MethodDelegation.to(delegator)); + } catch (Exception e) { + e.printStackTrace(); + return builder; + } + } +} diff --git a/agent/src/main/java/com/dragon/study/bytebuddy/redis/RedisInterceptor.java b/agent/src/main/java/com/dragon/study/bytebuddy/redis/RedisInterceptor.java new file mode 100644 index 0000000..00a7be6 --- /dev/null +++ b/agent/src/main/java/com/dragon/study/bytebuddy/redis/RedisInterceptor.java @@ -0,0 +1,49 @@ +package com.dragon.study.bytebuddy.redis; + +import com.dragon.study.bytebuddy.Trace; +import com.dragon.study.bytebuddy.bean.Person; +import com.dragon.study.bytebuddy.context.ApplicationContextHolder; + +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.SuperCall; + +import java.util.concurrent.Callable; + +import redis.clients.jedis.Connection; + +/** + * Created by dragon on 16/3/29. + */ +public class RedisInterceptor { + + public static Connection sendMessage( + @SuperCall + Callable client, + @AllArguments + Object[] args) throws Exception { + + Person person = ApplicationContextHolder.getBean(Person.class); + Trace trace = new Trace(); + long start = System.currentTimeMillis(); + + try { + int i = 0; + for(Object arg : args) { + System.out.println("redis arg index: " + i++ + ", value is " + arg.toString()); + } + + trace.setUrl("local redis"); + Connection response = client.call(); + trace.setCost(System.currentTimeMillis() - start); + trace.setStatusCode(200); + System.out.println("trace is " + trace + ", person is " + person.toString()); + return response; + } catch (Exception e) { + trace.setCost(System.currentTimeMillis() - start); + trace.setE(e); + trace.setStatusCode(-1); + System.out.println("exception trace is " + trace + ", person is " + person.toString()); + throw e; + } + } +} diff --git a/agent/src/main/java/com/dragon/study/bytebuddy/redis/RedisTransformer.java b/agent/src/main/java/com/dragon/study/bytebuddy/redis/RedisTransformer.java new file mode 100644 index 0000000..2479d39 --- /dev/null +++ b/agent/src/main/java/com/dragon/study/bytebuddy/redis/RedisTransformer.java @@ -0,0 +1,34 @@ +package com.dragon.study.bytebuddy.redis; + +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.implementation.MethodDelegation; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; + +/** + * Created by dragon on 16/3/29. + */ +public class RedisTransformer implements AgentBuilder.Transformer { + + private final TypeDescription delegator; + + public RedisTransformer(TypeDescription delegator) { + this.delegator = delegator; + } + + @Override + public DynamicType.Builder transform(DynamicType.Builder builder, + TypeDescription typeDescription, ClassLoader classLoader) { + try { + return builder.method(named("sendCommand") + .and(returns(named("redis.clients.jedis.Connection")))) + .intercept(MethodDelegation.to(delegator)); + } catch (Exception e) { + e.printStackTrace(); + return builder; + } + } +} diff --git a/app/pom.xml b/app/pom.xml new file mode 100644 index 0000000..9fa7503 --- /dev/null +++ b/app/pom.xml @@ -0,0 +1,54 @@ + + + + com.dragon.study.bytebuddy + agent-example + 1.0.0-SNAPSHOT + + + 4.0.0 + 1.0.0-SNAPSHOT + app + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + exec + + + + + + + + + org.springframework.boot + spring-boot-starter + 1.3.0.RELEASE + + + + org.apache.httpcomponents + httpclient + 4.3.2 + + + + redis.clients + jedis + 2.8.0 + + + diff --git a/app/src/main/java/com/dragon/study/bytebuddy/Application.java b/app/src/main/java/com/dragon/study/bytebuddy/Application.java new file mode 100644 index 0000000..3007c9a --- /dev/null +++ b/app/src/main/java/com/dragon/study/bytebuddy/Application.java @@ -0,0 +1,19 @@ +package com.dragon.study.bytebuddy; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * Created by dragon on 16/3/29. + */ +@ComponentScan +@EnableScheduling +@EnableAutoConfiguration +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/app/src/main/java/com/dragon/study/bytebuddy/bean/Person.java b/app/src/main/java/com/dragon/study/bytebuddy/bean/Person.java new file mode 100644 index 0000000..51a4f6c --- /dev/null +++ b/app/src/main/java/com/dragon/study/bytebuddy/bean/Person.java @@ -0,0 +1,34 @@ +package com.dragon.study.bytebuddy.bean; + +/** + * Created by dragon on 16/3/28. + */ +public class Person { + + private String name; + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", age=" + age + + '}'; + } +} diff --git a/app/src/main/java/com/dragon/study/bytebuddy/config/PersonConfiguration.java b/app/src/main/java/com/dragon/study/bytebuddy/config/PersonConfiguration.java new file mode 100644 index 0000000..8af3fa0 --- /dev/null +++ b/app/src/main/java/com/dragon/study/bytebuddy/config/PersonConfiguration.java @@ -0,0 +1,22 @@ +package com.dragon.study.bytebuddy.config; + +import com.dragon.study.bytebuddy.bean.Person; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Created by dragon on 16/3/28. + */ +@Configuration +public class PersonConfiguration { + + @Bean + public Person person() { + Person person = new Person(); + person.setName("longzhe"); + person.setAge(29); + return person; + } + +} diff --git a/app/src/main/java/com/dragon/study/bytebuddy/timer/TimerPerson.java b/app/src/main/java/com/dragon/study/bytebuddy/timer/TimerPerson.java new file mode 100644 index 0000000..1739805 --- /dev/null +++ b/app/src/main/java/com/dragon/study/bytebuddy/timer/TimerPerson.java @@ -0,0 +1,48 @@ +package com.dragon.study.bytebuddy.timer; + +import com.dragon.study.bytebuddy.bean.Person; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +import redis.clients.jedis.Jedis; + +/** + * Created by dragon on 16/3/28. + */ +@Component +public class TimerPerson { + + @Autowired + private Person person; + + @Scheduled(fixedDelay = 5000L, initialDelay = 1000L) + public void httpClientTest() { + System.out.println(person.toString() + " calling http client, time is " + System.currentTimeMillis()); + try { + CloseableHttpResponse response = HttpClients.createDefault() + .execute(new HttpGet("http://enjoy.ricebook.com/")); + System.out.println(response.getStatusLine()); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + @Scheduled(fixedDelay = 10000L, initialDelay = 3000L) + public void redisTest() { + System.out.println(person.toString() + " calling redis, time is " + System.currentTimeMillis()); + Jedis jedis = new Jedis("127.0.0.1", 6379); + jedis.set("a", "b"); + String value = jedis.get("a"); + System.out.println("key is a, value is " + value); + jedis.close(); + + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d50dbc5 --- /dev/null +++ b/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.dragon.study.bytebuddy + agent-example + pom + 1.0.0-SNAPSHOT + + agent + app + + + + + diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..d63d584 --- /dev/null +++ b/run.sh @@ -0,0 +1,3 @@ +mvn clean package +java -javaagent:./agent/target/agent-1.0.0-SNAPSHOT-jar-with-dependencies.jar -jar app/target/app-1.0.0-SNAPSHOT-exec.jar +