Java

java instrument api倒底能不能给一个类添加新方法?

java instrument api倒底能不能给一个类添加新方法? 答: 这取决于这个类是否已经被定义过了。     1. 如果还没被定义,就可以,即使这个类是系统类;     2. 如果已经定义了,这时的修改就是二次插桩(retransform),二次插桩时可以修改方法体,但不允许加减方法。 官方文档说: 引用 The retransformation may change method bodies, the constant pool and attributes. The retransformation must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance. These restrictions maybe be lifted in future versions. 不过要注意最后一句: These restrictions maybe …

java instrument api倒底能不能给一个类添加新方法? Read More »

谁加载了我 – 1.普通的Main程序

在普通的Main程序中, 某个类是被哪个加载器所加载的? print(String.class.getClassLoader()); //打印null, 这在HotSpot里代表Bootstrap ClassLoader,它负责加载$JAVA_HOME/lib里的类库 print(List.class.getClassLoader()); // 同上 print(learn.classloader.WhoLoadsMe.class.getClassLoader()); // 打印sun.misc.Launcher$AppClassLoader,它在HotSpot里代表System Class Loader, 负责加载用户类路径上的类库 print(learn.classloader.Friend.class.getClassLoader()); //同上 //看看sun.misc.Launcher$AppClassLoader的父加载器和祖父加载器是哪些 print(learn.classloader.Friend.class.getClassLoader().getParent()); //打印sun.misc.Launcher$ExtClassLoader,它在HotSpot里用于加载$JAVA_HOME/lib/ext下的类库 print(learn.classloader.Friend.class.getClassLoader().getParent().getParent()); //打印null, 即Bootstrap ClassLoader Class<?> remoteFriendClass = new learn.classloader.RemoteClassLoader().loadClass( "learn.classloader.RemoteFriend"); print(remoteFriendClass.getClassLoader()); //打印 learn.classloader.RemoteClassLoader,即自写的class loader(后文有这个class loader的代码) //看看自写加载器的父加载器是哪个 print(remoteFriendClass.getClassLoader().getParent()); //打印sun.misc.Launcher$AppClassLoader,即System ClassLoader print(remoteFriendClass.getField("friendOfFriend").getType() .getClassLoader()); //同上, 这意味着在"RemoteFriend"里直接引用的类(后文附上了代码描述这个引用)是由RemoteFriend的加载器所加载的 //最后看下context class loader print(Thread.currentThread().getContextClassLoader()); //打印sun.misc.Launcher$AppClassLoader,即system classloader 附: RemoteClassLoader package learn.classloader; public class …

谁加载了我 – 1.普通的Main程序 Read More »

谁加载了我 – 2.Tomcat6里面的web应用

public class MyServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException { print(Servlet.class.getClassLoader()); //输出org.apache.catalina.loader.StandardClassLoader,它负责加载/common下的类库; Servlet.class就是common/servlet-api.jar里的类 print(HttpServlet.class.getClassLoader()); //同上 //看看它的父加载器 print(HttpServlet.class.getClassLoader().getParent()); //是sun.misc.Launcher$AppClassLoader,即system classloader print(MyServlet.class.getClassLoader());//输出org.apache.catalina.loader.WebappClassLoader,它负责加载当前web-app下的类库; MyServlet.class就是当前web-app下的类 //看看它的父加载器 print(MyServlet.class.getClassLoader().getParent()); //输出 org.apache.catalina.loader.StandardClassLoader //试着找一下 Tomcat启动入口类的加载器 print(this.getClass().getClassLoader().loadClass("org.apache.catalina.startup.Bootstrap").getClassLoader()); //输出sun.misc.Launcher$AppClassLoader,即system classloader //看看当前的context class loader是哪个 print(Thread.currentThread().getContextClassLoader()); //输出org.apache.catalina.loader.WebappClassLoader,即加载当前web-app的加载器 } }

一步一步地理解Visitor模式

现在能找到的Visitor模式的讲解大都非常跳跃,问题列出来出后,讲着讲着就突然给出了生涩的UML类图和accept()、visit()等奇怪的方法名,让人非常费解; 读者即使理解了,也有囫囵吞枣的感觉,不能领会其精妙之处。 本文试图以问题为驱动,以代码重构的方式,展示我们是怎么样一步一步地解决问题,并选择visitor模式作为重构的终点。 #1.问题域   visitor模式用于遍历一群对象,这些对象按某种结构组织在一起(List, Set, Tree等)。这种场景常常面临的问题是:    组织里的对象的类型彼此不同,遍历者要根据不同的类型使用不同的逻辑,导致代码里频繁使用if语句,可读性、可维护性都会比较差 //这个方法逐个打印每个Employee的称谓 private static void printTitle(Team team) { //一个Team里面的Employee有两种类型:Manager, 或Worker for (Employee employee : team.getEmployees()) { if (employee instanceof Manager) { System.out.println("Manager " + employee.getName()); } if (employee instanceof Worker) { System.out.println("Worker " + employee.getName()); } } }     代码详见 https://github.com/chenjianjx/learn-visitor-pattern/blob/master/1st-PlainSolution/src/learn/visitor/research/client/EmployeeClient.java   要消除if,最常见的方式就是把各个if里面的逻辑塞入到对象的各个子类中 #2.通过多态解决对象类型不同的问题 public class …

一步一步地理解Visitor模式 Read More »

用java attach api + java instrument api写java agent的注意事项

用java attach api + java instrument api写java agent的注意事项 1. 字节码的transformation一旦发生,就会一直生效;agent的detach或中止,并不会使transformation回滚。 2. 由于#1,对于一个目标进程,在允许retransform的条件下,如果先后两次启动agent,就会导致两次transformation的效果叠加起来。举例来说,如果你的transform是在某方法里插入一条日志语句,那如果你先后两次使用你的agent,就会导致被transform的类在执行时生成两条日志。 待续…..

jip和btrace分别是怎么暴露进程数据的?

jip 和 btrace 是两个著名的开源工具,它们都可以察看另一个jvm进程的信息:jip的客户进程可以打印另一个进程中每个方法的执行时间,btrace则可以直接查看甚至改变变量的值。 两个工具可以做的事都直接依赖java instrument api。通过这个api在目标进程运行时修改字节码,在一定程度上改变代码的执行:jip让目标进程给出执行时间,btrace让目标进程给出变量的值。 jip和btrace在执行时都有自己的进程。它们自己的进程接收目标进程释放的信息,然后展现出来。 现在的问题是,工具自己的进程是怎么样跟目标进程通信的? 经研究发现:   1. jip进程与目标进程是通过原生的socket进行通信的。目标进程在启动时须使用-javaagent jip-xxx.jar这个jvm参数(java instrument的“Command-Line Interface”)立即进行字节码instrument操作。具体的操作中包含一步:启动一个ServerSocket. jip进程启动后再访问这个ServerSocket.   2. btrace进程与目标进程则是通过java attach api进行通信的。通过指定一个进程号,btrace进程可以拿到目标进程的句柄。这个句柄是VirtualMachine类的实例,而这个类有一个方法叫loadAgent(),通过这个方法,btrace进程再把预先准备好的agent注入到目标进程实现字节码的动态修改(java instrument的"Starting Agents After VM Startup"模式) 优劣性比较   假设目标进程是java web应用,两者的优劣点可以从多个方面来比较。     1. 运行时的侵入性:btrace优于jip. jip需要在启动目标进程时就使用-javaagent参数,这可能导致贵公司服务器的启动脚本的改动。你们的运维人员未必愿意帮忙。     2. 能否instrument远程的进程:jip优于btrace,因为btrace所基于的java attach api只能捆绑本机的进程,而jip基于的socket没有这个限制。     3. 实现的简易性:btrace优于jip. 使用现成的attach api,当然比手工实现一套基于socket的c/s模型要省事。     4. 平台无关性: jip胜出,因为btrace基于的attach api有两大制约:        a. java6 以后才有这个api        …

jip和btrace分别是怎么暴露进程数据的? Read More »

javassist的一些注意事项

1. CtClass在toClass()或writeFile()之后会被冻结,冻结之后不可改动;如果要改,需要先通过ctClass.defrost()解冻 2. 当javassist的目标是servlet 容器里的app时,如何让ClassPool搜索web app里面的CtClass?  ClassLoader cl = Thread.currentThread().getContextClassLoader(); classPool.insertClassPath(new LoaderClassPath(cl)); 3.CtClass对象创建后会保存在ClassPool里以供后用,如果对象太多了就会导致内存问题。如果某个CtClass对象不需要再用了,你就应该显式地移除它:    a.ctClass.detach(),把ctClass从pool里移除    b.或者再执行一次 classPool = ClassPool.getDefault(),使原来的pool对象失去引用被gc掉,它里面的ctClass对象也会跟着gc掉 4.ctClass.getDeclaredMethod("hello")并不会去父类搜索hello方法

java instrument的一些注意事项

1. 若agent与应用程序同时启动,   a. agent类将由System Class Loader来装载   b. 如果agent不能装载,那么应用程序也不能启动   c. 如果agent的premain方法抛异常,应用程序也会被强制退出 2.ClassFileTransformer的 transform 发生在三种场合:     a. when classes are defined  (当ClassLoader.defineClass()调用时 (这个方法把字节码对应的byte[]变成Class对象))     b. when classes are redefined (当Instrumentation.redefineClasses()被调用时)     c. when classes are retransformed (当Instrumentation.retransformClasses()被调用时) (if canRetransform is true)    注意:servlet容器重启Context时会触发ClassLoader.defineClass(),从而触发ClassFileTransformer.transform();如果某class在重启之前就已经被transform()过,这时就会再做一次transform();如果transform()的内容是添加一个方法,这时就会把方法再增添一次,造成一个类里有两处重名的方法,从而导致编译错误 3. Class Loading有好几步,transform则会发生在字节码校验verfication之前 4. 一个agent可以有多个transformer,各个transformer会依次执行。即使其中一个transformer抛出异常,下一个transformer也仍然会执行 5. 关于异常   a.若agent在应用启动后才启动,如果异常一直往外抛没有处理,控制台并不会自动打印这个异常 (通过实验证明,文档并无描述)   …

java instrument的一些注意事项 Read More »

复习ibatis

Ibatis 相比于 spring jdbc template的好处: 1.把 column value <=> JavaBean这种映射放到XML里来做会比在java代码里来做方便(作者很谦虚地说这只是他个人的体会) 2.SQL里的parameter可以用#age#, #name#来表示,而不是问号; 这可以增强sql的可读性 3.可以方便地将ResultSet一步映射为组合对象(如类A里面关联了类B的实例作为属性),免于用java进行手工组装。这意味着你的数据层bean可以写成domain风格,而不是table model风格 4.可以把动态SQL的拼写放在XML里,维持一个相对整洁的外观。这个在多条件搜索时大有用武之地。 5.可以在XML里重用SQL片断,比在JAVA里重用要直观一些 ========================================== 一些边角的东西: 1.“null value replacement”可以让你设置:在参数值为空时,用某个值来填充 2. 尽量用resultMap,而不是resultClass. 看这里 3. 支持复合主键 4. 动态SQL拼写除了支持<isNull>和<isNotNull>,还支持<isPropertyAvailable>(对付多态对象有效),<isEmpty>(对付未trimToNull的字符串有效)和它们的反义词

ibatis的statement的命名空间问题

只要保证一个sql-map文件里面不重复就可以了: 原文是: 3.2.4.1. id The required id attribute provides a name for this statement, which must be unique within this <SqlMap>.