今天学习了,spring中的cglib代理实现,在正式开始学习cglib代理之前,我们先来学习自定义的代理,以及jdk为我们提供的代理模式。那么什么是代理模式,说的直白一点,就是用一个代理来实现我对具体对象的访问,或者操作,比如我们学习android开发的时候需要更新sdk,可是google的android开发官方网站是访问不到的(其实百度浏览器是可以的),这时候我们就需要了,而我们所用到的网络就可以称之为一个代理服务器,通过该代理,我们实现具体网站的访问,是不是很好理解呢。我们先来看看没有使用代理的例子:
我们这个例子以添加用户举例:
首先我新建一个接口,用来定义添加用户操作的行为:
public interface UserDao {
public void add();
}
然后新建一个实现类UserDaoImpl.java,在进行添加用户之前需要开启事物,添加完成之后,需要关闭事物。如下:
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("开启事务.....");
System.out.println("执行了用户新增.....");
System.out.println("执提交事务.....");
}
}
可是这样的代码,耦合性很高,很难维护,比如:如果这个时候,我需要添加一个新注册的用户添加呢,这个时候就需要在新建一个UserRegDaoImpl然后实现UserDao接口,如果还有其他种类的用户新增的话,那么后果不堪设想啊。可以发现,利用传统的写法,没增加一个用户的种类,我们都需要新建一个该种类的实现类,然后实现UserDao接口,这样做代码就很难维护。接下来我们用代理类来实现这个需求:
public class UserDaoProxy implements UserDao {
private UserDao target;//被代理目标 UserDaoImpl实例
public UserDaoProxy(UserDao userDao){
this.target=userDao;
}
public void add() {
System.out.println("开启事务...");
target.add();//System.out.println("执行了用户新增.....");
System.out.println("提交事务...");
}
}
可以看到,此时我新建了一个UserDaoProxy的用户代理类。并且实现UserDao接口,此时我们并不需要关心具体添加的用户是什么类型的,我们需要做的就是为target属性设置需要被代理的用户类型即可,是不是很大程度降低了程序的耦合度。
此时运行改程序打印结果如下:
开启事务...
执行了用户新增.....
提交事务...
下面来看看jdk中提供给我们代理是如何使用的,同样是新增用户的栗子:
在jdk中为我们提供了一个Proxy类,该类有这样一个静态方法可以实现代理Proxy.newProxyInstance(ClassLoader classLoader,Class>interfaces,InvokecationHandler invokecationHandler);
这里有三个参数,非别代表如下意思:
classLoader:得到被代理类的类加载器
interfaces : 得到被代理类实现实现的所有接口
invokecationHandler 该类用于回调被代理类中的方法
具体代码如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 前提:被代理目标必须实现接口
* 生成的代理为接口的实现类
* @author Administrator
*
*/
public class JDKProxy {
private Object target;//被代理目标
/**
* 动态生成指定的目标的代理对象
* @param objecet :被代理目标
* @return:代理
*/
public Object createProxy(Object object){
target=object;
/**
* loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new MyInvocationHandler());
}
class MyInvocationHandler implements InvocationHandler{
/**
* proxy - 在其上调用方法的代理实例
method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("开启事务...");
Object returnValue = method.invoke(target, args);//回调被代理目标的方法userDaoImpl.add();
System.out.println("提交事务");
return returnValue;
}
}
public static void main(String[] args) {
JDKProxy jdkProxy = new JDKProxy();
UserDao userDao = (UserDao)jdkProxy.createProxy(new UserDaoImpl());
userDao.add();
}
}
代码中的注释比较详细,这里我就不详细解释了,需要注意一点:被代理的目标必须实现接口
下面我们在利用spring为我们提供的jar包(cglib-nodep-2.1.jar),该jar中为我们提供了cglib代理的实现,同样是添加用户的例子:
package com.test.aop2;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 前提: 要求被代理目标必须为非final的类(final修饰的类不能被继承)
* 生成一个代理:被代理目标的子类
* @author Administrator
*
*/
public class CglibProxy {
private Object target;
public Object createProxy(Object object){
this.target = object;
Enhancer enhancer = new Enhancer();
//设置父类(被代理目标)
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new MyIntercetpor());
return enhancer.create();//创建代理
}
/**
* 类似于jdk动态代理中MyInvocationHandler
* @author Administrator
*
*/
class MyIntercetpor implements MethodInterceptor{
@Override
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("开启事务..");
Object returnValue = method.invoke(target, args);
System.out.println("提交事务....");
return returnValue;
}
}
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao dao = (UserDaoImpl)cglibProxy.createProxy(new UserDaoImpl());
dao.add();
}
}
同样,我们利用createProxy方法来生成代理,同样需要注意一点:被代理目标必须为非final的类。打印结果如下:
开启事务..
执行了用户新增.....
提交事务....
好了,三种不同方法实现代理,就到这里了。
源码下载