今天学习了,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的类。打印结果如下:
开启事务..
执行了用户新增.....
提交事务....
好了,三种不同方法实现代理,就到这里了。