我有一个包含包私有(private)方法的库类。不能通过子类直接覆盖此方法。当从库内部调用这个包私有(private)方法时,有没有办法,无论多么丑陋,都可以执行自己的代码,例如使用 AspectJ?
这是该类的一个简化示例(packagePrivateMethod() 实际上不是直接调用的,而是从 native 代码调用的):
public LibClass {
public LibClass() {
...
packagePrivateMethod();
...
}
void packagePrivateMethod() {
// <-- here I want to execute additional code
...
}
}
最佳答案
您可以使用相当重量级的方法。
这会起作用,因为您可以修改所有内容的字节码,但它要求您在执行之前修改该字节码。
当然,您也可以静态地执行此操作,只需修改类文件,将现有的字节代码替换为您在上面的第 3 步中创建的字节代码。
如果您不想/不能静态替换类的字节码,则必须在运行时修改字节码。因为使用 Java 代理是一个好主意。
由于到目前为止这一切都相当抽象,我添加了一个示例,它将拦截您的库类的加载,在包私有(private)方法中注入(inject)方法调用。当 main 方法执行时,您可以从输出中看到,注入(inject)的方法直接在库类代码之前被调用。如果您添加 return; 作为注入(inject)代码,您还可以完全阻止该方法的执行。
下面是使用 Java 6 和 JavaAssist 解决问题的示例代码。如果你想沿着这条路走下去并使用更新的东西,比如 Java 7,你只需要用 ASM 替换字节码操作。这有点不太可读,但也不完全是火箭科学。
主类:
package com.aop.example;
public class Main {
public static void main(String[] args) {
System.out.println("Main starts!");
LibClass libClass = new LibClass();
System.out.println("Main finished!");
}
}
你的库类:
package com.aop.example;
public class LibClass {
public LibClass() {
packagePrivateMethod();
}
void packagePrivateMethod() {
// <-- here I want to execute additional code
System.out.println("In packagePrivateMethod");
}
}
代理:
package com.aop.agent;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.LoaderClassPath;
import javassist.NotFoundException;
public class Agent {
public static void premain(String agentArgs, Instrumentation instr) {
System.out.println("Agent starts!");
instr.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader classLoader, String className, Class<?> arg2, ProtectionDomain arg3,
byte[] bytes)
throws IllegalClassFormatException {
System.out.println("Before loading class " + className);
final String TARGET_CLASS = "com/aop/example/LibClass";
if (!className.equals(TARGET_CLASS)) {
return null;
}
LoaderClassPath path = new LoaderClassPath(classLoader);
ClassPool pool = new ClassPool();
pool.appendSystemPath();
pool.appendClassPath(path);
try {
CtClass targetClass = pool.get(TARGET_CLASS.replace('/', '.'));
System.out.println("Enhancing class " + targetClass.getName());
CtMethod[] methods = targetClass.getDeclaredMethods();
for (CtMethod method : methods) {
if (!method.getName().contains("packagePrivateMethod")) {
continue;
}
System.out.println("Enhancing method " + method.getSignature());
String myMethodInvocation = "com.aop.agent.Agent.myMethodInvocation();";
method.insertBefore(myMethodInvocation);
}
System.out.println("Enhanced bytecode");
return targetClass.toBytecode();
}
catch (CannotCompileException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
catch (NotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
});
}
public static void myMethodInvocation() {
System.out.println("<<<My injected code>>>!");
}
}
运行示例的命令(您必须将代理放入具有属性 Premain-Class: com.aop.agent.Agent 的 list 的 jar 中:
%JAVA_HOME%\bin\java -cp .;..\javassist-3.12.1.GA.jar -javaagent:..\..\agent.jar com.aop.example.Main
此示例运行如下命令的输出:
Agent starts!
Before loading class com/aop/example/Main
Main starts!
Before loading class com/aop/example/LibClass
Enhancing class com.aop.example.LibClass
Enhancing method ()V
Enhanced bytecode
<<<My injected code>>>!
In packagePrivateMethod
Main finished!
Before loading class java/lang/Shutdown
Before loading class java/lang/Shutdown$Lock
关于java - 添加代码打包私有(private)库方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20161210/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
我有一个ModularSinatra应用程序,我正在尝试将Bootstrap添加到应用程序中。get'/bootstrap/application.css'doless:"bootstrap/bootstrap"end我在views/bootstrap中有所有less文件,包括bootstrap.less。我收到这个错误:Less::ParseErrorat/bootstrap/application.css'reset.less'wasn'tfound.Bootstrap.less的第一行是://CSSReset@import"reset.less";我尝试了所有不同的路径格式,但它