记第一次Byte Buddy使用
Posted 街头小贩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记第一次Byte Buddy使用相关的知识,希望对你有一定的参考价值。
最近写了一个类似生产者和消费者的多线程程序,由客户(生产者)往队列中放入值, 后台有一个守护线程间歇的从队列中取值交给一个消费者(如存到数据库中)最终达到减轻保存数据库的频率.
写完我想知道中途是否有漏掉的值,也就是已经放到队列中但消费者未消费的值, 这时都需要有一个程序侦听生产者和消费者的记录数, 两者一致时即没有错误反之都是存在bug. 是不是有点像AOP干的事? 但又不想用AOP还有撒可以用: Java Agent!用maven引入依赖开始编译(需要下载asm jar),这时出现未知的模块错误, 项目用的是jdk 11. 不用Java Agent还能用撒?字节码修改。终于绕回来了.
maven 依赖
<!-- https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy -->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.10.13</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.10.13</version>
<scope>test</scope>
</dependency>
maven-surefire-plugin 引入一个参数
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20</version>
<configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
<argLine>-DfailIfNoTests=false</argLine>
<argLine>-javaagent:C:\\\\Users\\xiaofanku\\\\.m2\\repository\\\\net\\\\bytebuddy\\\\byte-buddy-agent\\\\1.10.13\\\\byte-buddy-agent-1.10.13.jar</argLine>
</configuration>
</plugin>
程序代码
public abstract class AbstractCubbyHole<T>
private final static Logger logger = LoggerFactory.getLogger(AbstractCubbyHole.class);
/**
* 保存对象
* @param value
* @return
*/
public abstract boolean put(final T value);
/**
* 保存对象
* @param values
*/
public abstract void putAll(final Collection<T> values);
@FunctionalInterface
public interface CubbyHoleProcessor<T>
/**
* 处理程序
*
* @param action 动作记录
* @return 执行成功的对像执行CubbyHole::toChecksum结果的集合
*/
Set<String> process(Collection<T> action);
public final class CubbyHoleLinkedDeque<T> extends AbstractCubbyHole<T>
private final ConcurrentLinkedDeque<T> queue;
private final static Logger logger = LoggerFactory.getLogger(CubbyHoleLinkedDeque.class);
public CubbyHoleLinkedDeque(final CubbyHoleProcessor<T> processor)
super();
this.queue = new ConcurrentLinkedDeque<>();
Thread thread = new Thread(() ->
while (!Thread.currentThread().isInterrupted()) //判断是否被中断
List<T> rs = new ArrayList<>();
for (T obj = queue.poll(); obj != null; obj = queue.poll())
rs.add(obj);
removeAll(rs, processor);
try
Thread.currentThread().sleep(1000);
catch(InterruptedException e)
e.printStackTrace();
);
thread.setDaemon(true);
thread.start();
@Override
public boolean put(final T value)
//往里放
return queue.add(value);
@Override
public void putAll(final Collection<T> values)
//往里放
queue.addAll(values);
public void each(final Consumer<T> action)
queue.forEach(action);
private void remove(final T data, final CubbyHoleProcessor<T> processor)
if(null == data)
return;
removeAll(List.of(data), processor);
private void removeAll(final Collection<T> data, final CubbyHoleProcessor<T> processor)
if(null == data || data.isEmpty())
return;
final Set<String> affect = processor.process(data);
queue.removeIf(aed -> affect.contains(CubbyHoleLinkedDeque.toChecksum(aed)));
ByteBuddy代码
/**
*
* @author xiaofanku
*/
public class CubbyHoleInterceptor
private final static String NEWLINE = "\\r\\n";
@RuntimeType
public static Object intercept(@Origin Method method, @Argument(0) Object arg0, @SuperCall Callable<?> callable) throws Exception
long start = System.currentTimeMillis();
Object resObj = null;
try
resObj = callable.call();
finally
String descrip = "[TraceCastInterceptor]" + NEWLINE;
descrip += "/*----------------------------------------------------------------------" + NEWLINE;
descrip += " method name: " + method.getName() + NEWLINE;
descrip += " method ages: " + method.getParameterCount() + NEWLINE;
descrip += " method result: " + resObj + NEWLINE;
descrip += " method elapsed: " + (System.currentTimeMillis() - start) + "ms" + NEWLINE;
descrip += "/*----------------------------------------------------------------------" + NEWLINE;
System.out.println(descrip);
return resObj;
public class CubbyHoleFirstTest
@Test
public void testBuddy() throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
//泛型类型需要这么声明参数类型
TypeDescription.Generic genericSuperClass = TypeDescription.Generic.Builder.parameterizedType(CubbyHoleLinkedDeque.class, UserDTO.class).build();
Class<?> dynamicType = new ByteBuddy()
.subclass(genericSuperClass)
.method(ElementMatchers.any())
.intercept(MethodDelegation.to(CubbyHoleInterceptor.class))
.make()
.load(CubbyHoleLinkedDeque.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
.getLoaded();
@SuppressWarnings("unchecked")
CubbyHoleLinkedDeque<UserDTO> ch = (CubbyHoleLinkedDeque<UserDTO>) dynamicType.getConstructor(CubbyHoleProcessor.class).newInstance(new UserDTOCubbyHoleProcessor());
ThreadPoolExecutor tpe=(ThreadPoolExecutor)Executors.newCachedThreadPool();
//开几个线程以不同的频率周斯的往ch中put
CubbyHoleRunner t1 = new CubbyHoleRunner("t1", ch);
CubbyHoleRunner t4 = new CubbyHoleRunner("t4", ch);
CubbyHoleRunner t3 = new CubbyHoleRunner("t3", ch);
CubbyHoleRunner t2 = new CubbyHoleRunner("t2", ch);
tpe.execute(t1);
tpe.execute(t2);
tpe.execute(t3);
tpe.execute(t4);
tpe.shutdown();
while (!tpe.isTerminated())
//System.out.println("thread execute now");
System.out.println("Finished all threads");
ch.each((UserDTO ut)->System.out.println(ut));
A: ERROR!
java.lang.IllegalArgumentException: Cannot subclass primitive, array or final types: com.apobates.forum.utils.cache.CubbyHoleLinkedDeque<com.apobates.forum.util.test.cubby.UserDTO>
CubbyHoleLinkedDeque 类有final标识, ByteBuddy因此不能创建子类, 改正:将final去掉
public class CubbyHoleLinkedDeque<T> extends AbstractCubbyHole<T>
B: ERROR!
java.lang.IllegalArgumentException: None of […] allows for delegation from public boolean java.lang.Object.equals(java.lang.Object)
改正
public class CubbyHoleInterceptor
private final static String NEWLINE = "\\r\\n";
@RuntimeType
public static Object intercept(@AllArguments Object[] allArguments, @net.bytebuddy.implementation.bind.annotation.Origin Method method, @SuperCall Callable<Object> callable) throws Exception
C: 侦听的方法没有输出: removeAll, each方法不希望输出
改正: 新增一下注解,为put, putAll, remove, removeAll方法标记
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TraceCast
String value();
CubbyHoleLinkedDeque类的方法加上注解
public class CubbyHoleLinkedDeque<T> extends AbstractCubbyHole<T>
@TraceCast(value="push")
@Override
public boolean put(final T value)
@TraceCast(value="push")
@Override
public void putAll(final Collection<T> values)
@TraceCast(value="pop")
private void remove(final T data, final CubbyHoleProcessor<T> processor)
@TraceCast(value="pop")
private void removeAll(final Collection<T> data, final CubbyHoleProcessor<T> processor)
CubbyHoleFirstTest.testBuddy作以下改正:
//泛型类型需要这么声明参数类型
TypeDescription.Generic genericSuperClass = TypeDescription.Generic.Builder.parameterizedType(CubbyHoleLinkedDeque.class, UserDTO.class).build();
Class<?> dynamicType = new ByteBuddy()
.subclass(genericSuperClass)
.method(ElementMatchers.isAnnotatedWith(TraceCast.class))
.intercept(MethodDelegation.to(CubbyHoleInterceptor.class))
.make()
.load(CubbyHoleLinkedDeque.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
.getLoaded();
@SuppressWarnings("unchecked")
CubbyHoleLinkedDeque<UserDTO> ch = (CubbyHoleLinkedDeque<UserDTO>) dynamicType.getConstructor(CubbyHoleProcessor.class).newInstance(new UserDTOCubbyHoleProcessor());
ThreadPoolExecutor tpe=(ThreadPoolExecutor)Executors.newCachedThreadPool();
//开几个线程以不同的频率周斯的往ch中put
CubbyHoleRunner t1 = new CubbyHoleRunner("t1", ch);
CubbyHoleRunner t4 = new CubbyHoleRunner("t4", ch);
CubbyHoleRunner t3 = new CubbyHoleRunner("t3", ch);
CubbyHoleRunner t2 = new CubbyHoleRunner("t2", ch);
tpe.execute(t1);
tpe.execute(t2);
tpe.execute(t3);
tpe.execute(t4);
//增加putAll测试
CubbyHoleMultiRunner mt1 = new CubbyHoleMultiRunner("mt1",ch);
CubbyHoleMultiRunner mt2 = new CubbyHoleMultiRunner("mt2",ch);
CubbyHoleMultiRunner mt3 = new CubbyHoleMultiRunner("mt3",ch);
CubbyHoleMultiRunner mt4 = new CubbyHoleMultiRunner("mt4",ch);
CubbyHoleMultiRunner mt5 = new CubbyHoleMultiRunner("mt5",ch);
tpe.execute(mt1);
tpe.execute(mt2);
tpe.execute(mt3);
tpe.execute(mt4);
tpe.execute(mt5);
tpe.shutdown();
while (!tpe.isTerminated())
//System.out.println("thread execute now");
System.out.println("Finished all threads");
ch.each((UserDTO ut)->System.out.println(ut));
还是没有removeAll方法调用的输出, 因为remove,removeAll都是private修饰的, 子类是不可见的, 可以将其改为public或protected. 但我不希望完全公开(public)。将其设为protected
总结
现在即使不用Byte Buddy也可以新增一个CubbyHoleLinkedDeque的子类,在子类的方法中(例:put)调用父类的同名方法,并增加日志输出. 可想而知语言规范不允话的修改字节码也没用
附送一个Advice示例
@Test
public void testBuddy() throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
//泛型类型需要这么声明参数类型
TypeDescription.Generic genericSuperClass = TypeDescription.Generic.Builder.parameterizedType(CubbyHoleLinkedDeque.class, UserDTO.class).build();
Class<?> dynamicType = new ByteBuddy()
.subclass(genericSuperClass)
.method(ElementMatchers.isAnnotatedWith(TraceCast.class))
.intercept(Advice.to(TraceCasAdvisor.class))
.make()
.load(CubbyHoleLinkedDeque.class.getClassLoader())
.getLoaded();
//ETC
TraceCasAdvisor类:
public class Byte Buddy 教程
字节码Byte-buddy 使用委托实现抽象类方法并注入自定义 注解信息
字节码基于Byte Buddy语法创建的第一个 HelloWorld