Hibernate性能调优(转载

Posted on

Hibernate性能调优(转载--作者:Robbin Fan)


一。**inverse = ?**

      inverse=false(default)

                  用于单向one-to-many关联

                  parent.getChildren().add(child) // insert child

                  parent.getChildren().delete(child) // delete child

       inverse=true

                  用于双向one-to-many关联

                  child.setParent(parent); session.save(child) // insert child

                   session.delete(child)

        在分层结构的体系中

         parentDao, childDao对于CRUD的封装导致往往直接通过session接口持久化对象,而很少通过关联对象可达性

二。**one-to-many**关系

            单向关系还是双向关系?

                 parent.getChildren().add(child)对集合的触及操作会导致lazy的集合初始化,在没有对集合配置二级缓存的情况下,应避免此类操作

               select /* from child where parent_id = xxx;

      性能口诀:

              1. 一般情况下避免使用单向关联,尽量使用双向关联

              2. 使用双向关联,inverse=“true”

              3. 在分层结构中通过DAO接口用session直接持久化对象,避免通过关联关系进行可达性持久化

三。**many-to-one**关系

     单向many-to-one表达了外键存储方

     灵活运用many-to-one可以避免一些不必要的性能问题

     many-to-one表达的含义是:0..n : 1,many可以是0,可以是1,也可以是n,也就是说many-to-one可以表达一对多,一对一,多对一关系

      因此可以配置双向many-to-one关系,例如:

            1.   一桌四人打麻将,麻将席位和打麻将的人是什么关系?是双向many-to-one的关系

四。**one-to-one**

        通过主键进行关联

        相当于把大表拆分为多个小表

        例如把大字段单独拆分出来,以提高数据库操作的性能

        Hibernate的one-to-one似乎无法lazy,必须通过bytecode enhancement

五。集合**List/Bag/Set**

        one-to-many

           1.    List需要维护index column,不能被用于双向关联,必须inverse=“false”,被谨慎的使用在某些稀有的场合

           2.      Bag/Set语义上没有区别

           3.       我个人比较喜欢使用Bag

       many-to-many

           1.      Bag和Set语义有区别

           2。   建议使用Set

六。集合的过滤

         1. children = session.createFilter(parent.getChildren(), “where this.age > 5 and   this.age < 10”).list()

     针对一对多关联当中的集合元素非常庞大的情况,特别适合于庞大集合的分页:

               session.createFilter(parent.getChildren(),“”).setFirstResult(0).setMaxResults(10).list();

在hibernate 中用 super.getSession().createFilter( , )

七。继承关系当中的隐式多态

       HQL: from Object

         1.     把所有数据库表全部查询出来

          2.     polymorphism=“implicit”(default)将当前对象,和对象所有继承子类全部一次性取出

          3.      polymorphism=“explicit”,只取出当前查询对象

八。**Hibernate**二级缓存

          著名的n+1问题:from Child,然后在页面上面显示每个子类的父类信息,就会导致n条对parent表的查询:

               select /* from parent where id = ?

               .......................

               select /* from parent where id = ?

          解决方案

                    1.      eager fetch

                     2.      二级缓存

九。**inverse**和二级缓存的关系

        当使用集合缓存的情况下:

             1.     inverse=“false”,通过parent.getChildren()来操作,Hibernate维护集合缓存

              2.    inverse=“true”,直接对child进行操作,未能维护集合缓存!导致缓存脏数据

              3.    双向关联,inverse=“true”的情况下应避免使用集合缓存

十。**Hibernate二级缓存是提升web**应用性能的法宝

          OLTP类型的web应用,由于应用服务器端可以进行群集水平扩展,最终的系统瓶颈总是逃不开数据库访问;

       哪个框架能够最大限度减少数据库访问,降低数据库访问压力, 哪个框架提供的性能就更高;针对数据库的缓存策略:

                1.        对象缓存:细颗粒度,针对表的记录级别,透明化访问,在不改变程序代码的情况下可以极大提升web应用的性能。对象缓存是ORM的制胜法宝。

                2.       对象缓存的优劣取决于框架实现的水平,Hibernate是目前已知对象缓存最强大的开源ORM

                3.        查询缓存:粗颗粒度,针对查询结果集,应用于数据实时化要求不高的场合

十一。应用场合决定了系统架构

一、是否需要ORM

Hibernate or iBATIS?

二、采用ORM决定了数据库设计

        Hibernate:

                倾向于细颗粒度的设计,面向对象,将大表拆分为多个关联关系的小表,消除冗余column,通过二级缓存提升性能(DBA比较忌讳关联关系的出现,但是ORM的缓存将突破关联关系的性能瓶颈);Hibernate的性能瓶颈不在于关联关系,而在于大表的操作

        iBATIS:

                倾向于粗颗粒度设计,面向关系,尽量把表合并,通过表column冗余,消除关联关系。无有效缓存手段。iBATIS的性能瓶颈不在于大表操作,而在于关联关系。

总结:

 性能口诀

           1、使用双向一对多关联,不使用单向一对多

           2、灵活使用单向多对一关联

           3、不用一对一,用多对一取代

           4、配置对象缓存,不使用集合缓存

           5、一对多集合使用Bag,多对多集合使用Set

           6、继承类使用显式多态

           7、表字段要少,表关联不要怕多,有二级缓存撑腰

最近开始留意项目中的Hibernate的性能问题,希望可以抽出时间学习一下hiberante的性能优化。主要是对数据库连接池技术、hibernate二级缓存、hibernate的配置优化等问题进行学习!

1.关联关系:

普通的关联关系:是不包括一个连接表,也就是中间表如:

create table Person(personId bigint not null primary key,addressId bigint not null)

create table Address(addressId bigint not null primary key)

也就是不会还有一个关系表如:

create table Person(personId bigint not null primary key)

create table Address(addressId bigint not null primary key)

create table PersonAddress(personId bigint not null,ddressId bigint not null primary key)

单向many-to-one关联是最常见的,而单向one-to-many是不常见的

  1. inner join(内连接)

left (outer) join(左外连接)

right (outer) join (右外连接)

full join (全连接,并不常用)

3.小技巧:

统计结果数目:

(Integer)session.iterator("select count(/*) from ..").next()).intValue();

根据一个集合大小来排序:

select user.id,user.name

from User as user.name

left join user.messages msg

group by user.id,user.name

having count(msg)>=1

activemq的网络层介绍

Posted on

activemq的网络层介绍

#

概要

activemq是一个apache的顶级项目,其实现了JMS规范,作为一个开源的JMS实现,activemq已经在很多地方得到了应用。同时,开源小组在研究JMS实现的时候,也选择了activemq作为研究对象,希望能够读其源码,让开源小组更好的明白JMS规范和实现。

在学习activemq的时候,我们发现还不能一下子就进去学习JMS规范的实现,而是需要了解其底层代码,包括其对网络连接的处理等等。所以,这篇文章就学习了activemq的网络层实现

activemq网络层概念

activemq 是支持多协议的,因此,把单一协议的server抽象成更高一层,就很有意义,请看下面这张图:

在activemq中,差不多每种概念都有生命周期(见Service接口)。 这张图十分简单,实际中当然比这个要复杂。下面针对每个概念进行叙述。

Transport

Transport 应该算是很底层的概念,他封装了 Socket,职责是发送和接受连接方的信息。 同时,在Transport中,增加了同步和异步的概念。 Transport拥有一个TransportListener。

TransportListener

顾名思义,TransportListener 就是监听 Transport 事件的发生以及做出一定的反映。 在activemq中,正是利用了 TransportListener 对 Command 进行处理 —— Command 是 activemq 网络传递的形式,每个请求都会被反序列化成一个 Command。

TransportServer

就是一个server实例,他会处理外部进来的Socket,进而封装成 Transport 。此 Server 拥有一个 TransportAcceptListener 。

TransportAcceptListener

负责 Accept Transport,将 Transport 封装成 Connection,从而开启 Connection 的生命周期。 这里需要注意的是:TransportServer 将 Socket 封装成 Transport,而 TransportAcceptListener 将 Transport 封装成 Connection。

Connection

可以理解成业务层面上的连接。 Connection 会给相应的 Transport 设置 TransportListener ,从而获取底层 Socket 的活动情况,进行业务的处理。

Connector

Connector就是较高层次的抽象,它代表对外的连接器,每个连接器都可以支持不同的协议。 Connector 通过给 TransportServer 设置 TransportAcceptListener ,从而能够控制 Transport 并产生相应的 Connection。

总结

总体来说,两个Listener是连接 Conncetor与TransportServer 和 Connection和Transport 的关键概念。 至于为什么要分成 Connector , TransportServer 这两层,我想原因应该就是开头提到的: activemq需要支持多协议,因此 Connector 抽象起来,可以当作是针对不同协议的统一行为;而 TransportServer 就是针对各协议的具体实现。 对于 Connection,Transport 来说, Connector 是与 Connection 统一层次, Transport 是与 TransportServer 统一层次。 如果读者有时间,还可以查看开源小组当时分析的activemq网络连接序列图(是基于TcpTransportServer的,图像有点大,需要耐心看)

来源: [http://www.goldendoc.org/2011/09/activemq-network-process/](http://www.goldendoc.org/2011/09/activemq-network-process/)

本文会把主要篇幅集中在这几个组件的创建时期,运行时期来讲解,最后会总结一下这几个组建使用的线程情况。一下的分析是基于 bio 的(nio 也是在这个结构当中,只是行为是基于 nio 特点的)。在activemq的网络层介绍(一)也讲过,activemq中很多组件都实现了 Service 接口,而 Service 接口的作用就是赋予实现者有生命周期的意义。

TransportConnector

TransportConnector 是 Connector 的实现,是基于 Transport 的 Connector(目前activemq的Connector实现,也就是基于Transport的)。其内部依赖了 TransportServer ,这个也容易理解,因为这个 Connector 是基于 Transport 的。因此 TransportServer 也是其所重点依赖的组件。 值得注意的是 TransportServer 的 AcceptListener 也是在 TransportConnector 中指定的,具体见下文。

TransportConnector 的创建

TransportConnector 的创建比较简单,直接接受传入进来的 TransportServer (关于 TransportServer 的创建,详见下文)。

TransportConnector 的运行

TransportConnector 是一个 Service,有自己的生命周期。因此,在 activemq 开始的时候,其生命周期,会被启动(具体表现为被调用 start 方法)。

在 TransportConnector 的 start 方法中,可以很明显的看到两个主要的逻辑:为 TransportServer 设置一个匿名的 AcceptListener ,调用 TransportServer 启动。

对于 TransportConnector 为 TransportServer 设置的 AcceptListener,其逻辑主要是当 TransportServer 接收到传输进来的 Transport(由 TransportServer 将 Socket 封装成 Transport)之后,将 Transport 封装成 Connection ,并且启动 Connection 的生命周期。这里也不难理解:在activemq网络层介绍(一)中也讲过, Connector 是与 Connection 一个层次的,TransportServer 与 Transport 是一个层次的。因此,将 AcceptListener 的匿名实现放在 TransportConnector 中,也就很好理解了。

Connection

在 TransportServer 的 AcceptListener 收到传输进来的 Transport 之后,就创建了 Connection(实际类型是 TransportConnection),然后为 Connection 开启生命周期。 但一个Socket是从什么时候传进来,进而被封装成 Transport 的,会在下文有描述。现在让我们跟着 TransportConnection ,看看他是如何被创建的,以及在它的生命周期中,会做什么事情。

TransportConnection 的创建

TransportConnection 是在 AcceptListener 收到 TransportServer 传进来的 Transport 之后创建的。可以想象,TransportConnection 是封装了 Transport 的。同时, Transport 的 TransportListener ,也是在 TransportConnection 的构造方法中进行了创建。此 TransportListener 的逻辑就是服务一个 Command (Command是 activemq 实例之间交互的基本对象,任何请求和响应都被activemq 序列化成一个 Command),在得到响应的时候,进行分发(响应其实也是一个Command)。

TransportConnection 的运行

在开始说 TransportConnection 的生命周期之前,可以先说一下 activemq 的 Task 接口,此接口有一个 iterate 遍历方法,activemq 一般对这个接口的用法都是使用一个线程去跑 iterater 方法,直到 iterater 返回 false 。一般的,iterater 方法里面都是在重复的做一段逻辑,只不过是这个逻辑实施的对象不一样而已。TransportConnection 正是实现了此接口。

在 TransportConnection 的 start 方法中,主要做了两件事情:创建一个 taskRunner ,以做好跑 Task 的准备(就是在另起线程跑自己的 Iiterater 方法);调用 Transport 的 Start 方法,开启 Transport 的生命周期。

在 TransportConnection 自己的 Iiterater 方法中,就是到一个队列中取出 Command,并且使用 Transport 的 oneWay,将 Command 发送出去。

TransportServer

按理说要接下来讲 Transport,但如果不先把 TransportServer 弄清出的话, Transport 还是有些难以理解的。因为是 TransportServer 将 接受到的 Socket 封装成 Transport。这里说的是 TransportServer 的实现, TcpTransportServer。

TcpTransportServer 的创建

TransportServer 是由 TransportFactory 创建的。TransportFactory,不同的 TransportFactory 创建出不同的 TransportServer,而 TcpTransportServer 则是由 TcpTransportFactory 创建的。关于 TransportFactory 如何创建相应的 TransportServer,activemq使用了一套基于分析参数和配置文件来实现,说起来很高深,其实很朴实的,有兴趣也可以了解一下,这里不做深入。

TcpTransportServer 的运行

这里主要使用 TcpTransportServer,TcpTransportServer 生命周期,是通过 TransportConnector 来开启的,这点在上面已经有所讲述。

这里要说一下 TransportServerThreadSupport,此类的作用是支持 TransportServer 能够开启另外一个线程,只要实现了 TransportServer Runnable,在 start 的时候,就能够开启另外一个线程,这也是 TransportServerThreadSupport 命令的含义。同样的机制也出现在 TransportThreadSupport 中。

现在来看一下 TcpTransportServer 的 start : TcpTransportServer 会开启两个线程,包括:

  1. 实现了Runnable的线程,此线程的工作是不停的 accept 外部的 socket,然后将 socket 扔到一个队列中。简单来说,此线程是一个 acceptor。
  2. 另外一个线程的工作是,从队列中取出 socket ,并且将 WireFormat( WireFormat 是在 activemq 中负责序列化和发序列化的组件) 和socket 封装成一个 Transport ,然后将创建好的 Transport 通知到 AcceptListener 中。

Transport

这里也主要讲 TcpTransport。

TcpTransport 的创建

上面已经讲到,TcpTransport 是在 TcpTransportServer 中创建,并扔给了 AcceptListener 。并且,TcpTransport 也支持 TransportThreadSupport 的形式,可以另起一个线程。

TcpTransport 的运行

TcpTransport 开始执行之后,专注于做两个事情:

  1. 初始化 socket,并且把 socket 的流给设置起来。
  2. 另起一个线程,利用 wireFormat 将 socket 传过来的字节序列转化成一个 command ,然后将 Command 扔给 transportListener 。transportListener 所做的事情就是进行业务处理,然后将得到的 response (也是一个 command)通过 tcpTransport 的 oneway 传给对方。

这样,这个网络通讯的过程就完成了。下面我们来总结一下 TcpTransportServer 的线程模型。

TcpTransportServer 的线程模型

如图所示: activemq网络线程模型

activemq网络线程模型

总结

从上图中可以看出,accpetor 和 sockethandler 线程是在整个 transportServer 中的,而 ThreadPool 中的线程不会进行消息的发送,相反,当需要发送消息的时候,总是会创建新的线程去发送。这也是为了 ThreadPool 能将自己的线程利用率提高。因为这是 bio 的网络,如果消息的发送是利用线程池中的线程来发送的,那么很有可能线程池中的线程会在发送的时候阻塞,因为对方的网络不可预知。因此,新建一个新的线程,利用新建的线程发送消息,则是很明智的选择。 来源: [http://www.goldendoc.org/2011/09/activemq-network-process-2/](http://www.goldendoc.org/2011/09/activemq-network-process-2/)

Java反射机制剖析

Posted on

Java反射机制剖析

(一)-定义和API

  1. 什么是Java反射机制 Java的反射机制是在程序运行时,能够完全知道任何一个类,及其它的属性和方法,并且能够任意调用一个对象的属性和方法。这种运行时的动态获取就是Java的反射机制。其实这也是Java是动态语言的一个象征。

用一句话来概括反射就是加载一个运行时才知道的类以及它的完整内部结构。

  1. 为什么要有Java反射机制 我们为什么要用Java的反射机制呢?

我认为有两种:

第一种:反射的目的就是为了扩展未知的应用。比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的dll都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把dll加载进内存,然后通过反射的方式来调用dll中的方法。

第二种:在编码阶段不知道那个类名,要在运行期从配置文件读取类名, 这时候就没有办法硬编码new ClassName(),而必须用到反射才能创建这个对象。

一个生活中常看到的例子有助于理解我们为什么要用Java的反射机制:你进了一家饭店,你不知道他们都有那些菜,要多少钱。那么你第一件事情是干啥“服务员拿个菜单过来”,然后指着菜单说“我要这个,我要那个”。

  1. 一起来看ReflectionAPI

在生活中,我们使用一个未知的东西的时候总会用帮助来解决我们的使用问题,电视机有帮助,电脑有帮助,几乎所有的事物都携带着它的一本帮助,Java的反射机制也不例外。

在JDK中有Reflection API的帮助,它主要说明了什么是Java反射机制,这种反射机制提供了什么样的属性和方法,进一步我们能够知道能够通过它完成什么样的工作。

下面咱就一起看看这部分的API。这些接口和类都位于lang包里。

如图:

接口:

类:

简单介绍一些类和接口的用法。

1) Member成员是一种接口,反映有关单个成员(字段或方法)或构造方法的标识信息

2) InvocationHandler是代理实例的调用处理程序 实现的接口(这个接口的具体用法等到java反射机制剖析4着重介绍)

3) Method提供一个类的方法的信息以及访问类的方法的接口。

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Method;
  2. public class TestMethod {
  3. ///
  4. /* @param args
  5. /* @throws Exception
  6. /*/
  7. public static void main(String[] args) throws Exception {
  8. // TODO Auto-generated method stub
  9. Class classType=Class.forName(args[0]);
  10. Method methods[]=classType.getDeclaredMethods();
  11. for(int i=0;i<methods.length;i++){
  12. System.out.println(methods[i].toString());
  13. }
  14. }
  15. }

4) Filed提供一个类的域的信息以及访问类的域的接口。

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Field;
  2. public class TestField {
  3. ///
  4. /* @param args
  5. /* @throws Exception
  6. /*/
  7. public static void main(String[] args) throws Exception {
  8. // TODO Auto-generated method stub
  9. Class classType=Class.forName(args[0]);
  10. Field[] fields = classType.getFields();
  11. for(int i=0;i<fields.length;i++){
  12. System.out.println(fields[i].toString());
  13. }
  14. }
  15. }

5) Array 类提供了动态创建和访问 Java 数组的方法。

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Array;
  2. public class TestArray {
  3. public TestArray(){
  4. }
  5. ///
  6. /* @param args
  7. /* @throws Exception
  8. /*/
  9. public static void main(String[] args) throws Exception {
  10. Class<?> classType = Class.forName("java.lang.String");
  11. Object array = Array.newInstance(classType, 10);
  12. Array.set(array, 5, "hello");
  13. String s = (String)Array.get(array, 5);
  14. System.out.println(s);
  15. }
  16. }

6) Proxy提供动态地生成代理类和类实例的静态方法(这个方法在java放射机制剖析4着重介绍)。

其余的类和接口的使用方法详见API 来源: [http://blog.csdn.net/lfsf802/article/details/7239652](http://blog.csdn.net/lfsf802/article/details/7239652)

(二)-功能以及举例

《java反射机制剖析(一)》的API我们看到了许多接口和类,我们能够通过这些接口做些什么呢?

从上篇API中我们能看到它能够完成下面的这些功能:

1) 获得类 A. 运用getClass() (每个class都有此函数)

                                i.    String str = "abc";

                              ii.    Class c1 = str.getClass();

B. 运用Class.forName() (static method 最常被使用)

                                i.    Class c1 = Class.forName ("java.lang.String");

                              ii.    Class c2 = Class.forName ("java.awt.Button");

C. 运用 .class 语法

                                i.    Class c1 = String.class;

                              ii.    Class c2 = java.awt.Button.class;

                            iii.    Class c4 = int.class;

                              iv.    Class c5 = int[].class;

D. 运用 TYPE语法 ( primitive wrapper classes 基本数据类型包装类的 TYPE语法)

                                i.    Class c1 = Boolean.TYPE;

2) 获得属性(这个功能的实例见Java反射机制剖析(2)) 以下四个方法可以获得属性,主要参见的是Java.lang.class

Public Field getField

(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段 public Field[] getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段 Public Field

getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段 public Field[]

getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段

3) 获得方法() 以下四个方法可以获得方法,要参见的是Java.lang.class

public Method

getMethod(String name,

... )

返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法 public Method[] getMethods()

返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法 public Method

getDeclaredMethod(String name,…)

返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法 public Method[]

getDeclaredMethods()

返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

4) 获取类的构造器 以下四个方法可以获得方法,要参见的是Java.lang.class

public Constructor

getConstructor(Class<?>... )

返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法 public Constructor<?>[]

getConstructors()

返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法 Public Constructor

getDeclaredConstructor(Class<?>...)

返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法 public Constructor<?>[]

getDeclaredConstructors()

返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法 示例代码:

[java] view plaincopyprint?

  1. import java.lang.reflect.Constructor;
  2. public class TestConstructor {
  3. ///
  4. /* @param args
  5. /* @throws ClassNotFoundException
  6. /* @throws SecurityException
  7. /* @throws NoSuchMethodException
  8. /*/
  9. public static void main(String[] args) throws Exception {
  10. // TODO Auto-generated method stub
  11. Class classType=Class.forName(args[0]);
  12. Constructor Constructor= classType.getConstructor();
  13. System.out.println(Constructor.toString());
  14. }
  15. }

5) 新建类的实例

调用类的Class对象的newInstance方法

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Constructor;
  2. public class TestConstructor {
  3. ///
  4. /* @param args
  5. /* @throws ClassNotFoundException
  6. /* @throws SecurityException
  7. /* @throws NoSuchMethodException
  8. /*/
  9. public static void main(String[] args) throws Exception {
  10. // TODO Auto-generated method stub
  11. Class classType=Class.forName(args[0]);
  12. //Constructor Constructor= classType.getConstructor();
  13. Object inst=classType.newInstance();
  14. System.out.println(inst);
  15. }
  16. } 调用默认Constructor对象的newInstance方法

示例:

[java] view plaincopyprint?

  1. import java.lang.reflect.Constructor;
  2. public class TestConstructor {
  3. ///
  4. /* @param args
  5. /* @throws ClassNotFoundException
  6. /* @throws SecurityException
  7. /* @throws NoSuchMethodException
  8. /*/
  9. public static void main(String[] args) throws Exception {
  10. // TODO Auto-generated method stub
  11. Class classType=Class.forName(args[0]);
  12. Constructor Constructor= classType.getConstructor();
  13. Object inst=Constructor.newInstance();
  14. System.out.println(inst);
  15. }
  16. } 调用带参数Constructor对象的newInstance方法

示例:

[java] view plaincopyprint?

  1. Class classType=User.class
  2. Constructor constructor2 =
  3. classType.getDeclaredConstructor(int.class, String.class);
  4. Object inst = constructor2.newInstance(1, "123");
  5. System.out.println(inst);

来源: [http://blog.csdn.net/lfsf802/article/details/7239711](http://blog.csdn.net/lfsf802/article/details/7239711)

(三)-简单谈谈动态代理 通过Java反射机制剖析(一)Java反射机制剖析(二)的学习,已经对反射有了一定的了解,这一篇通过动态代理的例子来进一步学习反射机制。

  1. 代理模式

代理模式就是为其他对象提供一种代理来控制对这个对象的访问。其实代理模式是在访问的对象时引入一定程度的间接性,这种间接性可以附加多种用途。

它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

  1. 分类

代理类按照创建时期可以分为两种,静态代理类和动态代理类。

静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

动态代理类:在程序运行时,运用反射机制动态创建而成。

  1. 静态代理和动态代理举例

静态代理:

业务接口类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public interface UserManager {
  3. public void addUser(String userId, String userName);
  4. public void delUser(String userId);
  5. public void modifyUser(String userId, String userName);
  6. public String findUser(String userId);
  7. }
    业务接口实现类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public class UserManagerImpl implements UserManager {
  3. public void addUser(String userId, String userName) {
  4. //System.out.println("start-->>addUser() userId-->>" + userId);
  5. try {
  6. System.out.println("UserManagerImpl.addUser() userId-->>" + userId);
  7. //System.out.println("success-->>addUser()");
  8. }catch(Exception e) {
  9. e.printStackTrace();
  10. //System.out.println("error-->>addUser()");
  11. throw new RuntimeException();
  12. }
  13. }
  14. public void delUser(String userId) {
  15. System.out.println("UserManagerImpl.delUser() userId-->>" + userId);
  16. }
  17. public String findUser(String userId) {
  18. System.out.println("UserManagerImpl.findUser() userId-->>" + userId);
  19. return "张三";
  20. }
  21. public void modifyUser(String userId, String userName) {
  22. System.out.println("UserManagerImpl.modifyUser() userId-->>" + userId);
  23. }
  24. }
    业务代理类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public class UserManagerImplProxy implements UserManager {
  3. private UserManager userManager;
  4. public UserManagerImplProxy(UserManager userManager) {
  5. this.userManager = userManager;
  6. }
  7. public void addUser(String userId, String userName) {
  8. try {
  9. System.out.println("start-->>addUser() userId-->>" + userId);
  10. userManager.addUser(userId, userName);
  11. System.out.println("success-->>addUser()");
  12. }catch(Exception e) {
  13. e.printStackTrace();
  14. System.out.println("error-->>addUser()");
  15. }
  16. }
  17. public void delUser(String userId) {
  18. }
  19. public String findUser(String userId) {
  20. return null;
  21. }
  22. public void modifyUser(String userId, String userName) {
  23. }
  24. }
    客户端类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public class Client {
  3. ///
  4. /* @param args
  5. /*/
  6. public static void main(String[] args) {
  7. //UserManager userManager = new UserManagerImpl();
  8. UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());
  9. userManager.addUser("0001", "张三");
  10. }
  11. }
    运行结果:

[java] view plaincopyprint?

  1. start-->>addUser() userId-->>0001
  2. UserManagerImpl.addUser() userId-->>0001
  3. success-->>addUser()

动态代理:

业务接口类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public interface UserManager {
  3. public String test(String userId);
  4. }
    业务接口实现类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. public class UserManagerImpl implements UserManager {
  3. public String test(String userId) {
  4. System.out.println("UserManagerImpl.findUser() userId-->>" + userId);
  5. return "张三";
  6. }
  7. }
    BusinessHandler类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. public class BusinessHandler implements InvocationHandler {
  6. private Object targetObject;
  7. public Object newProxyInstance(Object targetObject) {
  8. this.targetObject = targetObject;
  9. return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
  10. targetObject.getClass().getInterfaces(), this);
  11. }
  12. public Object invoke(Object proxy, Method method, Object[] args)
  13. throws Throwable {
  14. System.out.println("start-->>" + method.getName());
  15. for (int i=0; i<args.length; i++) {
  16. System.out.println(args[i]);
  17. }
  18. Object ret = null;
  19. try {
  20. //调用目标方法
  21. ret = method.invoke(targetObject, args);
  22. System.out.println("success-->>" + method.getName());
  23. }catch(Exception e) {
  24. e.printStackTrace();
  25. System.out.println("error-->>" + method.getName());
  26. throw e;
  27. }
  28. return ret;
  29. }
  30. }
    客户端类:

[java] view plaincopyprint?

  1. package com.bjpowernode.pattern;
  2. import java.lang.reflect.Field;
  3. public class Client {
  4. ///
  5. /* @param args
  6. /*/
  7. public static void main(String[] args) {
  8. BusinessHandler businessHandler = new BusinessHandler();
  9. UserManager userManager = (UserManager)businessHandler.newProxyInstance(new UserManagerImpl());
  10. //userManager.addUser("0001", "张三");
  11. //userManager.delUser("0001");
  12. // System.out.println(userManager.getClass().getName());
  13. String name = userManager.test("0001");
  14. //String name = ((UserManagerImpl) logHandler.newProxyInstance(new UserManagerImpl())).test("0001");
  15. System.out.println("Client.main() --- " + name);
  16. }
  17. }
    运行结果:

[java] view plaincopyprint?

  1. start-->>test
  2. 0001
  3. UserManagerImpl.findUser() userId-->>0001
  4. success-->>test
  5. Client.main() --- 张三

来源: [http://blog.csdn.net/lfsf802/article/details/7239766](http://blog.csdn.net/lfsf802/article/details/7239766)

(四)-深度剖析动态代理原理及总结 动态代理类原理(示例代码参见java反射机制剖析(三)) a) 理解上面的动态代理示例流程

b) 代理接口实现类源代码剖析

咱们一起来剖析一下代理实现类($Proxy0)的源代码和整个动态代理的流程。

$Proxy0生成的代码如下:

[html] view plaincopyprint?

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. import java.lang.reflect.UndeclaredThrowableException;
  5. public final class $Proxy0 extends Proxy implements Manager {
  6. private static Method m1;
  7. private static Method m0;
  8. private static Method m3;
  9. private static Method m2;
  10. static {
  11. try {
  12. m1 = Class.forName("java.lang.Object").getMethod("equals",
  13. new Class[] { Class.forName("java.lang.Object") });
  14. m0 = Class.forName("java.lang.Object").getMethod("hashCode",
  15. new Class[0]);
  16. m3 = Class.forName("com.ml.test.Manager").getMethod("test",
  17. new Class[0]);
  18. m2 = Class.forName("java.lang.Object").getMethod("toString",
  19. new Class[0]);
  20. } catch (NoSuchMethodException nosuchmethodexception) {
  21. throw new NoSuchMethodError(nosuchmethodexception.getMessage());
  22. } catch (ClassNotFoundException classnotfoundexception) {
  23. throw new NoClassDefFoundError(classnotfoundexception.getMessage());
  24. }
  25. }
  26. public $Proxy0(InvocationHandler invocationhandler) {
  27. super(invocationhandler);
  28. }
  29. @Override
  30. public final boolean equals(Object obj) {
  31. try {
  32. return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
  33. .booleanValue();
  34. } catch (Throwable throwable) {
  35. throw new UndeclaredThrowableException(throwable);
  36. }
  37. }
  38. @Override
  39. public final int hashCode() {
  40. try {
  41. return ((Integer) super.h.invoke(this, m0, null)).intValue();
  42. } catch (Throwable throwable) {
  43. throw new UndeclaredThrowableException(throwable);
  44. }
  45. }
  46. public final void test() {
  47. try {
  48. super.h.invoke(this, m3, null);
  49. return;
  50. } catch (Error e) {
  51. } catch (Throwable throwable) {
  52. throw new UndeclaredThrowableException(throwable);
  53. }
  54. }
  55. @Override
  56. public final String toString() {
  57. try {
  58. return (String) super.h.invoke(this, m2, null);
  59. } catch (Throwable throwable) {
  60. throw new UndeclaredThrowableException(throwable);
  61. }
  62. }
  63. }

引入眼帘的是这个代理接口实现类实现了业务类的接口(也就是例子中的UserManager接口),又继承了基类Proxy类;

接着就是构造函数,在构造方法中把BusinessHandler传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值(这里要看Proxy的构造方法);

随后看到的就是这个类重写了Proxy类的Equals、hashCode、toString方法,又实现了业务类接口的方法(即UserManager的test方法),具体重写和实现都是用到的super.h.invoke(即Proxy.h.invoke)这个方法。

简单分析完这个代理接口实现类,咱们下面来整体看一下这个动态代理是怎么实现的:

首先客户端初始化了BusinessHandler类,调用这个类的newProxyInstance(new UserManagerImpl())方法来初始化了上面的代理接口实现类;

接下来代理接口实现类通过构造函数把BusinessHandler传过去(也就是代码中的this),并通过Proxy的构造函数给h赋值;

随后再客户端就能实例化出代理接口实现类$Proxy0,我们把它强制转换为业务实现接口(UserManager)类型的(为什么要强制转换,这里非常有意思,如果不强制转换就会报错,这里很好解释,因为当前的环境根本不会知道这个代理接口实现类$Proxy0既继承Proxy又实现业务实现接口UserManager,但是强制转换成UserManager它是可以做到的,因为当前环境中就有UserManager。这就是反射的厉害之处,可以在运行时动态调用任何一个类并可以使用这个类的具体细节。);

之后当我们调用test方法的时候其实是调用了$Proxy0中的test方法,这个方法的实现是通过Proxy.h的invoke方法实现的(即调用了BusinessHandler.invoke方法);

之后在调用了Method的invoke方法(这时的参数是this,和args)。

这样就调用了UserManagerImpl的对应方法,之后返回给客户端。

到此就完成了整个的调用关系。

反射,反射,程序员的快乐 通过上篇文章对动态代理进行了深度剖析,现在想起来还感觉非常有意思,这里面其实最根本的机制就是反射机制,运行时动态实例化任何一个类,并且调用它的具体细节。现在反看动态代理的示例,其实发现这里最关键的还是在就在Proxy.newProxyInstance(..)方法执行时生成了$Proxy0的内存字节码这一点上,当我们有了内存字节码,我们的反射就会大显威力,这样才有了我们之后的一系列的调用关系。

通过反射机制的分析和动态代理示例的剖析,发现编程是一件多么有意思的事情,以至于我们沉浸其中不能自拔。

最后总结一下:反射,反射,程序员的快乐! 来源: [http://blog.csdn.net/lfsf802/article/details/7239799](http://blog.csdn.net/lfsf802/article/details/7239799)

Spring

Posted on

Spring-设计模式-aop-ioc

黄金档

路漫漫其修远兮,吾将上下而求索……

Spring

Spring中的设计模式

2

2年前

jackey发布 在Spring 应该说设计模式是我们在写代码时候的一种被承认的较好的模式。好的设计模式就像是给代码造了一个很好的骨架,在这个骨架里,你可以知道心在哪里,肺在哪里,因为大多数人都认识这样的骨架,就有了很好的传播性。这是从易读和易传播来感知设计模式的好处。当然设计模式本身更重要的是设计原则的一种实现,比如开闭原则,依赖倒置原则,这些是在代码的修改和扩展上说事。说到底就是人类和代码发生关系的四种场合:阅读,修改,增加,删除。让每一种场合都比较舒服的话,就需要用设计模式。

下面来简单列举Spring中的设计模式: 1. 简单工厂 又叫做静态工厂方法(StaticFactory Method)模式,但不属于23种GOF设计模式之一。 简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。 Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。 2. 工厂方法(Factory Method) 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。 Spring中的FactoryBean就是典型的工厂方法模式。如下图:

3. 单例(Singleton) 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 Spring中的单例模式完成了后半句话,即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例,这是因为Spring管理的是是任意的Java对象。 4. 适配器(Adapter) 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 Spring中在对于AOP的处理中有Adapter模式的例子,见如下图: 由于Advisor链需要的是MethodInterceptor对象,所以每一个Advisor中的Advice都要适配成对应的MethodInterceptor对象。 5.包装器(Decorator) 动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。 **

Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。 6. 代理(Proxy) 为其他对象提供一种代理以控制对这个对象的访问。 从结构上来看和Decorator模式类似,但Proxy是控制,更像是一种对功能的限制,而Decorator是增加职责。 Spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。 7.观察者(Observer) 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 Spring中Observer模式常用的地方是listener的实现。如ApplicationListener。 8. 策略(Strategy) 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。 Spring中在实例化对象的时候用到Strategy模式,见如下图: 在SimpleInstantiationStrategy中有如下代码说明了策略模式的使用情况: 9.模板方法(Template Method) 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 Template Method模式一般是需要继承的。这里想要探讨另一种对Template Method的理解。Spring中的JdbcTemplate,在用这个类时并不想去继承这个类,因为这个类的方法太多,但是我们还是想用到JdbcTemplate已有的稳定的、公用的数据库连接,那么我们怎么办呢?我们可以把变化的东西抽出来作为一个参数传入JdbcTemplate的方法中。但是变化的东西是一段代码,而且这段代码会用到JdbcTemplate中的变量。怎么办?那我们就用回调对象吧。在这个回调对象中定义一个操纵JdbcTemplate中变量的方法,我们去实现这个方法,就把变化的东西集中到这里了。然后我们再传入这个回调对象到JdbcTemplate,从而完成了调用。这可能是Template Method不需要继承的另一种实现方式吧。

以下是一个具体的例子: JdbcTemplate中的execute方法:

JdbcTemplate执行execute方法:

设计模式

Spring AOP介绍及源码分析

3

2年前

lwei发布 在Spring 软件开发经历了从汇编语言到高级语言和从过程化编程到面向对象编程;前者是为了提高开发效率,而后者则使用了归纳法,把具有共性的东西进行归类并使之模块化,达到便于维护和扩展的目的;如果说面向对象编程可以对业务需求进行很好的分解使之模块化;那么面向切面编程AOP(Aspect-Oriented Programming)则可以对系统需求进行很好的模软件开发经历了从汇编语言到高级语言和从过程化编程到面向对象编程;前者是为了提高开发效率,而后者则使用了归纳法,把具有共性的东西进行归类并使之模块化,达到便于维护和扩展的目的;如果说面向对象编程可以对业务需求进行很好的分解使之模块化;那么面向切面编程AOP(Aspect-Oriented Programming)则可以对系统需求进行很好的模块组织,简化系统需求和实现之间的对比关系,是对OOP思想的一种补充;块组织,简化系统需求和实现之间的对比关系,是对OOP思想的一种补充;

一、AOP介绍

举个例子来说明一下吧!现在系统中有很多的业务方法,如上传产品信息、修改产品信息、发布公司库等;现在需要对这些方法的执行做性能监控,看每个业务方法的执行时间;在不改变原业务代码的基础上,也许我们会这么做:

Offer接口:

Offer实现:

Offer代理:

我们要通过下面的方式来使用:

上面的例子的输出为:

上面的例子中,OfferProxy实现了IOffer,而所有的业务实现均委托给其成员offer;可以想像,这应该就是最简单的AOP的实现了;但这种方式会存在一个问题:如果有非常多的这种业务对象需要性能监控,我们就需要写同样多的XyzProxy来满足需求,这也是非常巨大的工作量。

上面说到了代理,我们先看看代理模式吧!

二、代理模式及实现

下面是代理模式的类图:

代理模式类图

代理模式中,存在一个称为ProxyObject的代理对象和RealObject的真实对象,它们都实现了相同的接口;在调用的地方持有ProxyObject的实例,当调用request()方法时,ProxyObject可以在执行RealObject.request()前后做一些特定的业务,甚至不调用RealObject.request()方法。

目前实现代理模式的方式有两种:基于JDK的动态代理和基于CGLIB字节码的代理。

2.1 JDK动态代理

JDK动态代理,顾名思义,是基于JDK的反射(reflect)机制;在JDK中,提供了InvocationHandler这个接口,下面是JDK里面的注释:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

简单翻译,意思是说:该接口由被代理对象的handler所实现;当调用代理对象的方法时,该方法调用将被编码,然后交给代理对象的invoke方法去执行;

是不是有一种豁然开朗的感觉呢?没错,答案就在你心中。

这样,上面的代码就可以改成下面的实现方式:

**

调用端:

通过这种方式,你不需要为针对每一个业务写一个代理对象,就可以很轻松地完成你的需求;但也许你已经注意到了,JDK的动态代理,在创建代理对象(上面红色代码部分)时,被代理的对象需要实现接口(即面向接口编程);

这就是JDK的动态代理,简单吧!下面看看CGLIB代理方式。

2.2 CGLIB代理

如果目标对象没有实现任何接口,那怎么办呢?不用担心,你可以用CGLIB来实现代理:

调用端:

使用CGLIB创建的代理对象,其实就是继承了要代理的目标类,然后对目标类中所有非final方法进行覆盖,但在覆盖方法时会添加一些拦截代码(上面CglibProxyFactory类中的intercept方法)。

下面看看Spring中是如何实现AOP的。

三、Spring AOP的实现

3.1 Spring AOP的几个概念

Spring AOP中的几个基本概念,每次学习AOP都被这几个概念折腾的很不爽,我们在这里再把这几个概念描述一遍,力争把这几个概念搞清,在每次review这块内容的时候可以很快上手。

  1. 切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;
  2. 连接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;
  3. 通知(Advice):通知就是在切面的某个连接点上执行的操作,也就是事务管理、日志管理等;
  4. 切入点(Pointcut):切入点就是描述某一类选定的连接点,也就是指定某一类要织入通知的方法;
  5. 目标对象(Target):就是被AOP动态代理的目标对象;

用一张图来形象地表达AOP的概念及其关系如下:

3.2 Spring AOP中切入点、通知、切面的实现

理解了上面的几个概念后,我们分别来看看Spring AOP是如何实现这些概念的;

  1. 切入点(Pointcut):它定义了哪些连接点需要被织入横切逻辑;在Java中,连接点对应哪些类(接口)的方法。因此,我们都能猜到,所谓的切入点,就是定义了匹配哪些娄的哪些方法的一些规则,可以是静态的基于类(方法)名的值匹配,也可以是基于正则表达式的模式匹配。来看看Spring AOP Pointcut相关的类图:

在Pointcut接口的定义中,也许你已经想到了,ClassFilter是类过滤器,它定义了哪些类名需要拦截;典型的两个实现类为TypePatternClassFilterTrueClassFilter(所有类均匹配);而MethodMatcher为方法匹配器,定义哪些方法需要拦截。

在上面的类图中:

  • StaticMethodMatch与DynamicMethodMatch的区别是后者在运行时会依据方法的参数值进行匹配。
  • NameMatchMethodPointCut根据指定的mappedNames来匹配方法。
  • AbstractRegexpMethodPointCut根据正则表达式来匹配方法。
  1. 通知(Advice):通知定义了具体的横切逻辑。在Spring中,存在两种类型的Advice,即per-class和per-instance的Advice。

所谓per-class,即该类型的Advice只提供方法拦截,不会为目标对象保存任何状态或者添加新的特性,它也是我们最常见的Advice。下面是per-class的类图:

  • BeforeAdvice:在连接点前执行的横切逻辑。
  • AfterReturningAdvice:在连接点执行后,再执行横切逻辑。
  • AfterAdvice:一般由程序自己实现,当抛出异常后,执行横切逻辑。
  • AroundAdvice:Spring AOP中并没有提供这个接口,而是采用了AOP Alliance的MethodInteceptor接口;通过看AfterReturningAdvice的源码我们知道,它是不能更改连接点所在方法的返回值的(更改引用);但使用的MethodInteceptor,所有的事情,都不在话下。

在上面的类图中,还有两种类没有介绍,那就是///AdviceAdapter和///AdviceInteceptor,我们以AfterReturningAdviceInterceptor为例来说明:

该类实现了MethodInterceptor和AfterAdvice接口,同时构造函数中还有一个AfterReturningAdvice实例的参数;这个类存在的作用是为了什么呢?对,没错,Spring AOP把所有的Advice都适配成了MethodInterceptor,统一的好处是方便后面横切逻辑的执行(参看下一节),适配的工作即由///*AdviceAdapter完成;

哈哈,Spring AOP的代码也不过如此嘛:所谓的AfterReturningAdvice,通过适配成MethodInterceptor后,其实就是在invoke方法中,先执行目标对象的方法,再执行的AfterReturningAdvice所定义的横切逻辑。你现在明白它为什么不能修改返回值的引用了吧?

对于per-instance的Advice,目前只有一种实现,就是Introduction,使用的场景比较少,有兴趣的同学可以自己研究一下,呵呵!

  1. 切面(Aspect):在Spring中,Advisor就是切面;但与通常的Aspect不同的是,Advisor通常只有一个Pointcut和一个Advice,而Aspect则可以包含多个Pointcut和多个Advice,因此Advisor是一种特殊的Aspect。但,这已经够用了!

接下来看下per-class Advisor的类图:

其实没有什么好看的,前面已经说过,Advisor包含一个Pointcut和一个Advisor;在AbstractGenericPointcutAdvisor中,持有一个Advice的引用;下面的几个实现,均是针对前面提到的几种不同的Pointcut的实现。

3.3 Spring AOP实现的基本线索

我们选择ProxyFactoryBean作为入口点和分析的开始。ProxyFactoryBean是在Spring IoC环境中,创建AOP应用的最底层方法,从中,可以看到一条实现AOP的基本线索。

所有的逻辑从以下的方法开始,我们主要针对单例的代理对象的生成:

下面我们深入到SpringAOP核心代码的内部,看看代理对象的生成机制,拦截器横切逻辑以及织入的实现。

3.4 代理对象的生成

对于getSingletonInstance()方法返回了什么,这就是代理对象如何产生的逻辑了,然我们须根溯源,看看传说中的proxy到底是如何一步一步的产生的。

ProxyFactoryBean是AdvisedSupport的子类,Spring使用AopProxy接口把AOP代理的实现与框架的其他部分分离开来。在AdvisedSupport中通过这样的方式来得到AopProxy,当然这里需要得到AopProxyFactory的帮助 ,从JDK或者cglib中得到想要的代理对象:

这个DefaultAopProxyFactory是Spring用来生成AopProxy的地方,它包含JDK和Cglib两种实现方式。让我接着往里面看:

可以看到其中的代理对象可以由JDK或者Cglib来生成,JdkDynamicAopProxy类和Cglib2AopProxy都实现的是AopProxy的接口,我们进入JdkDynamicAopProxy实现中看看Proxy是怎样生成的:

用Proxy包装target之后,通过ProxyFactoryBean得到对其方法的调用就被Proxy拦截了, ProxyFactoryBean的getObject()方法得到的实际上是一个Proxy了,target对象已经被封装了。对 ProxyFactoryBean这个工厂bean而言,其生产出来的对象是封装了目标对象的代理对象。

3.5 拦截器的作用

前面分析了SpringAOP实现中得到Proxy对象的过程,接下来我们去探寻Spring AOP中拦截器链是怎样被调用的,也就是Proxy模式是怎样起作用的。 还记得在JdkDynamicAopProxy中生成Proxy对象的时候,有一句这样的代码吗?

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);**

这里我们的JdkDynamicAopProxy实现了InvocationHandler这个接口,this参数对应的是InvocationHandler对象,也就是说当 Proxy对象的函数被调用的时候,InvocationHandler的invoke方法会被作为回调函数调用:

上面所说的目标对象方法的调用,是通过AopUtils的方法调用,使用反射机制来对目标对象的方法进行的:

接下来,我们来看具体的ReflectiveMethodInvocation中proceed()方法的实现,也就是拦截器链的实现机制:

从上面的分析我们看到了Spring AOP拦截机制的基本实现,比如Spring怎样得到Proxy,怎样利用JAVA Proxy以及反射机制对用户定义的拦截器链进行处理。

3.6 织入的实现

在上面调用拦截器的时候,经过一系列的注册,适配的过程以后,拦截器在拦截的时候,会调用到预置好的一个通知适配器,设置通知拦截器,这是一系列Spring设计好为通知服务的类的一个,是最终完成通知拦截和实现的地方,例如对 MethodBeforeAdviceInterceptor的实现是这样的:

可以看到通知适配器将advice适配成Interceptor以后,会调用advice的before方法去执行横切逻辑。这样就成功的完成了before通知的织入。

AOP

Spring IoC之ApplicationContext

0

2年前

iwlh发布 在Spring 本章主要讲ApplicationContext接口对BeanFactory接口的扩展内容。BeanFactory接口主要围绕着bean和bean相关配置方式,没有关注应用环境的相关配置。ApplicationContext接口从BeanFactory接口派生而来,它与BeanFactory的对比如下图所示:

BeanFactory

ApplicationContext Bean配置/实例化

Yes

Yes 自动装配BeanPostProcessor

No

Yes 自动装配BeanFactoryPostProcessor

No

Yes 国际化信息(MessageSources)支持

No

Yes 容器内部事件(ApplicationEvent)支持

No

Yes 多配置模块加载

No

Yes

1、统一资源加载

Spring中提供了org.springframework.core.io.Resource接口作为所有资源的抽象。Spring中默认提供了一些Resource接口的实现类,如图所示:

实现类命名上就可以看出对应的资源,比如ClassPathResource类是从java应用程序的ClassPath中加载相关资源等等。

Spring中使用ResourceLoader来查找和定位Resource资源。ResorceLoader接口类图如下所示:

从上图可以看出,ApplicationContext接口继承自ResourceLoader接口。AbstractApplicationContext抽象类也继承自DefaultResourceLoader类,而且还拥有一个PathMatchingResourcePatternResolver属性字段,需要加载多个Resource时候,委派给PathMatchingResourcePatternResolver类加载即可。

回过来,让我们再看下DefaultResourceLoader中getResource方法的实现代码。

可以看到getResource方法尝试了classPath、url方式加载资源。需要注意的是,该类不能加载相对路径或绝对路径下的资源(例如文件),如果需要加载绝对路径的资源,可以使用FileSystemResourceLoader对象。

2、国际化信息支持

Java SE中已经有了国际化支持,也就是java.util.Locale和java.util.ResourceBundle。Spring在JavaSE的国际化支持上,进一步抽象了国际化信息的访问接口,提供了org.springframework.context.MessageSource接口,该接口提供一下方法:

ApplicationContext也实现了MessageSource接口。当ApplicationContext初始化时,它会自动在容器中查找名称为”messageSource”的bean。如果找到,对上述方法的调用将被委托给该bean。否则ApplicationContext会在其父类中查找是否含有同名的bean。如果有,就把它作为MessageSource。如果它最终没有找到任何的消息源,一个空的StaticMessageSource将会被实例化,使它能够接受上述方法的调用。messageSource bean的配置实例如下:

Spring提供了三种MessageSource的实现。即StaticMessageSource(提供简单实现,可通过编程方式添加信息条目,多用于测试,不应该用于正式的生产环境)、ResourceBundleMessageSource(基于标准的java.util.ResourceBundle而实现的MessageSource,对父类AbstractMessageSource进行扩展,提供对多个ResourceBundle的缓存以提高查询速度。是最常用的,可用于生产环境下的MessageSource)、ReloadableResourceBundleMessageSource(同样是基于标准的java.util.ResourceBundle而实现的MessageSource,通过cacheSeconds属性可以定期刷新并检查properties资源文件是否发生变化,并且通过ResourceLoader加载properties资源文件。)

另外,MessageSourceAware接口还能用于获取任何已定义的MessageSource引用。任何实现了MessageSourceAware接口的bean将在Spring容器初始化时候与MessageSource一同被注入。

MessageSource与ApplicationContext的类结构图如下所示:

3、Spring容器内部事件发布

ApplicationContext容器提供了容器内部事件发布功能,是继承自JavaSE标准自定义事件类而实现的。

JavaSE标准自定义事件结构不在此详细描述,一张图很直观的描述清楚:

EventObject,为JavaSE提供的事件类型基类,任何自定义的事件都继承自该类,例如上图中右侧灰色的各个事件。Spring中提供了该接口的子类ApplicationEvent。

EventListener,为JavaSE提供的事件监听者接口,任何自定义的事件监听者都实现了该接口,如上图左侧的各个事件监听者。Spring中提供了该接口的子类ApplicationListener接口。

JavaSE中未提供事件发布者这一角色类, 由各个应用程序自行实现事件发布者这一角色。Spring中提供了ApplicationEventPublisher接口作为事件发布者,并且ApplicationContext实现了这个接口,担当起了事件发布者这一角色。但ApplicationContext在具体实现上有所差异,Spring提供了ApplicationEventMulticaster接口,负责管理ApplicationListener和发布ApplicationEvent。ApplicationContext会把相应的事件相关工作委派给ApplicationEventMulticaster接口实现类来做。类图如下所示:

附上一张事件发布的时序图:

4、多配置模块加载

现在的应用程序,一般都会把配置信息按照某些规则进行分割,将不同类型或关注点的配置项放置在不同的文件中。相对BeanFactory来说,ApplicationContext已经支持了加载多个配置文件。

ApplicationContext加载多个配置文件的方法有:

1) 数组方式

String[] locations = new String[]{ “conf/bean1.xml”,”conf/bean2.xml”, “conf/bean3.xml”};

ApplicationContext container = new FileSystemXmlApplicationContext(locations);

2) 通配符

ApplicationContext container = new FileSystemXmlApplicationContext(“conf/////*.xml”);

3) ClassPathXmlApplicationContext特性

ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[] {“services.xml”, “daos.xml”}, MessengerService.class);

该方法可以通过MessengerService类在ClassPath中的位置定位配置文件,而不用指定每个配置文件的完整路径名。

ApplicationContext

Spring IoC之BeanFactory

0

2年前

khotyn发布 在Spring 本文的内容为对Spring IoC容器实现的分析。

本文一共分为5个部分:

  • 第一部分简要讲述了IoC的概念
  • 第二部分对Spring IoC容器中的主要类及其职责做一些了解
  • 第三部分分析了Spring IoC容器的初始化过程
  • 第四部分分析了从Spring IoC容器中获取Bean的过程
  • 第五部分简要讲述了Spring IoC容器对Bean生命周期的管理。

本文假设读者对以下的概念有所了解:IoC(控制反转),DI(依赖注入),Bean,并且读者有使用Spring IoC容器的经验。

约定:本文中所指的IoC容器没有特别说明均为Spring IoC容器

一、什么是IoC

IoC是Inversion of Control的缩写,中文的意思是控制反转,在IoC中,组件不需要去寻找它所依赖的对象,而是由IoC容器来负责将组件所依赖的对象通过Java Bean的Setter方法或者是构造函数等方式注入给组件。IoC的另一个名字是DI,即依赖注入,关于IoC和DI之间的关系以及关于IoC的更多内容,大家可以参考Wiki上的控制反转条目。

[二、IoC容器中的类主要类及其职责

]()

我们先来看下IoC容器的一个大概的类图:

点击查看大图

这张图中比较简单的展示了IoC容器中的各个类及其指责,我们需要重点把握几个接口的职责:

  • BeanFactory:这个接口是整个IoC容器最底层的接口,定义了一组访问Bean容器的基本方法。一些其他的接口,比如ListableBeanFactory和ConfigurableBeanFactory,都是继承了BeanFactory,并添加了其他的方法来完成某些特别的功能(比如ConfigurableBeanFactory,顾名思义,这个接口的职责是让BeanFactory变得可配置,那么它就定义了一组可以配置BeanFactory的方法)。
  • AbstractBeanFactory:从名字可以看出,这个类是BeanFactory接口的一个抽象实现类,这个类本身实现的是ConfigurableBeanFactory,对ConfigurableBeanFactory以及BeanFactory中的方法提供了实现,并且提供了一些诸如单例缓存,别名等等功能。
  • SingletonBeanRegistry:定义了一组操作单例Bean的方法

三、[IoC容器的初始化

]()

[ 使用过Spring的人都知道,我们都是在一份Bean配置文件中定义Bean,然后就可以通过BeanFactory的getBean()方法来获取Bean,那么我们就可以大致猜想到IoC容器的初始化工作大概就是将我们编写的Bean配置文件转换成IoC容器内部定义的用于放置Bean定义信息的数据结构,而这个数据结构就是BeanDefinition这个类。下面我们就来了解下这个转换过程是如何进行的。

我们通过实例化ClassPathXmlApplicationContext这个类来一步步来看其初始化的过程,简单地实例化ClassPathXmlApplicationContext的代码如下:

这里我们传入一个beans.xml作为配置文件的路径去实例化一个ClassPathXmlApplicationContext。

首先我们还是来看下整个初始化过程的序列图: ]()

点击查看大图

这个图中涉及到的类或许有点吓人,且慢,下面我会慢慢带你了解整个过程。从序列图里面我们看到初始化过程首先调用了refresh()方法,后面调用到了AbstractXmlApplicationContext的loadBeanDefinitions方法,来看下这个方法的实现:

这个方法将beanFactory(实现了BeanDefinitionRegistry接口,后续将通过这个接口将Bean定义信息注册到BeanFactory中去)传入new了一个XmlBeanDefinitionReader对象,然后将刚刚new出来的beanDefinitionReader传入调用loadBeanDefinitions方法,最终调用了XmlBeanDefinitionReader的loadBeanDefinitions(EncodedResource encodedResource)方法:

这个方法获取了Bean配置文件的输入流,并且调用了doLoadBeanDefinitions方法,在这个方法里面,程序将输入流转换成Document对象,然后调用了下面这个方法:

需要注意这个createReaderContext(resource)方法,创建这个方法的时候XmlBeanDefinitionReader将自己传入,以便在后面可以获取到它的Registry对象。最终DefaultBeanDefinitionDocumentReader将解析BeanDefinition的工作又交给了BeanDefinitionParserDelegate对象:

从上面的方法中,我们可以看到BeanDefinitionParserDelegate对象解析出BeanDefinition后,就由BeanDefinitionRegistry来将BeanDefinition注册到BeanFactory中去了。

至此,整个BeanFactory就初始化完毕了,可能一大堆方法调来调去地早就把大家给调晕了,我们就来总结下初始化过程中设计到的几个主要的类以及它们的职责吧:

  • XmlBeanDefinitionReader:读取定义Bean的XML文件并且将XML文件转成Document对象,交给BeanDefinitionDocuementReader再做解析。它持有一个BeanDefinitionRegistry对象,用于将BeanDefinition注册到BeanFactory中去。
  • XmlBeanDefinitionDocumentReader:取出Docuement对象内的各个元素并将这些元素交给BeanDefinitionParserDelegate来解析。
  • BeanDefinitionParserDelegate:用于解析Bean定义信息的代理类,负责一个Bean定义信息(可以看作是一个标签对)解析成一个BeanDefinition对象。
  • BeanDefinitionRegister:负责将BeanDefinition注册到BeanFactory中去。

四、从IoC容器中获取Bean

用过Spring的人大概都知道,在Spring中,我们是通过调用BeanFactory的getBean()方法来取得我们所需要的Bean的,而getBean()方法的主要逻辑在AbstactBeanFactory的doGetBean()方法中。在Spring中,有单例Bean和原型Bean的区分,在从容器中获取Bean的时候,单例Bean和原型Bean有些不同,当第一次获取单例Bean的时候,整个过程和获取原型Bean几乎是一样的,都需要创建一个Bean,但是当第二次,第三次,……,获取同样的单例Bean的时候,容器就直接从单例缓存中获取Bean了,而不会再去像获取原型Bean一样一而再再而三地创建Bean了。这样我们这一节也主要从两个方面来讲,一个是获取原型Bean,第一次获取单例Bean的逻辑和这个类似,有特别的地方也会在这里顺带提到,二则是将从单例缓存中获取单例Bean的过程,首先我们来看获取原型Bean:

[4.1、获取原型Bean

]()

[ 正如前面所说,我们来看下AbstractBeanFactory的doGetBean()方法来了解获取原型Bean的整个过程。

在获取Bean的时候,无论这个Bean是单例的还是原型的,Spring都会尝试从单例缓存中获取Bean,但是当拿原型Bean的时候,这里显然是拿不到的,接下来程序就会根据BeanDefinition信息来判断要创建的Bean是不是原型Bean,如果是,则进入下面这段逻辑:

程序在上图的(1)中的位置调用了beforePrototypeCreation方法,告诉容器当前的这个Bean正在创建中,来防止发生重复创建的情况。接下来,程序在(2)处调用了createBean方法来创建这个prototypeBean,最后,在(3)处,程序调用afterProtytypeCreation来告诉容器,这个Bean现在已经不再创建过程中了。

那么,让我们来关注下createBean这个方法里面干了些什么事情:

在做了一堆准备工作后,程序就到了上面的这一段中,从代码中我们可以看出这个方法主要的功能为以下两点:

  • 调用resolveBeforeInstantiation方法,让BeanPostProcessor可以有机会给你返回一个代理类而不是原来的类,当后面我们看到Spring AOP代理类的生成的时候,就会看到这个方法的用处了。
  • 调用doCreateBean()方法创建Bean

我们再来看下doCreateBean方法:

同样,这个方法里面也有两个主要的功能:

  • 一是调用createBeanInstance方法,创建一个Bean实例,在这个方法的内部,会调用BeanUtils的instantiateClass来实例化Bean,并把它包装成一个BeanWrapper
  • 二是调用populateBean方法来将Bean依赖的属性设置进去。 ]()

[4.2、创建单例Bean

]()

[ 整个获取原型Bean的过程大概就是这样样子,因为创建单例Bean和这个过程基本上是一样的,但是也有一些稍微不一样的地方,这里也稍微提到一下:

创建单例Bean是通过调用getSingleton来实现的,这个方法传入一个beanName和一个ObjectFactory,这个ObjectFactory的getObject方法里面调用到了我们前面提到的createBean方法,所以我们看下getSingleton这个方法的实现:

这个方法先尝试从singletonObjects中获取单例Bean,如果获取不到,则自己创建,同样,和创建原型Bean一样,在创建开始之前会调用beforeSingletonCreation方法来将beanName放到singletonsCurrentlyInCreation来告诉容器这个Bean已经在创建中了,在创建完成之后,会将BeanName从singletonsCurrentlyInCreation中删除掉。创建的过程是调用了传入的ObjectFactory的getObject方法,和创建原型Bean类似。在创建完成之后,还有一步addSingleton的操作,来讲单例放到单例缓存中去,看一下这个的实现:

方法的逻辑非常简单:把创建出来的单例Bean放到singletonObjects中去,然后从singletonFactories和earlySingletonObjects中删除掉,最后在registeredSingletons里面再加入这个Bean,对于这里面用到的几个容器,我觉得有必要在这里描述一下其作用,要不然读者肯定是晕呼晕呼的:

  • singletonObjects:用于保存BeanName和Bean实例之间的关系
  • singletonFactories:用于保存BeanName和创建Bean的工厂之间的关系
  • earlySingletonObjects:也是保存BeanName和Bean实例之间的关系,与singletonObjects的不同之处在于,当一个单例Bean被放到这里面去后,那么当Bean还在创建过程中,就可以通过getBean来拿到了,其目的是用来检测循环引用。
  • registeredSingletons:用来保存当前所有已注册的Bean ]()

[4.3、从单例缓存中获取单例Bean

]()

[ 单例在Spring的同一个容器内只会被创建一次,后续再获取Bean,就直接从单例缓存中获取了,我们来看下这一段过程,看下doGetBean里面调用的getSingleton方法:

这个方法的逻辑也相对简单,先尝试从singletonObjects里面获取,如果获取不到再从earlySingletonObjects里面获取,如果再获取不到,再尝试从singletonFactories里面获取beanName对应的ObjectFactory,然后调用这个ObjectFactory的getObject来创建Bean,并放到earlySingletonObjects里面去,并且从singletonFacotories里面remove掉这个ObjectFactory。

六、IoC容器对Bean生命周期的管理

Spring有一套Bean生命周期去管理Bean,值得注意的是,Spring只对非单例的Bean进行生命周期管理。关于Spring中Bean的生命周期,我们来看下一张老图:

在上面这张图里面,我们看到有很多的生命周期方法,那么这些生命周期方法是在哪里调用的呢?在前面获取原型Bean的一节中,我们已经知道,Spring会先调用createBeanInstance方法来创建Bean实例,然后通过populateBean方法来设置Bean的属性,在调用这个方法之后,其实Spring还调用了一个initializeBean的方法,上图中我们看到的生命周期方法基本上都在这个方法里面调用:

在这个方法里面:

  • Spring首先调用了invokeAwareMethods来调用各个Aware方法,包括BeanNameAware,BeanClassLoaderAware和BeanFactoryAware
  • 然后调用了所有BeanPostProcessor的postProcessBeforeInitialization方法
  • 接着调用invokeInitMethods方法,里面包括调用afterPropertiesSet和自定义的init方法
  • 最后调用了所有BeanPostProcessor的postProcessAfterInitialization方法

在调用这些方法以后,我们的Bean才算是可以使用啦。至于生命周期的最后两个方法,是在容器销毁的时候来调用的。 ]()

BeanFactory

*

Eclipse Profiler Plugin

Posted on

Eclipse Profiler Plugin

Eclipse Profiler Plugin

This is a plugin for the Eclipse platform which allows java code profiling. Project

License

CPL.

General note

1.) if you run the remote profiler, the filter settings in the eclipse environment are not taken into account. Instead you must provide the filter settings in the start command of your application server. You do this by adding an environment variable PROFILER_PACKAGE_FILTER to the startup command of your aplication, see below. 2.) Setting up the profiler package filters (in general) You must define the environment variable PROFILERPACKAGEFILTER as shown below in the examples for tomcat, jboss, weblogic and resin. General important Note: The environment variable contains following parts: the application starting class (A) inclusion filters (P) exclusion filters (__M) You must provide exactly one starting class, but you can have multiple inclusion and exclusion filters. The parts MUST be separated by the OS specific path.separator, i.e. ":" for unix or ";" for WINDOWS platforms) example package filter setting for WINDOWS: -DPROFILER_PACKAGE_FILTER=Aorg.apache.catalina.startup.Bootstrap;Msun.;Pmy.company.classes. example package filter setting for UNIX/LINUX: -DPROFILER_PACKAGE_FILTER=Aorg.apache.catalina.startup.Bootstrap:Msun.:Pmy.company.classes. In the examples below we provide the WINDOWS style. Please take care to use the LINUX/UNIX style if that aplies to you.

Win32 installation

Copy ProfilerDLL.dll from root plugin folder into bin folder of your JRE installation. You can skip this step, plugin will ask you and copy DLL into your JRE\BIN when you will start profiling local application inside of Eclipse first time. It will also check, that you have in JRE\BIN same DLL as in plugin directory.

Linux installation

Profiler has native part compiled with gcc 3.2, but if you have old gcc or libraries you can build native part yourself. Extract files from native\profiler_linux.tgz and look at script "m". This is example of compilation script. Change it as needed for you OS. See also profile_cpu/profile_heap for examples of start line for cpu and heap profiling and r_cpu/r_heap as example how to use them.

Usage

Profiler plugin creates additional kind of launch configuration in Run menu. Profiler tab allows the user to define packages which shouldn't be instrumented, thus, all time usage will be referred to calling methods. You can specify refresh rate, i.e. how often plugin will read statistics from prolifing JVM. You can also set method, how to get time of enter in method and leave. There are two methods: fast, but usefull only if you have one active thread, which tries to use all CPU; or slow, which use JVMPI function GetCurrentThreadCpuTime() and allows detect how much of CPU was used by thread. However with this method profiled program runs about 3 times slower than with fast method. Fig. 1. Launch configuration.

Profiler supports inclusive (grren) and exlusive (red) filters. You can noy only specify what packages should be excluded from instrumentation, but also what packages should be included. Usefull, if you want to profile only your own classes. You can move filters up and down using buttons or drag and drop. Fig. 1.1. Drag filters.

Plugin allows remote profiling with "Remote Profiler" launch configuration. Notice, that remote profiling is supported only in "run" mode, i.e. when you create launch configuration using "Run|Run..." menu item. You need to start remote application with special switches like: java -XrunProfilerDLL -Xbootclasspath/a:jakarta-regexp.jar;profilertrace.jar;commons-lang.jar -DPROFILER_PACKAGE_FILTER=Msun.;Mcom.sun. -DPROFILERUSEPACKAGEFILTER=1 Here M prefix used for exclusive filter, __P can be used for inclusive filter. And you should use __A for class with method "main". Fig. 2. Remote launch configuration.

Tomcat CPU profiling

Profiler was tested with jakarta-tomcat-4.1.12. Add after lines in catalina.bat: set EXECJAVA=%RUNJAVA% set MAINCLASS=org.apache.catalina.startup.Bootstrap set ACTION=start set SECURITY_POLICY_FILE= set DEBUG_OPTS= set JPDA= following line: set JAVA_OPTS=-XrunProfilerDLL:1 -Xbootclasspath/a:jakarta-regexp.jar;profiler_trace.jar;commons-lang.jar -DPROFILER_PACKAGE_FILTER=A%MAINCLASS%;Msun.;Mcom.sun.;Mjava.;Mjavax.;Morg.apache. -D__PROFILER_TIMING_METHOD=1 Then copy jar's: commons-lang.jar jakarta-regexp.jar profiler_trace.jar to bin directory of tomcat. Now you can start tomcat using startup.bat and it will gather statistics for you. Next step is configuring Eclipse. You should create remote launch configuration and set host address as needed. This is all. Launch it, plugin will connect to your Tomcat and show you some statistics.

Tomcat heap profiling

Profiling heap is almost same as CPU profiling, but you should use following line: set JAVAOPTS=-XrunProfilerDLL:3,10,0 -DPROFILER_PROFILE_HEAP=1 -Xbootclasspath/a:jakarta-regexp.jar;profiler_trace.jar;commons-lang.jar -DPROFILERPACKAGEFILTER=_A%MAINCLASS%;__M -D__PROFILER_TIMING_METHOD=1

JBoss profiling

Profiler was tested with jboss-3.0.6_tomcat-4.1.18. Add following line in your bin/run.bat (directly after echo off): set JAVA_OPTS=-XrunProfilerDLL:1 -Xbootclasspath/a:jakarta-regexp.jar;profiler_trace.jar;commons-lang.jar -DPROFILER_PACKAGE_FILTER=Aorg.jboss.Main;Msun.;Mcom.sun.;Mjava.;Mjavax. -DPROFILER_TIMING_METHOD=1 Then copy jar's: commons-lang.jar jakarta-regexp.jar profiler_trace.jar to bin directory of JBoss. Now you can start JBoss using run.bat and it will gather statistics for you. Next step is configuring Eclipse. You should create remote launch configuration and set host address as needed. This is all. Launch it, plugin will connect to your JBoss and show you some statistics.

WebLogic profiling

Profiler was tested with WebLogic 8.1, installed in "c:/bea". WebLogic has its own JRE's, so you will need to copy ProfilerDLL.dll manually to C:\bea\jdk141_02\jre\bin. For profiling examples, change file C:\bea\weblogic81\samples\domains\examples\startExamplesServer.cmd, line where JAVA_OPTIONS is defined: set JAVA_OPTIONS=-XrunProfilerDLL:1 -Xbootclasspath/a:jakarta-regexp.jar;profiler_trace.jar;commons-lang.jar -DPROFILER_PACKAGE_FILTER=Aweblogic.Server;Msun.;Mcom.sun.;Mjava.;Mjavax.;Mweblogic. -DPROFILER_TIMING_METHOD=1 Then copy jar's: commons-lang.jar jakarta-regexp.jar profiler_trace.jar to "C:\bea\weblogic81\samples\domains\examples". Now you can start WebLogic examplex using startExamplesServer.cmd, or shortcut in Windows menu, and it will gather statistics for you. Next step is configuring Eclipse. You should create remote launch configuration and set host address as needed. This is all. Launch it, plugin will connect to your WebLogic and show you some statistics.

Resin profiling

Profiler was tested with Resin-ee 2.1.10. Create batch file with following content in "bin": httpd.exe -J-XrunProfilerDLL:1 -Xbootclasspath/a:c:/prof/jakarta-regexp.jar;c:/prof/profiler_trace.jar;c:/prof/commons-lang.jar -DPROFILER_PACKAGE_FILTER=Acom.caucho.server.http.HttpServer;Msun.;Mcom.sun.;Mjava.;Mjavax. -DPROFILER_TIMING_METHOD=1 Then create directory "prof" in disc "C" and copy jar's: commons-lang.jar jakarta-regexp.jar profiler_trace.jar. Now you can start your batch file and it will gather statistics for you. Next step is configuring Eclipse. You should create remote launch configuration and set host address as needed. This is all. Launch it, plugin will connect to your Resin and show you some statistics.

Options

Profiler supports several options via -DXXX. Options Description PROFILER_PACKAGE_FILTER Contains list of packages to include or exclude. Here P - inclusive pattern, M - exclusive pattern. And A - application start class. Examples: Include only ru./ and de./ packages: Pru.;Pde. Exclude system packages: Aorg.jboss.Main;Msun.;Mcom.sun.;Mjava.;Mjavax. If at least one inclusive pattern used, only classes accepted by inclusive patterns will be instrumented. PROFILER_TIMING_METHOD Specifies timing method - how to measure elapsed time. 0 - fast, System.currentTimeMillis(), good for application without sleeps and waits. 1 - precise, thread aware, slow, good for multithreaded applications. 2 - sampling profiling, very fast, good for long runned processes. PROFILER_PROFILE_HEAP 1, if HEAP profiling should be used. PROFILER_AUTO_START 0 - don't start automatically. 1 (default) - start automatically. PROFILER_START_ON_METHOD Start method name. When applications tries to enter in this method profiling starts (see PROFILER_AUTO_START, it should be 0). Example: -DPROFILER_START_ON_METHOD=ru.nlmk.train.Main.mainLoop PROFILER_PAUSE_ON_METHOD 1, if you need pause profiling when PROFILER_START_ON_METHOD leaved. PROFILER_INSTRUMENT_SYSTEM_CLASSES 1, if you need instrument additional system classes, like java.lang.String, etc. Expensive! __PROFILER_WAIT_FRONTEND_CONNECT 1, if you need to wait, until frontend (plugin) will connect. (0 by default).

CPU profiling

Profiler supports CPU profiling and basic function for heap profiling. The profiler collects invocation count and direct time statistics for every method. Direct time is the amount of time used by selected method for execute. Also a total time value displayed in calling tree for selected thread. The total time means a time used by selected method and by all the methods it had called.

Only instrumentation profiling method is supported because of its precise. The overhead expenses reflects at speed decreasing app. 5 times to normal.

How it works

The profiler subscribes for JVPMI event JVMPI_EVENT_CLASS_LOAD_HOOK which called during every class loading. The profiler modifies loading byte-code by adding profiler's method call "enter" in the beginning of the every method of loading class and at the end of it - "leave". "Enter" profiler's method notes the time of entering to profiling method. "Leave" makes method leaving timestamp and calculates a difference between enter and leave timestamps. This difference means total time for this method call. Direct time calculates as total time of this method minus total time of the all methods called by it. The profiles makes some time corrections for the time spent by profiler.

The profiler implemented as an Eclipse perspective with following views: Threads, Packages, Classes, Methods, Thread methods, Thread call tree, Heap.

Threads. Shows a list with all threads (alive or dead) in the current process. Here you can pause/resume refreshing of statistics in views, pause/resume all threads in profilied program, clear all statistics or run GC in profiled JVM. Fig. 3. Threads View.

Call graph. Profiler uses Draw2D for displaying call graph of thread. All methods shown in several columns, column depends on level, on which this method was called. Inside one column methods sorted by direct time used. Each node in graph has color from red to dark gray, depending on direct time. Methods with maximum time use have red color, and methods, which almost don't use time, have grey color. Each method has hint with detailed information. You can double click on method to open it in editor. Lines between method present call from source method to target. Lines from one level to directly next have black color, to next - blue, inside one level - red, and from backward calls - green. Width of line depeneds on how much of time use in this call. Fig. 3.1. Call graph. You can also see hit for call. Fig. 3.2. Call hint. As you can see, full graph looks fairly complex, but you can select part of it. Press mouse on some method and select then button "Show callers", "Show calles" or "Show caller and callees". You can see something like this: Fig. 3.3. Callers. Fig. 3.4. Callees. Fig. 3.5. Callers and callees. You can double click on call line for opening editor with source method with highlighting places, where it calls target method. Fig. 3.6. Show calls.

Packages. Shows a list with all methods with class hierarchy from package. This allows to determine packages which used the most of CPU time. In this view (and in Classes and Methods views also) the user can see in gray color the methods with unmodified parameters since last update. The user can hide such kind of methods by applying appropriate filters. Here: Name - name of package/class/method Inv. - invocation count % - percent of all invocations Time - direct time used % - percent of total time Time/Inv. - average time used for one invocation Total time - total time used directly by method and by all methods it calls Inst. time - time used for instrumentation of class Fig. 4. Packages View.

You can add package or class to filter by pressing right mose button on element in table and selecting menu item. Filter can be added to launch configuration filter (will be used in next profiling, if you will active it) or to view filters (will be activated right now). Fig. 4.1. Add filter.

Classes. Shows a list of all class methods with method hierarchy from class. This allows to determine classes with the most CPU time usage. Fig. 5. Classes View.

Filters. Allows to define which kind of methods can be shown. Fig. 6. Filters. You can save configured name patterns (inclusive or exalusive) with some name and description, then you will able to select them in check list box. Fig. 6.1. New filter. Later you can change this user defined filter. Fig. 6.2. Edit filter.

Methods. Shows the statistics for all methods of the current process. Fig. 7. All Methods View.

Thread methods. Shows methods statictics for thread selected in Threads View. Fig. 8. Thread Methods View.

Thread tree. Shows a methods invocation tree for thread selected in Threads View. Here red square used for highlighted methods (from thread methods context menu) and green dot used for all other methods. Fig. 9. Thread Tree View.

Inverted thread tree. Shows a methods invocation tree for thread selected in Threads View starting from leaves. This allows you fastly detect, that some leaf method uses much of total time and see, what methods it is called. Fig. 9.1. Inverted thread Tree View.

Heap. Shows heap usage graph: total (green), used (blue) and free (yellow) heap. Heap View. Fig. 10. Heap View.

Here is an overall view of the profiler perspective. You can see a method HashMap.put opened in source code editor with highlighted lines of code which has the maximum hit count (size of annotation depends on hit count). You can open source code editor by choosing menu item "Open method in editor" in context menu. Fig. 11. Perspective.

SourceForge Logo