`

spring JDK的Proxy技术实现AOP功能和CGBLB-学习笔记

阅读更多

动态代理类(以下简称为代理类)是一个实现在创建类时在运行时指定的接口列表的类,该类具有下面描述的行为。 代理接口 是代理类实现的一个接口。 代理实例 是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序 对象,它可以实现接口 InvocationHandler。通过其中一个代理接口的代理实例上的方法调用将被指派到实例的调用处理程序的 Invoke 方法,并传递代理实例、识别调用方法的 java.lang.reflect.Method 对象以及包含参数的 Object 类型的数组。调用处理程序以适当的方式处理编码的方法调用,并且它返回的结果将作为代理实例上方法调用的结果返回。 

代理类具用以下属性: 

代理类是公共的、最终的,而不是抽象的。 
未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。 
代理类扩展 java.lang.reflect.Proxy。 
代理类会按同一顺序准确地实现其创建时指定的接口。 
如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。否则,代理类的包也是未指定的。注意,包密封将不阻止代理类在运行时在特定包中的成功定义,也不会阻止相同类加载器和带有特定签名的包所定义的类。 
由于代理类将实现所有在其创建时指定的接口,所以对其 Class 对象调用 getInterfaces 将返回一个包含相同接口列表的数组(按其创建时指定的顺序),对其 Class 对象调用 getMethods 将返回一个包括这些接口中所有方法的 Method 对象的数组,并且调用 getMethod 将会在代理接口中找到期望的一些方法。 
如果 Proxy.isProxyClass 方法传递代理类(由 Proxy.getProxyClass 返回的类,或由 Proxy.newProxyInstance 返回的对象的类),则该方法返回 true,否则返回 false。 
代理类的 java.security.ProtectionDomain 与由引导类加载器(如 java.lang.Object)加载的系统类相同,原因是代理类的代码由受信任的系统代码生成。此保护域通常被授予 java.security.AllPermission。 
每个代理类都有一个可以带一个参数(接口 InvocationHandler 的实现)的公共构造方法,用于设置代理实例的调用处理程序。并非必须使用反射 API 才能访问公共构造方法,通过调用 Proxy.newInstance 方法(将调用 Proxy.getProxyClass 的操作和调用带有调用处理程序的构造方法结合在一起)也可以创建代理实例。 
代理实例具有以下属性: 

提供代理实例 proxy 和一个由其代理类 Foo 实现的接口,以下表达式将返回 true: 
     proxy instanceof Foo 
并且以下的强制转换操作将会成功(而不抛出 ClassCastException): 
     (Foo) proxy 
每个代理实例都有一个关联的调用处理程序,它会被传递到其构造方法中。静态 Proxy.getInvocationHandler 方法将返回与作为其参数传递的代理实例相关的调用处理程序。 
代理实例上的接口方法调用将按照该方法的文档描述进行编码,并被指派到调用处理程序的 Invoke 方法。
在代理实例上的 java.lang.Object 中声明的 hashCode、equals 或 toString 方法的调用将按照与编码和指派接口方法调用相同的方式进行编码,并被指派到调用处理程序的 invoke 方法,如上所述。传递到 invoke 的 Method 对象的声明类是 java.lang.Object。代理类不重写从 java.lang.Object 继承的代理实例的其他公共方法,所以这些方法的调用行为与其对 java.lang.Object 实例的操作一样。 
在多代理接口中重复的方法 
当代理类的两个或多个接口包含一个具有相同名称和参数签名的方法时,代理类的接口顺序变得非常重要。在代理实例上调用重复方法 时,传递到调用处理程序的 Method 对象没有必要成为其声明类可以从接口(通过该接口调用代理方法)的引用类型指派的对象。此限制存在的原因是,生成的代理类中的相应方法实现无法确定它通过哪一个接口调用。因此,在代理实例上调用重复方法时,第一个接口中的方法的 Method 对象包含接口的代理类列表中的方法(直接或通过超级接口继承),该对象会传递到调用处理程序的 invoke 方法,无论该方法调用通过哪一种引用类型发生。 

如果代理接口包含某一方法,它的名称和参数签名与 java.lang.Object 的 hashCode、equals 或 toString 方法相同,那么在代理实例上调用这样的方法时,传递到调用处理程序的 Method 对象将使 java.lang.Object 成为其声明类。换句话说,java.lang.Object 公共的非最终方法理论上在所有代理接口之前,以便确定哪一个 Method 对象传递到调用处理程序。 

还要注意,当重复方法被指派到调用处理程序时,invoke 方法只可以抛出经过检查的异常类型,该异常类型可以使用所有 代理接口(可以通过它调用)中方法的 throws 子句指派一种异常类型。如果 invoke 方法抛出一个经过检查的异常,该异常没有指派给任何由一个代理接口(可以通过它调用)中的方法声明的异常类型,那么该代理实例上的调用将抛出一个未经检查的 UndeclaredThrowableException。此限制表示并非所有的由传递到 invoke 方法的 Method 对象上调用 getExceptionTypes 返回的异常类型都可以由 invoke 方法成功抛出。 

 

 

package com.sample.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.sample.service.impl.PersonServiceBean;

/**
 * JDK 代理对象
 * 
 * @author DYLAN
 * 
 */
public class JDKProxyFactory implements InvocationHandler{
	private Object targetObject; // 代理目标对象(接口) 代理对象实现了所代理的接口

	public Object createProxyInstance(Object targetObject) {
		this.targetObject = targetObject;
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
				targetObject.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		PersonServiceBean personServiceBean = (PersonServiceBean)targetObject;
		Object result = null;	//环绕通知
		if (personServiceBean.getUser() != null) {
			//advice() -- 前置通知
			try {
				//讲方法的调用委派给目前对象
				result = method.invoke(targetObject, args);
				//afteradvice()--后置通知
			} catch (Exception e) {
				// exceptionadvice() -- 例外通知
			} finally {
				//finallyadvice() -- 最终通知
			}
		}
		return result;
	}
}

spring CGLIB的动态代理(非spring架构)

 

 

package com.sample.aop;

import java.lang.reflect.Method;

import com.sample.service.impl.PersonServiceBean;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;


public class CGlibProxyFactory implements MethodInterceptor{
	
	private Object targetObject;	//代理目标对象
	
	public Object createProxyInstance(Object targetObject) {
		this.targetObject = targetObject;
		Enhancer enhancer = new Enhancer();	//该类用于生成代理对象
		enhancer.setSuperclass(this.targetObject.getClass());	//设置父类(目标类)覆盖目标类中所有非final方法
		enhancer.setCallback(this); //设置回调对象 本身
		return enhancer.create();	//返回代理对象
	}

	@Override
	public Object intercept(Object proxy, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		PersonServiceBean bean = (PersonServiceBean) this.targetObject;
		Object result = null;
		if (bean.getUser() != null) {
			result = methodProxy.invoke(targetObject, args);
		} else {
			System.out.println("user IS NULL!");
		}
		return null;
	}
}

需要jar包 cglib-nodep-2.1_3.jar 、junit-4.7.jar

CGLIB 可以生成目标类的子类,并重写父类所有非final修饰符的方法

 

@Test public void cglibProxyTest() {

CGlibProxyFactory proxyFactory = new CGlibProxyFactory();

PersonServiceBean service = (PersonServiceBean)proxyFactory.createProxyInstance(new PersonServiceBean("dylan_xu"));

service.save("aop");

}

 

 

以上的CGLIB的动态代理都是没有借助任何框架的情况下实现AOP的方法

 

最后附上spring3.0一些术语

 

Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是横切性关注点的抽象。

 

Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或者类构造器

 

Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义。

 

Advice(通知):所谓通知是指拦截到joinpoint之后索要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知。

 

Target(目标对象):代理的目标对象

 

Weave(织入):指将aspect应用到target对象并导致proxy对象创建的过程称为织入。

 

Introduction(引入):在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field.

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics