cglib代理jvm对象池会直接载入jvm吗

import java.lang.reflect.M
import com.alibaba.fastjson.JSON;
import n...
发布于 1个月前
用cglib实现,父类是jar包里的类
发布于 1年前
@黄勇 你好,想跟你请教个问题: 在使用了cglib代理类后,在原生类里抛出异常后,打印异常堆栈时,会显示很多代理类的堆栈信息,有没有办法可以不打印代理类...
发布于 1年前
&无详细内容&
发布于 6年前
CGLib动态代理技术百度了还是看不懂。
发布于 1年前
在设计一个后台项目的架构,想不到一个好方法封装 mybatis 的查询条件,想要用 cglib 动态生成 example 类,可行性如何?
发布于 2年前
如题,用cglib? 貌似只能生成代理对象,加入横切代码啊
发布于 6年前
文章转自:http://www.tbdata.org/archives/1851 之前有写 基于AOP的日志调试 讨论一种跟踪Java程序的方法, 但不是...
发布于 6年前
@黎明伟 你好,想跟你请教个问题: 我看到你之前也遇到过这样的问题 /problems/102520
发布于 3年前
java项目经常要用到ssh框架,可是用接口注入的话要多写接口方法,比较麻烦,cglib不用,请问spring中如何配置cglib啊?包已经下载了
发布于 3年前
Spring3使用注解方式实现AOP的时候,被代理的Customer对象使用@Service或者@Component注解时拦截失效,无法生成Cglib代理...
发布于 3年前
这两天做压力测试(服务器是IBM P750 8C 32G),高并发下如果用反射直接调用一个简单的服务(空方法,直接return)TPS大概能到将近...
发布于 3年前
环境: tomcat 7. JDK1.6. 目标:获取正在运行的jvm列表 尝试代码: for (int i=0; i&VirtualMachine.li...
发布于 4年前
package sun.testP
import java.lang.reflect.InvocationH
import ja...
发布于 4年前
配置了强制使用cglib,但spring还是使用JDK代理service,所有的service都实现了IGenericService接口,而且在有注入be...
发布于 4年前
我想自己做一套ORM的产品,然后在做某一部分时,因为所有方法都需要调用一个公共方法,因此想使用动态代理,后来选择了CGLib。但在代理时出现了这样一个问题...
发布于 5年前
我有定义一个类: @Transactional(propagation = Propagation.REQUIRED)
public abstract c...
发布于 4年前
不知道有没有人用CGLIB库做动态代理时, 强制转换成普通类后,响应键盘事件不对劲了。
注: 我定义了Direction类的UP,DOWN,RIGHT...
发布于 4年前
CGLib (Code Generation Library) 是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java...
发布于 7年前
AOP(Aspect Orient Programming),也就是面向方面编程,作为面向对象编程的一种补充,专门用于处理系统中分布于各个模块(不同方法)...
发布于 5年前
为什么hibernate使用javassist代替cglib呢? 难道是cglib不行~但spring好像还是使用cglib~ 使用这两个都会出现java...
发布于 7年前
一次切入用 MethodInterceptor.intercept(),如果要插入多个呢。
发布于 5年前
cglib是个好东东,说牛刀,是因为它很强大,用途很广。小试,是因为仅仅用它做了个类似hello world的小程序。
cglib是Spring、Hi...
发布于 7年前
定义一个Map用来缓存反射时对Class解析的Method对象(作为Method缓存来用) //从缓存中获取method对象 Method actionT...
发布于 5年前
例如在一个典型的J2EE应用系统中,大量的PO类来充表达待处理的数据。PO类的最大问题在于,它是静态的,不可以在运行期,只能在开发期去改变。当然我知道,静...
发布于 6年前
在使用 #Spring#
BeanNameAutoProxyCreator 的过程中发现,cglib 生成的代理类,类原来的 Annotation 都没...
发布于 6年前
纯web项目,项目中用到了cglib中的部分功能,代码如下: private Object getInstrumentedClass(Class clz)...
发布于 8年前
1.建立第一个sprint项目(java project) 步骤:window--&preferences--&java--&user libraries...
发布于 5年前JVM内存溢出的方式
JVM内存溢出的方式
编辑:www.fx114.net
本篇文章主要介绍了"JVM内存溢出的方式",主要涉及到JVM内存溢出的方式方面的内容,对于JVM内存溢出的方式感兴趣的同学可以参考一下。
了解了Java虚拟机五个内存区域的作用后,下面我们来继续学习下在什么情况下
这些区域会发生溢出。
1.虚拟机参数配置
-Xms:初始堆大小,默认为物理内存的1/64(&1GB);默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制。
-Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。
-Xss:每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。应根据应用的线程所需内存大小进行适当调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在左右。一般小的应用, 如果栈不是很深, 应该是128k够用的,大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。
-XX:PermSize:设置永久代(perm gen)初始值。默认值为物理内存的1/64。
-XX:MaxPermSize:设置持久代最大值。物理内存的1/4。
2.方法区溢出
因为方法区是保存类的相关信息的,所以当我们加载过多的类时就会导致方法区
溢出。在这里我们通过JDK动态代理和CGLIB代理两种方式来试图使方法区溢出。
2.1 JDK动态代理
package com.cdai.jvm.
import java.lang.reflect.InvocationH
import java.lang.reflect.M
import java.lang.reflect.P
public class MethodAreaOverflow {
static interface OOMInterface {
static class OOMObject implements OOMInterface {
static class OOMObject2 implements OOMInterface {
public static void main(String[] args) {
final OOMObject object = new OOMObject();
while (true) {
OOMInterface proxy = (OOMInterface) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
OOMObject.class.getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println(&Interceptor1 is working&);
return method.invoke(object, args);
System.out.println(proxy.getClass());
System.out.println(&Proxy1: & + proxy);
OOMInterface proxy2 = (OOMInterface) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
OOMObject.class.getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println(&Interceptor2 is working&);
return method.invoke(object, args);
System.out.println(proxy2.getClass());
System.out.println(&Proxy2: & + proxy2);
虽然我们不断调用Proxy.newInstance()方法来创建代理类,但是JVM并没有内存溢出。
每次调用都生成了不同的代理类实例,但是代理类的Class对象没有改变。是不是Proxy
类对代理类的Class对象有缓存?具体原因会在之后的《JDK动态代理与CGLIB》中进行
详细分析。
2.2 CGLIB代理
CGLIB同样会缓存代理类的Class对象,但是我们可以通过配置让它不缓存Class对象,
这样就可以通过反复创建代理类达到使方法区溢出的目的。
package com.cdai.jvm.
import java.lang.reflect.M
import net.sf.cglib.proxy.E
import net.sf.cglib.proxy.MethodI
import net.sf.cglib.proxy.MethodP
public class MethodAreaOverflow2 {
static class OOMObject {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
return method.invoke(obj, args);
OOMObject proxy = (OOMObject) enhancer.create();
System.out.println(proxy.getClass());
堆溢出比较简单,只需通过创建一个大数组对象来申请一块比较大的内存,就可以使
堆发生溢出。
package com.cdai.jvm.
public class HeapOverflow {
private static final int MB = 1024 * 1024;
@SuppressWarnings(&unused&)
public static void main(String[] args) {
byte[] bigMemory = new byte[1024 * MB];
栈溢出也比较常见,有时我们编写的递归调用没有正确的终止条件时,就会使方法不断
递归,栈的深度不断增大,最终发生栈溢出。
package com.cdai.jvm.
public class StackOverflow {
private static int stackDepth = 1;
public static void stackOverflow() {
stackDepth++;
stackOverflow();
public static void main(String[] args) {
stackOverflow();
catch (Exception e) {
System.err.println(&Stack depth: & + stackDepth);
e.printStackTrace();
版权声明:本文为博主原创文章,未经博主允许不得转载。
一、不得利用本站危害国家安全、泄露国家秘密,不得侵犯国家社会集体的和公民的合法权益,不得利用本站制作、复制和传播不法有害信息!
二、互相尊重,对自己的言论和行为负责。
本文标题:
本页链接:计算机程序的思维逻辑 (86) - 动态代理
来源:博客园
前面两节,我们介绍了和,利用它们,可以编写通用灵活的程序,本节,我们来探讨Java中另外一个动态特性 - 动态代理。动态代理是一种强大的功能,它可以在运行时动态创建一个类,实现一个或多个接口,可以在不修改原有类的基础上动态为通过该类获取的对象添加方法、修改行为,这么描述比较抽象,下文会具体介绍,这些特性使得它广泛应用于各种系统程序、框架和库中,比如Spring, Hibernate, MyBatis, Guice等。动态代理是实现面向切面的编程(AOP - Aspect Oriented Programming)的基础,切面的例子有日志、性能监控、权限检查、数据库事务等,它们在程序的很多地方都会用到,代码都差不多,但与某个具体的业务逻辑关系也不太密切,如果在每个用到的地方都写,代码会很冗余,也难以维护,AOP将这些切面与主体逻辑相分离,代码简单优雅的多。和注解类似,在大部分的应用编程中,我们不需要自己实现动态代理,而只需要按照框架和库的文档说明进行使用就可以了。不过,理解动态代理有助于我们更为深刻的理解这些框架和库,也能更好的应用它们,在自己的业务需要时,也能自己实现。理解动态代理,我们首先要了解静态代理,了解了静态代理后,我们再来看动态代理。动态代理有两种实现方式,一种是Java SDK提供的,另外一种是第三方库如cglib提供的,我们会分别介绍这两种方式,包括其用法和基本实现原理,理解了基本概念和原理后,我们来看一个简单的应用,实现一个极简的AOP框架。静态代理我们首先来看代理,代理是一个比较通用的词,作为一个软件设计模式,它在《设计模式》一书中被提出,基本概念和日常生活中的概念是类似的,代理背后一般至少有一个实际对象,代理的外部功能和实际对象一般是一样的,用户与代理打交道,不直接接触实际对象,虽然外部功能和实际对象一样,但代理有它存在的价值,比如:节省成本比较高的实际对象的创建开销,按需延迟加载,创建代理时并不真正创建实际对象,而只是保存实际对象的地址,在需要时再加载或创建执行权限检查,代理检查权限后,再调用实际对象屏蔽网络差异和复杂性,代理在本地,而实际对象在其他服务器上,调用本地代理时,本地代理请求其他服务器代理模式的代码结构也比较简单,我们看个简单的例子,代码如下:public class SimpleStaticProxyDemo {
static interface IService {
public void sayHello();
static class RealService implements IService {
public void sayHello() {
System.out.println("hello");
static class TraceProxy implements IService {
private IService realS
public TraceProxy(IService realService) {
this.realService = realS
public void sayHello() {
System.out.println("entering sayHello");
this.realService.sayHello();
System.out.println("leaving sayHello");
public static void main(String[] args) {
IService realService = new RealService();
IService proxyService = new TraceProxy(realService);
proxyService.sayHello();
}}代理和实际对象一般有相同的接口,在这个例子中,共同的接口是IService,实际对象是RealService,代理是TraceProxy。TraceProxy内部有一个IService的成员变量,指向实际对象,在构造方法中被初始化,对于方法sayHello的调用,它转发给了实际对象,在调用前后输出了一些跟踪调试信息,程序输出为:entering sayHellohelloleaving sayHello我们在介绍过两种设计模式,适配器和装饰器,它们与代理模式有点类似,它们的背后都有一个别的实际对象,都是通过组合的方式指向该对象,不同之处在于,适配器是提供了一个不一样的新接口,装饰器是对原接口起到了"装饰"作用,可能是增加了新接口、修改了原有的行为等,代理一般不改变接口。不过,我们并不想强调它们的差别,可以将它们看做代理的变体,统一看待。在上面的例子中,我们想达到的目的是在实际对象的方法调用前后加一些调试语句,为了在不修改原类的情况下达到这个目的,我们在代码中创建了一个代理类TraceProxy,它的代码是在写程序时固定的,所以称为静态代理。输出跟踪调试信息是一个通用需求,可以想象,如果每个类都需要,而又不希望修改类定义,我们需要为每个类创建代理,实现所有接口,这个工作就太繁琐了,如果再有其他的切面需求呢,整个工作可能又要重来一遍。这时,就需要动态代理了,主要有两种方式实现动态代理,Java SDK和第三方库cglib,我们先来看Java SDK。Java SDK动态代理用法在静态代理中,代理类是直接定义在代码中的,在动态代理中,代理类是动态生成的,怎么动态生成呢?我们用动态代理实现前面的例子:public class SimpleJDKDynamicProxyDemo {
static interface IService {
public void sayHello();
static class RealService implements IService {
public void sayHello() {
System.out.println("hello");
static class SimpleInvocationHandler implements InvocationHandler {
private Object realO
public SimpleInvocationHandler(Object realObj) {
this.realObj = realO
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("entering " + method.getName());
Object result = method.invoke(realObj, args);
System.out.println("leaving " + method.getName());
public static void main(String[] args) {
IService realService = new RealService();
IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(),
new Class&?&[] { IService.class }, new SimpleInvocationHandler(realService));
proxyService.sayHello();
}}代码看起来更为复杂了,这有什么用呢?别着急,我们慢慢解释。IService和RealService的定义不变,程序的输出也没变,但代理对象proxyService的创建方式变了,它使用java.lang.reflect包中的Proxy类的静态方法newProxyInstance来创建代理对象,这个方法的声明如下:public static Object newProxyInstance(ClassLoader loader, Class&?&[] interfaces, InvocationHandler h)它有三个参数:loader表示类加载器,下节我们会单独探讨它,例子使用和IService一样的类加载器interfaces表示代理类要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类,例子中只有一个IServiceh的类型为InvocationHandler,它是一个接口,也定义在java.lang.reflect包中,它只定义了一个方法invoke,对代理接口所有方法的调用都会转给该方法newProxyInstance的返回值类型为Object,可以强制转换为interfaces数组中的某个接口类型,这里我们强制转换为了IService类型,需要注意的是,它不能强制转换为某个类类型,比如RealService,即使它实际代理的对象类型为RealService。SimpleInvocationHandler实现了InvocationHandler,它的构造方法接受一个参数realObj表示被代理的对象,invoke方法处理所有的接口调用,它有三个参数:proxy表示代理对象本身,需要注意,它不是被代理的对象,这个参数一般用处不大method表示正在被调用的方法args表示方法的参数在SimpleInvocationHandler的invoke实现中,我们调用了method的invoke方法,传递了实际对象realObj作为参数,达到了调用实际对象对应方法的目的,在调用任何方法前后,我们输出了跟踪调试语句。需要注意的是,不能将proxy作为参数传递给method.invoke,比如:Object result = method.invoke(proxy, args);上面的语句会出现死循环,因为proxy表示当前代理对象,这么调用又会调用到SimpleInvocationHandler的invoke方法。基本原理看了上面的介绍是不是更晕了,没关系,看下Proxy.newProxyInstance的内部就理解了。上面例子中创建proxyService的代码可以用如下代码代替:Class&?& proxyCls = Proxy.getProxyClass(IService.class.getClassLoader(),
new Class&?&[] { IService.class });Constructor&?& ctor = proxyCls.getConstructor(new Class&?&[] { InvocationHandler.class });InvocationHandler handler = new SimpleInvocationHandler(realService);IService proxyService = (IService) ctor.newInstance(handler);分为三步:通过Proxy.getProxyClass创建代理类定义,类定义会被缓存获取代理类的构造方法,构造方法有一个InvocationHandler类型的参数创建InvocationHandler对象,创建代理类对象Proxy.getProxyClass需要两个参数,一个是ClassLoader,另一个是接口数组,它会动态生成一个类,类名以$Proxy开头,后跟一个数字,对于上面的例子,动态生成的类定义如下所示,为简化起见,我们忽略了异常处理的代码:final class $Proxy0 extends Proxy implements SimpleJDKDynamicProxyDemo.IService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
public final boolean equals(Object paramObject) {
return ((Boolean) this.h.invoke(this, m1,
new Object[] { paramObject })).booleanValue();
public final void sayHello() {
this.h.invoke(this, m3, null);
public final String toString() {
return (String) this.h.invoke(this, m2, null);
public final int hashCode() {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService")
.getMethod("sayHello",new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}}$Proxy0的父类是Proxy,它有一个构造方法,接受一个InvocationHandler类型的参数,保存为了实例变量h,h定义在父类Proxy中,它实现了接口IService,对于每个方法,如sayHello,它调用InvocationHandler的invoke方法,对于Object中的方法,如hashCode, equals和toString, $Proxy0同样转发给了InvocationHandler。可以看出,这个类定义本身与被代理的对象没有关系,与InvocationHandler的具体实现也没有关系,而主要与接口数组有关,给定这个接口数组,它动态创建每个接口的实现代码,实现就是转发给InvocationHandler,与被代理对象的关系以及对它的调用由InvocationHandler的实现管理。我们是怎么知道$Proxy0的定义的呢?对于Oracle的JVM,可以配置java的一个属性得到,比如:java -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true shuo.laoma.dynamic.c86.SimpleJDKDynamicProxyDemo以上命令会把动态生成的代理类$Proxy0保存到文件$Proxy0.class中,通过一些反编译器工具比如JD-GUI(http://jd.benow.ca/)就可以得到源码。理解了代理类的定义,后面的代码就比较容易理解了,就是获取构造方法,创建代理对象。动态代理的优点相比静态代理,动态代理看起来麻烦了很多,它有什么好处呢?使用它,可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为每个被代理的类型都创建一个静态代理类。看个简单的示例:public class GeneralProxyDemo {
static interface IServiceA {
public void sayHello();
static class ServiceAImpl implements IServiceA {
public void sayHello() {
System.out.println("hello");
static interface IServiceB {
public void fly();
static class ServiceBImpl implements IServiceB {
public void fly() {
System.out.println("flying");
static class SimpleInvocationHandler implements InvocationHandler {
private Object realO
public SimpleInvocationHandler(Object realObj) {
this.realObj = realO
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("entering " + realObj.getClass().getSimpleName() + "::" + method.getName());
Object result = method.invoke(realObj, args);
System.out.println("leaving " + realObj.getClass().getSimpleName() + "::" + method.getName());
@SuppressWarnings("unchecked")
private static &T& T getProxy(Class&T& intf, T realObj) {
return (T) Proxy.newProxyInstance(intf.getClassLoader(), new Class&?&[] { intf },
new SimpleInvocationHandler(realObj));
public static void main(String[] args) throws Exception {
IServiceA a = new ServiceAImpl();
IServiceA aProxy = getProxy(IServiceA.class, a);
aProxy.sayHello();
IServiceB b = new ServiceBImpl();
IServiceB bProxy = getProxy(IServiceB.class, b);
bProxy.fly();
}}在这个例子中,有两个接口IServiceA和IServiceB,它们对应的实现类是ServiceAImpl和ServiceBImpl,虽然它们的接口和实现不同,但利用动态代理,它们可以调用同样的方法getProxy获取代理对象,共享同样的代理逻辑SimpleInvocationHandler,即在每个方法调用前后输出一条跟踪调试语句。程序输出为:entering ServiceAImpl::sayHellohelloleaving ServiceAImpl::sayHelloentering ServiceBImpl::flyflyingleaving ServiceBImpl::flycglib动态代理用法Java SDK动态代理的局限在于,它只能为接口创建代理,返回的代理对象也只能转换到某个接口类型,如果一个类没有接口,或者希望代理非接口中定义的方法,那就没有办法了。有一个第三方的类库cglib(/cglib/cglib)可以做到这一点,Spring,Hibernate等都使用该类库。我们看个简单的例子:public class SimpleCGLibDemo {
static class RealService {
public void sayHello() {
System.out.println("hello");
static class SimpleInterceptor implements MethodInterceptor {
public Object intercept(Object object, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("entering " + method.getName());
Object result = proxy.invokeSuper(object, args);
System.out.println("leaving " + method.getName());
@SuppressWarnings("unchecked")
private static &T& T getProxy(Class&T& cls) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls);
enhancer.setCallback(new SimpleInterceptor());
return (T) enhancer.create();
public static void main(String[] args) throws Exception {
RealService proxyService = getProxy(RealService.class);
proxyService.sayHello();
}}RealService表示被代理的类,它没有接口。getProxy()为一个类生成代理对象,这个代理对象可以安全的转换为被代理类的类型,它使用了cglib的Enhancer类,Enhancer类的setSuperclass设置被代理的类,setCallback设置被代理类的public非final方法被调用时的处理类,Enhancer支持多种类型,这里使用的类实现了MethodInterceptor接口,它与Java SDK中的InvocationHandler有点类似,方法名称变成了intercept,多了一个MethodProxy类型的参数。与前面的InvocationHandler不同,SimpleInterceptor中没有被代理的对象,它通过MethodProxy的invokeSuper方法调用被代理类的方法:Object result = proxy.invokeSuper(object, args);注意,它不能这样调用被代理类的方法:Object result = method.invoke(object, args);
object是代理对象,调用这个方法还会调用到SimpleInterceptor的intercept方法,造成死循环。在main方法中,我们也没有创建被代理的对象,创建的对象直接就是代理对象。基本实现原理cglib的实现机制与Java SDK不同,它是通过继承实现的,它也是动态创建了一个类,但这个类的父类是被代理的类,代理类重写了父类的所有public非final方法,改为调用Callback中的相关方法,在上例中,调用SimpleInterceptor的intercept方法。Java SDK代理与cglib代理比较Java SDK代理面向的是一组接口,它为这些接口动态创建了一个实现类,接口的具体实现逻辑是通过自定义的InvocationHandler实现的,这个实现是自定义的,也就是说,其背后都不一定有真正被代理的对象,也可能多个实际对象,根据情况动态选择。cglib代理面向的是一个具体的类,它动态创建了一个新类,继承了该类,重写了其方法。从代理的角度看,Java SDK代理的是对象,需要先有一个实际对象,自定义的InvocationHandler引用该对象,然后创建一个代理类和代理对象,客户端访问的是代理对象,代理对象最后再调用实际对象的方法,cglib代理的是类,创建的对象只有一个。如果目的都是为一个类的方法增强功能,Java SDK要求该类必须有接口,且只能处理接口中的方法,cglib没有这个限制。动态代理的应用 - AOP利用cglib动态代理,我们实现一个极简的AOP框架,演示AOP的基本思路和技术。用法我们添加一个新的注解@Aspect,其定义为:@Retention(RUNTIME)@Target(TYPE)public @interface Aspect {
Class&?&[] value();}它用于注解切面类,它有一个参数,可以指定要增强的类,比如:@Aspect({ServiceA.class,ServiceB.class})public class ServiceLogAspect ServiceLogAspect就是一个切面,它负责类ServiceA和ServiceB的日志切面,即为这两个类增加日志功能。再比如:@Aspect({ServiceB.class})public class ExceptionAspect ExceptionAspect也是一个切面,它负责类ServiceB的异常切面。这些切面类与主体类怎么协作呢?我们约定,切面类可以声明三个方法before/after/exception,在主体类的方法调用前/调用后/出现异常时分别调用这三个方法,这三个方法的声明需符合如下签名:public static void before(Object object, Method method, Object[] args)public static void after(Object object, Method method, Object[] args, Object result)public static void exception(Object object, Method method, Object[] args, Throwable e)object, method和args与cglib MethodInterceptor中的invoke参数一样,after中的result表示方法执行的结果,exception中的e表示发生的异常类型。ServiceLogAspect实现了before和after方法,加了一些日志,其代码为:@Aspect({ ServiceA.class, ServiceB.class })public class ServiceLogAspect {
public static void before(Object object, Method method, Object[] args) {
System.out.println("entering " + method.getDeclaringClass().getSimpleName()
+ "::" + method.getName() + ", args: " + Arrays.toString(args));
public static void after(Object object, Method method, Object[] args, Object result) {
System.out.println("leaving " + method.getDeclaringClass().getSimpleName()
+ "::" + method.getName() + ", result: " + result);
}}ExceptionAspect只实现exception方法,在异常发生时,输出一些信息,代码为:@Aspect({ ServiceB.class })public class ExceptionAspect {
public static void exception(Object object,
Method method, Object[] args, Throwable e) {
System.err.println("exception when calling: " + method.getName()
+ "," + Arrays.toString(args));
}}ServiceLogAspect的目的是在类ServiceA和ServiceB所有方法的执行前后加一些日志,而ExceptionAspect的目的是在类ServiceB的方法执行出现异常时收到通知并输出一些信息。它们都没有修改类ServiceA和ServiceB本身,本身做的事是比较通用的,与ServiceA和ServiceB的具体逻辑关系也不密切,但又想改变ServiceA/ServiceB的行为,这就是AOP的思维。只是声明一个切面类是不起作用的,我们需要与介绍的DI容器结合起来,我们实现一个新的容器CGLibContainer,它有一个方法:public static &T& T getInstance(Class&T& cls)通过该方法获取ServiceA或ServiceB,它们的行为就会被改变,ServiceA和ServiceB的定义与上节一样,这里重复下:public class ServiceA {
@SimpleInject
public void callB(){
b.action();
}}public class ServiceB {
public void action(){
System.out.println("I'm B");
}}通过CGLibContainer获取ServiceA,会自动应用ServiceLogAspect,比如:ServiceA a = CGLibContainer.getInstance(ServiceA.class);a.callB();输出为:entering ServiceA::callB, args: []entering ServiceB::action, args: []I'm Bleaving ServiceB::action, result: nullleaving ServiceA::callB, result: null实现原理这是怎么做到的呢?CGLibContainer在初始化的时候,会分析带有@Aspect注解的类,分析出每个类的方法在调用前/调用后/出现异常时应该调用哪些方法,在创建该类的对象时,如果有需要被调用的方法,则创建一个动态代理对象,下面我们具体来看下代码。为简化起见,我们基于介绍的DI容器的第一个版本,即每次获取对象时都创建一个,不支持单例。我们定义一个枚举InterceptPoint,表示切点(调用前/调用后/出现异常):public static enum InterceptPoint {
BEFORE, AFTER, EXCEPTION}在CGLibContainer中定义一个静态变量,表示每个类的每个切点的方法列表,定义如下:static Map&Class&?&, Map&InterceptPoint, List&Method&&& interceptMethodsMap = new HashMap&&();我们在CGLibContainer的类初始化过程中初始化该对象,方法是分析每个带有@Aspect注解的类,这些类一般可以通过扫描所有的类得到,为简化起见,我们将它们写在代码中,如下所示:static Class&?&[] aspects = new Class&?&[] { ServiceLogAspect.class, ExceptionAspect.class };分析这些带@Aspect注解的类,并初始化interceptMethodsMap的代码如下所示:static {
init();}private static void init() {
for (Class&?& cls : aspects) {
Aspect aspect = cls.getAnnotation(Aspect.class);
if (aspect != null) {
Method before = getMethod(cls, "before", new Class&?&[] {
Object.class, Method.class, Object[].class });
Method after = getMethod(cls, "after",
new Class&?&[] {
Object.class, Method.class, Object[].class, Object.class });
Method exception = getMethod(cls, "exception",
new Class&?&[] {
Object.class, Method.class, Object[].class, Throwable.class });
Class&?&[] intercepttedArr = aspect.value();
for (Class&?& interceptted : intercepttedArr) {
addInterceptMethod(interceptted, InterceptPoint.BEFORE, before);
addInterceptMethod(interceptted, InterceptPoint.AFTER, after);
addInterceptMethod(interceptted, InterceptPoint.EXCEPTION, exception);
}}对每个切面,即带有@Aspect注解的类cls,查找其before/after/exception方法,调用方法addInterceptMethod将其加入目标类的切点方法列表中,addInterceptMethod的代码为:private static void addInterceptMethod(Class&?& cls,
InterceptPoint point, Method method) {
if (method == null) {
Map&InterceptPoint, List&Method&& map = interceptMethodsMap.get(cls);
if (map == null) {
map = new HashMap&&();
interceptMethodsMap.put(cls, map);
List&Method& methods = map.get(point);
if (methods == null) {
methods = new ArrayList&&();
map.put(point, methods);
methods.add(method);}准备好了每个类的每个切点的方法列表,我们来看根据类型创建实例的代码:private static &T& T createInstance(Class&T& cls)
throws InstantiationException, IllegalAccessException {
if (!interceptMethodsMap.containsKey(cls)) {
return (T) cls.newInstance();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls);
enhancer.setCallback(new AspectInterceptor());
return (T) enhancer.create();}如果类型cls不需要增强,则直接调用cls.newInstance(),否则使用cglib创建动态代理,callback为AspectInterceptor,其代码为:static class AspectInterceptor implements MethodInterceptor {
public Object intercept(Object object, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
//执行before方法
List&Method& beforeMethods = getInterceptMethods(
object.getClass().getSuperclass(), InterceptPoint.BEFORE);
for (Method m : beforeMethods) {
m.invoke(null, new Object[] { object, method, args });
// 调用原始方法
Object result = proxy.invokeSuper(object, args);
// 执行after方法
List&Method& afterMethods = getInterceptMethods(
object.getClass().getSuperclass(), InterceptPoint.AFTER);
for (Method m : afterMethods) {
m.invoke(null, new Object[] { object, method, args, result });
} catch (Throwable e) {
//执行exception方法
List&Method& exceptionMethods = getInterceptMethods(
object.getClass().getSuperclass(), InterceptPoint.EXCEPTION);
for (Method m : exceptionMethods) {
m.invoke(null, new Object[] { object, method, args, e });
}}这个代码也容易理解,它根据原始类的实际类型查找应该执行的before/after/exception方法列表,在调用原始方法前执行before方法,执行后执行after方法,出现异常时执行exception方法,getInterceptMethods方法的代码为: static List&Method& getInterceptMethods(Class&?& cls,
InterceptPoint point) {
Map&InterceptPoint, List&Method&& map = interceptMethodsMap.get(cls);
if (map == null) {
return Collections.emptyList();
List&Method& methods = map.get(point);
if (methods == null) {
return Collections.emptyList();
return}这个代码也容易理解。CGLibContainer最终的getInstance方法就简单了,它调用createInstance创建实例,代码如下所示:public static &T& T getInstance(Class&T& cls) {
T obj = createInstance(cls);
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
if (f.isAnnotationPresent(SimpleInject.class)) {
if (!f.isAccessible()) {
f.setAccessible(true);
Class&?& fieldCls = f.getType();
f.set(obj, getInstance(fieldCls));
} catch (Exception e) {
throw new RuntimeException(e);
}}完整的代码可以在github上获取,文末有链接。这个AOP的实现是非常粗糙的,主要用于解释动态代理的应用和AOP的一些基本思路和原理。小结本节探讨了Java中的代理,从静态代理到两种动态代理,动态代理广泛应用于各种系统程序、框架和库中,用于为应用程序员提供易用的支持、实现AOP、以及其他灵活通用的功能,理解了动态代理,我们就能更好的利用这些系统程序、框架和库,在需要的时候,也可以自己创建动态代理。下一节,我们来进一步理解Java中的类加载过程,探讨如何利用自定义的类加载器实现更为动态强大的功能。(与其他章节一样,本节所有代码位于 ,位于包shuo.laoma.dynamic.c86下)----------------未完待续,查看最新文章,敬请关注微信公众号“老马说编程”(扫描下方二维码),从入门到高级,深入浅出,老马和你一起探索Java编程及计算机技术的本质。用心原创,保留所有版权。
免责声明:本站部分内容、图片、文字、视频等来自于互联网,仅供大家学习与交流。相关内容如涉嫌侵犯您的知识产权或其他合法权益,请向本站发送有效通知,我们会及时处理。反馈邮箱&&&&。
学生服务号
在线咨询,奖学金返现,名师点评,等你来互动

我要回帖

更多关于 jvm 对象头 的文章

 

随机推荐