您的位置:首页 > 运维架构

AOP技术应用和研究--动态代理

2014-09-17 20:44 295 查看
在SpringAop的实现中,我们首先需要知道的就是动态代理。

1,cglib动态代理

先看一个简单的cglib动态代理实例,我们假设的场景是在web开发中,用户实体访问数据库的Dao层UserDao实现事务处理这项功能。具体代码如下:

package cn.miao.proxy.cglib;

public class User {
private String username;
private String password;

public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}

}
package cn.miao.proxy.cglib;
/**
* 事务处理类
* @author demiaowu
*
*/
public class Transaction {

public void beginTransaction() {
System.out.println("begin transaction");
}

public void commit() {
System.out.println("commit");
}
}

package cn.miao.proxy.cglib;

import org.junit.Test;
/**
* cglib动态代理示例测试客户端
* @author demiaowu
*
*/
public class UserClient {

@Test
public void clientTest() {
User user = new User("demiaowu","123456");
//实例化transaction
Transaction transaction = new Transaction();
UserDaoImpl target = new UserDaoImpl();
UserMethodInterceptor inteceptor = new UserMethodInterceptor(transaction, target);
UserDaoImpl proxy = (UserDaoImpl) inteceptor.getProxy();
proxy.saveUser(user);
}
}

package cn.miao.proxy.cglib;

public class UserDaoImpl{

public void saveUser(User user) {
System.out.println("saveUser");
}

public void updateUser(User user) {
System.out.println("updateUser");
}

public void deleteUser(String username) {
System.out.println("deleteUser");
}

}

package cn.miao.proxy.cglib;

import java.lang.reflect.Method;

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

public class UserMethodInterceptor implements MethodInterceptor {
private Transaction transaction;
private Object target;

public UserMethodInterceptor(Transaction transaction, Object target) {
this.transaction = transaction;
this.target = target;
}
public Object getProxy() {
Enhancer enhancer = new Enhancer();
//设置需要创建子类的类
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
public Object intercept(Object arg0, Method method, Object[] arg2,
MethodProxy proxy) throws Throwable {
Object retValue;
String methodName = method.getName();
if ("saveUser".equals(methodName) || "updateUser".equals(methodName) || "deleteUser".equals(methodName)) {
this.transaction.beginTransaction();
retValue = method.invoke(this.target, arg2);
this.transaction.commit();
} else {
retValue = method.invoke(this.target, arg2);
}
return retValue;
}
}
写的jdk实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,就需要cglib了。cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用。

cglib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。其原理是Enhancer类通过字节码技术为原有的类创建子类,并且设置好callback到proxy,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept()函数。

2,jdk动态代理

直接看例子吧。
package cn.miao.proxy.jdk;
/**
* 事务处理类
* @author demiaowu
*
*/
public class Transaction {

public void beginTransaction() {
System.out.println("begin transaction");
}

public void commit() {
System.out.println("commit");
}
}
package cn.miao.proxy.jdk;
/**
* 用户类
* @author demiaowu
*
*/
public class User {
private String username;
private String password;

public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}

}

package cn.miao.proxy.jdk;

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

import org.junit.Test;
/**
* 客户端测试类
* @author demiaowu
*
*/
public class UserClient {

@Test
public void clientTest() {
User user = new User("demiaowu","123456");
//实例化transaction
Transaction transaction = new Transaction();
//实例化目标对象
UserDao target = new UserDaoImpl();
//实例化InvocationHandler
InvocationHandler userHandler = (InvocationHandler) new UserInvocationHandler(transaction, target);
//生成代理对象
UserDao proxy = (UserDao)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), userHandler);
proxy.saveUser(user);
}
}

package cn.miao.proxy.jdk;

public interface UserDao {

public void saveUser(User user);
public void updateUser(User user);
public void deleteUser(String username);
}

package cn.miao.proxy.jdk;

public class UserDaoImpl implements UserDao {

public void saveUser(User user) {
System.out.println("saveUser");
}

public void updateUser(User user) {
System.out.println("updateUser");
}

public void deleteUser(String username) {
System.out.println("deleteUser");
}

}

package cn.miao.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* User条用处理类
* @author demiaowu
*
*/
public class UserInvocationHandler implements InvocationHandler {
private Transaction transaction;
private Object target;

public UserInvocationHandler(Transaction transaction, Object target) {
this.transaction = transaction;
this.target = target;
}
/**
* proxy 是Proxy类要为你生成的代理类实例
* method 条用的方法是Method的实例,如果调用saveUser()方法,那么就是saveUser()的Method实例
* args 调用方法传入的参数,如果调用saveUser(User user)方法,那么就是传入的参数user
* return 使用代理后将作为调用方法后的返回值,如果调用saveUser(User user)方法,那么就是它的返回值
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object retValue = null;
String mehodName = method.getName();
if ("saveUser".equals(mehodName) || "deleteUser".equals(mehodName) || "updateUser".equals(mehodName)) {
this.transaction.beginTransaction();
retValue = method.invoke(this.target, args);
this.transaction.commit();
} else {
retValue = method.invoke(this.target, args);
}
return retValue;
}

}

下面我们来看看了解下jdk动态代理的原理,动态代理还要从 jdk本身说起。在 jdk 的 java.lang.reflect 包下有个 Proxy 类,它正是构造代理类的入口。这个类的结构如下图3.4:



从上图发现最后面四个是公有方法。而最后一个方法 newProxyInstance 就是创建代理对象的方法。这个方法的源码如下:

/**
* loader:类加载器
* interfaces:目标对象实现的接口
* h:InvocationHandler的实现类
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
…//省略
Class cl = getProxyClass(loader, interfaces);
try {
// 生成代理对象的构造方法(也就是$Proxy4(InvocationHandler h))
Constructor cons = cl.getConstructor(constructorParams);
// 生成代理类的实例并把UserInvocationHandler的实例传给它的构造方法
return (Object) cons.newInstance(new Object[] { h });
}
…//省略
}


这个方法需要三个参数:ClassLoader,用于加载代理类的Loader 类,通常这个 Loader 和被代理的类是同一个Loader 类。Interfaces,是要被代理的那些那些接口。InvocationHandler,就是用于执行除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到 InvocationHandler 类中定义的唯一方法 invoke 中。这在后面再详解。

首先getProxyClass()方法生成代理类的Class,然后代理类的Class使用getConstructor()方法生成代理类的构造函数,也就是$Proxy4(InvocationHandlerh) 如图3.5 Proxy,



最后cons.newInstance(newObject[]{h})生成代理类的实例并把UserInvocationHandler的实例传给它的构造方法。这就是Proxy生成代理对象的整个过程。下面看看Proxy如何产生代理类的过程,如图3.6,创建代理对象的时序图



getProxyClass()是生成的代理Class。 getProxyClass()方法核心代码如下图3.7:



在getProxyClass()中使用ProxyGenerator类的静态方法generateProxyClass(),这里是真正生成代理类class字节码的地方。最后defineClass0()根据字节码生成代理类实例。

现在,jdk是怎样动态生成代理类的字节的原理已经一目了然了。再来解决另外一个问题,那就是由谁来调用InvocationHandler的invoke方法的。要解决这个问题就要看一下jdk到底为我们生成了一个什么东西。实际上生成的$Proxy4继承了Proxy类和UserDao接口。并且有一个数据成员InvocationHandler如图3.5。$Proxy的构造函数如下:

public$Proxy4(InvocationHandler invocationhandler) {
super(invocationhandler);
}


代理对象就是用上面的构造函数生成的。其中Proxy当然也生成了saveUser(User user)方法。这个方法实际上调用的就是UserInvocationHandler的invoke()方法。这样我们就把jdk和cglib动态代理的思想原理大致的描述完。

代码在:https://github.com/demiaowu/aop

jdk的示例代码代码在:src/cn/miao/proxy/jdk目录下面

cglib的示例代码在:src/cn/miao/proxy/cglib目录下面

AOP技术应用和研究系列博客
AOP技术应用和研究
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息