`
ray_yui
  • 浏览: 220584 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

实现AOP — CGLIB

    博客分类:
  • Java
阅读更多

AOP系列文章:
      Spring AOP:http://ray-yui.iteye.com/blog/2024759
               CGLIB:http://ray-yui.iteye.com/blog/2026426
          Javassist:http://ray-yui.iteye.com/blog/2029261


什么是CGLIB?
      CGLIB是一个强大的高性能的代码生成包.它广泛的被许多AOP的框架使用,例如Spring AOP.诸如EasyMock和JMock等通过模仿对象来测试java代码的包都使用CGLIB.他们都通过使用CGLIB来为那些没有实现接口的类创建代理,流行ORM框架Hibernate亦使用CGLIB来实现延迟加载和单端映射(新版本Hibernate已直接依赖更底层的ASM),CGLIB底层通过一个小而快的字节码处理框架ASM,来转换字节码并生成新的类.ASM是低級的字节码生成工具,使用ASM已经近乎接近使用Java bytecode编程,而使用CGLIB更像是对ASM进行的高级化封装.


为什么要使用CGLIB?
      JDK内置的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的类必须实现一个或多个接口.注意必须是接口,如果想代理没有实现接口的类,JDK内置的动态代理就显得非常无力,此时CGLIB就显出了它的魅力,而且CGLIB因為使用直接生成字节码的方式,效率更高


CGLIB使用:

public class TestMain {

	// 第一种写法,使用Enhancer静态create方法
	@Test
	public void testCGLIBProxy01() {

		/*
		 * create有多种重载方式,此重载第一个参数为超类(本类)的类型,
		 * 若然需要动态代理的类没有实现接口,就需要填写第一个参数,
		 * 第二个参数为Class类型的数组,当动态代理的类有实现接口,
		 * 可以选择性填写第二个参数,否则为null
		 */
		RayTest test = (RayTest) Enhancer.create(RayTest.class, null,
				new MethodInterceptor() {

					// 真实主题
					RayTest test = new RayTest();

					@Override
					public Object intercept(Object arg0, Method method,
							Object[] args, MethodProxy arg3) throws Throwable {

						// 动态代理增加的logic
						String before = "before ";
						// 使用MethodProxy调用效率更高
						Object str = arg3.invoke(test, args);
						String after = " after";
						return before + str + after;
					}
				});

		Assert.assertEquals("before RayTest after", test.execute());
	}

	// 第二种写法,创建Enhancer对象
	@Test
	public void testCGLIBProxy2() {
		// 创建Enhancer类
		Enhancer enhancer = new Enhancer();
		// 当类没有实现接口而又需要动态代理,使用setSuperclass
		enhancer.setSuperclass(RayTest.class);
		// 当类实现了接口需要动态代理,使用setInterface
		enhancer.setInterfaces(new Class[] {});
		enhancer.setCallback(new MethodInterceptor() {
			// 真实主题
			RayTest test = new RayTest();

			@Override
			public Object intercept(Object arg0, Method method, Object[] args,
					MethodProxy arg3) throws Throwable {

				// 动态代理增加的logic
				String before = "before ";
				// 使用MethodProxy调用效率更
				Object str = arg3.invoke(test, args);
				String after = " after";
				return before + str + after;
			}
		});

		Assert.assertEquals("before RayTest after", enhancer.create());

	}
}


      通过上面的代码可以看出.真正处理代理业务逻辑也就是JDK动态代理中InvocationHandler中invoke方法的是使用enhancer.setCallback传递Callback接口实现来进行.上面例子中我们使用MethodInterceptor,MethodInterceptor能满足所有拦截的业务需求,但一些已经普遍存在的动态代理使用方式,例如使用动态代理实现延迟加载,CGLIB为我们提供了更方便和简化的实现

public class TestMain {

	private Enhancer enhancer;

	@Before
	public void init() {
		this.enhancer = new Enhancer();
		enhancer.setSuperclass(RayTest.class);
	}

	@Test
	public void testCGLIBUseSuperClass() {
		// NoOp实现类会使用默认的父类实现,没增加任何logic
		enhancer.setCallback(NoOp.INSTANCE);
		RayTest test = (RayTest) enhancer.create();
		Assert.assertNotNull(test);
	}

	@Test
	public void testCGLIBLazyObject() {
		//实现LazyLoader接口,在创建对象时可以增加业务logic和创建对象的子类等
		enhancer.setCallback(new LazyLoader() {

			@Override
			public Object loadObject() throws Exception {
				final RayTest lazyTest = new RayTest();
				return lazyTest;
			}
		});
		RayTest test = (RayTest) enhancer.create();
		Assert.assertNotNull(test);
	}
}


      由于CGLIB可以不需要实现接口来实现动态代理,其原理是通过字节码生成类的一个子类来完成的,那就有可能出现需要动态代理对象不存在一个无参构造函数,那么CGLIB在生成子类并实例化时将会产生错误

public class TestMain {

	/*
	 * 我們假设RayTest类不存在默认构造函数,
	 * 只提供需要传入 String和Integer作为参数的构造函数
	 */
	private Enhancer enhancer;

	@Before
	public void init() {
		this.enhancer = new Enhancer();
		enhancer.setSuperclass(RayTest.class);
	}

	public void testNotExistDefaultConstructor() {
		enhancer.setCallback(NoOp.INSTANCE);
		// 可以使用create的重载方式,参数1是构造函数参数类型,参数2是值
		RayTest test = (RayTest) enhancer.create(new Class[] { String.class,
				Integer.class }, new Object[] { "Hello World", 100 });
		Assert.assertNotNull(test);
	}
}


      在我们真实开发当中,在使用动态代理进行方法请求拦截时,可能会需要判断调用的方法然后决定拦截的逻辑,也就是同一个代理类在调用不同的方法时拦截的逻辑都不相同,CGLIB提供了CallbackFilter来帮助我们实现这一功能

public class TestMain {
	private Enhancer enhancer;

	@Before
	public void init() {
		this.enhancer = new Enhancer();
		enhancer.setSuperclass(RayTest.class);
	}

	@Test
	public void testCGLIBCallbackFilter() {

		// 创建callback1
		Callback callback1 = new MethodInterceptor() {
			// 真是主题类
			RayTest test = new RayTest();

			@Override
			public Object intercept(Object obj, Method method, Object[] args,
					MethodProxy proxy) throws Throwable {
				String before = "callback1 before ";
				Object str = proxy.invoke(test, args);
				String after = " callback1 after";
				return before + str + after;
			}
		};
		// 创建callback2
		Callback callback2 = new MethodInterceptor() {

			// 真是主题类
			RayTest test = new RayTest();

			@Override
			public Object intercept(Object obj, Method method, Object[] args,
					MethodProxy proxy) throws Throwable {
				String before = "callback2 before ";
				Object str = proxy.invoke(test, args);
				String after = " callback2 after";
				return before + str + after;
			}
		};
		// 使用setCallbacks设置多个Callback
		enhancer.setCallbacks(new Callback[] { callback1, callback2 });
		enhancer.setCallbackFilter(new CallbackFilter() {
			static final int EXECUTE_METHOD = 0;
			static final int OTHER_METHOD = 1;

			/*
			 * accept需要返回一個int类型,
			 * 该int类型为上文中setCallbacks设置的多个
			 * Callback处理逻辑的数组的下标,上文中设置了两个Callback,
			 * 分别为callback1和callback2
			 */

			@Override
			public int accept(Method method) {
				/*
				 * Method参数代表代理类的执行方法, 
				 * 以下logic为 判断执行方法名称是否为execute,
				 * 是则执行callback1,也就是数组下标为0的逻辑, 
				 * 否则执行Other逻辑
				 */
				if ("execute".equals(method.getName()))
					return EXECUTE_METHOD;
				else
					return OTHER_METHOD;
			}
		});
		RayTest test = (RayTest) enhancer.create();
		String executeResult = test.execute();
		Assert.assertEquals("callback1 before execute callback1 after",
				executeResult);
		String otherResult = test.action();
		Assert.assertEquals("callback2 before action callback2 after",
				otherResult);
	}
}


总结:在传统使用JDK动态代理时,会受限于必须实现接口而带来不便,若然该类是自己编写的话还可以抽象出多一层接口,若然该类是别人编写好并且已经正在生产使用,那就没有任何改动的理由,CGLIB帮助我们解决了没实现接口的继承问题,而且使用简单,提供了更多的功能例如CallbackFilter,这些都是JDK动态代理所没有的,而且效率也要高于JDK动态代理
9
4
分享到:
评论
2 楼 ghyghost 2014-03-06  
靠,这才是文章!有技术含量!
1 楼 完美天龙 2014-03-06  
不错的文章,save it now了,以后有空再研究。

相关推荐

    AOP使用CGLIB实现AOP功能

    Spring AOP实现方法之一:CGLIB 实现AOP功能

    spring_aop_cglib的实现方式

    在Spring AOP(面向切面编程)中,CGLIB是一个重要的动态代理库,它用于在运行时创建子类以实现对目标对象的代理。CGLIB是针对那些不支持接口代理(例如Java中的final类)的情况而设计的。下面我们将深入探讨Spring ...

    使用JDK中的Proxy技术实现AOP功能与使用CGLIB实现AOP功能

    本篇将深入探讨如何使用JDK的动态代理和CGLIB库来实现Spring中的AOP功能。 首先,我们来看JDK中的Proxy技术。JDK Proxy是Java提供的一种动态代理机制,它允许我们在运行时创建一个实现了特定接口的新类。这个新类...

    AOP-CGLIB学习-实现简单的注解权限系统

    标题中的"AOP-CGLIB学习-实现简单的注解权限系统"指的是使用Spring的面向切面编程(AOP)特性,结合CGLIB库来创建一个基于注解的权限控制系统。在这个系统中,通过CGLIB动态代理技术,可以对带有特定注解的方法进行...

    AOP、CGLIB

    在Spring AOP中,CGLIB被用来创建代理对象,当目标对象没有实现接口时,Spring会使用CGLIB来动态生成子类,以此实现对目标对象的代理。CGLIB的使用避免了反射带来的性能损失,提高了代理效率。 CGLIB的工作原理: ...

    JavaEE CGLIB字节码增强方式实现AOP编程

    JavaEE CGLIB字节码增强技术是实现面向切面编程(AOP)的一种常见方法。在JavaEE应用中,AOP允许开发者定义“切面”,这些切面封装了跨越多个对象的横切关注点,如日志、事务管理等。CGLIB(Code Generation Library...

    AOP编程示例

    以下是一段使用CGlib实现AOP的简单代码: ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method...

    基于Cglib简单实现Spring体系(Ioc+Aop+Mvc)

    基于Cglib简单实现Spring体系(Ioc+Aop+Mvc)基于Cglib简单实现Spring体系(Ioc+Aop+Mvc)基于Cglib简单实现Spring体系(Ioc+Aop+Mvc)基于Cglib简单实现Spring体系(Ioc+Aop+Mvc)基于Cglib简单实现Spring体系(Ioc+Aop+Mvc)...

    Xml配置实现AOP

    基于代理的AOP实现主要涉及到两种代理方式:JDK动态代理和CGLIB代理。Spring会根据目标对象是否实现了接口来选择合适的代理方式。 - **JDK动态代理**:如果目标对象实现了至少一个接口,Spring会选择JDK动态代理。...

    Spring实现AOP的4种方式 - Java -

    对于未实现接口的对象,Spring会使用CGLIB库生成目标类的子类,从而实现AOP。CGLIB是一个强大的代码生成库,Spring利用它在运行时动态创建子类,为方法添加拦截逻辑。在子类中,Spring会覆盖父类方法,并在调用原始...

    反射实现 AOP 动态代理模式(Spring AOP 的实现原理)

    如果被代理的类没有实现接口,Spring AOP会采用CGLIB来生成代理对象。CGLIB(Code Generation Library)是一个开源的代码生成库,它允许运行时在内存中动态生成类和对象。 在Spring AOP中,我们通常使用@Aspect注解...

    AOP之JDK动态代理和CGLib动态代理

    Spring框架是AOP实现的一个典范,它提供了两种主要的动态代理方式:JDK动态代理和CGLib动态代理。 **JDK动态代理**: JDK动态代理基于Java的反射API实现,适用于接口代理。当目标对象实现了至少一个接口时,Spring...

    Spring-AOP-JDK动态代理

    5. **配置代理**:Spring会根据目标对象是否实现了接口来决定使用JDK动态代理还是CGLIB代理。如果目标对象实现了接口,Spring会选择JDK动态代理。动态代理类会继承自`java.lang.reflect.Proxy`,并实现目标对象的...

    springAop.rar_AOP java_cglib_spring aop

    CGLIB是一个强大的高性能的代码生成库,它在许多AOP的实现中被用到,比如Spring AOP。CGLIB使用字节码技术为一个类创建子类,并在子类中拦截方法调用,从而实现代理。CGLIB生成的子类是动态的,因此在编译时并不存在...

    Spring学习笔记(14)----使用CGLIB实现AOP功能

    总结起来,Spring使用CGLIB实现AOP功能,主要是通过动态生成目标类的子类并插入拦截代码来实现切面的织入。理解CGLIB的工作原理以及如何在Spring中配置和使用它,对于提升Spring AOP的应用能力至关重要。通过阅读...

    Spring实现AOP的4种方式

    本篇文章将详细探讨Spring实现AOP的四种主要方法:基于代理的方式、基于AspectJ的注解方式、基于XML的AOP配置以及基于Java的AOP配置。 1. 基于代理的实现 Spring的AOP支持两种代理类型:JDK动态代理和CGLIB代理。...

    cglib及其依赖包

    2. 设置回调函数,即MethodInterceptor,这是CGLib实现动态代理的关键,它定义了当代理对象调用方法时执行的逻辑。 3. 调用Enhancer的create()方法,生成代理对象。 在Spring AOP中,CGLib通过Enhancer和...

    反射实现 AOP 动态代理模式(Spring AOP 的实现 原理) - Java 例子 -

    它不仅可以处理接口代理,还可以处理基于类的代理,支持CGLIB库生成字节码实现。此外,Spring AOP还提供了一套强大的切点表达式和注解,使得切点和通知的定义更加简洁直观。 总结来说,Spring AOP通过动态代理实现...

    JavaEE spring自动实现AOP代理

    Spring 提供了两种方式来实现AOP代理:JDK动态代理和CGLIB代理。 1. **JDK动态代理**: - JDK动态代理基于Java的接口实现,适用于目标对象实现了接口的情况。Spring会为这个接口创建一个代理类,代理类在调用真实...

    Java字节码实现Aop

    总结来说,Java字节码实现AOP是一种高效且灵活的技术手段,通过ASM、CGLIB等字节码工具,可以在运行时动态地修改类的行为,实现面向切面编程。理解并掌握这项技术,对于提升Java开发效率和代码质量具有重要意义。

Global site tag (gtag.js) - Google Analytics