提及Spring的AOP(Aspect-Oriented Programming)面向切面编程你们都很熟悉(Spring不是此次博文的重点),可是我先提出几个问题,看看同窗们是否了解,若是了解的话能够不用继续往下读:html
1. Spring的AOP的实现方式有哪些?java
2. 为何使用动态代理?编程
3. 它们是怎么实现的?数组
4. 它们的区别是什么?ide
下面进入正题,Spring采用动态代理的方式实现AOP,具体采用了JDK的动态代理和CGLib的动态代理。使用动态代理的目的是在现有类的基础上增长一些功能。简单地将就是有一个Proxy类,实现了原始类的方法,而且在原始类的基础上增长了新的功能。那么这么作能够实现不少功能:this
1. 在方法先后进行日志处理。spa
2. 进行额外的校验,好比参数的验证功能等。代理
3. 实现一些懒加载,也就是实例化的时候若是不去调用真正的方法的时候,这个类的属性就不会存在(Hibernate有这样相似的功能)。日志
下面我们用简单的代码实现它是如何进行代理的,首先采用的是JDK的动态代理实现:code
定义一个接口:
定义一个实现类:
package com.hqs.proxy; /** * Mac 的实现 * @author hqs * */ public class Mac implements OpSystem { public void work() { System.out.println("Mac is running"); } }
关键位置来了,咱们经过实现JDK自带的反射机制的包的InvocationHandler来进行反射处理,实现它以后须要实现里边的invoke方法,这个invoke方法里边的参数分别为:代理类实例,用于调用method的;method参数是实际执行的方法;args所传输的参数数组。
package com.hqs.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class OpHandler implements InvocationHandler { private final OpSystem ops; public OpHandler(OpSystem ops) { this.ops = ops; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before system running"); method.invoke(ops, args); System.out.println("After system running"); return null; } public static void main(String[] args) { Mac mac = new Mac(); OpHandler oph = new OpHandler(mac); OpSystem os = (OpSystem)Proxy.newProxyInstance(oph.getClass().getClassLoader(), mac.getClass().getInterfaces(), oph); os.work(); System.out.println(os.getClass()); } } 输出: Before system running Mac is running After system running class com.sun.proxy.$Proxy0
而后看到里边的main方法中,代理类实例化对象的方法Proxy.newProxyInstance,这个是JDK的反射方法去实例化代理类,其中有三个分别是,去实例化代理类的class loader;所代理的类的全部接口Class数组;hander处理类,用于作拦截使用的类。最后我输出了一下os.getClass(),你们能够看到的是代理类的实例,而不是真正代理类的实例,这么作的好处就是很方便的复用这个代理类,好比你能够重复调用它而不用去从新实例化新类,再一点就是你能够针对不一样的方法进行拦截,好比你能够method.getName()去判断调用的方法名字是什么从而更细粒度的拦截方法。我们继续看用CGLib的实现:
package com.hqs.proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * CGLib Interceptor用于方法拦截 * @author hqs * */ public class CGLibInterceptor implements MethodInterceptor { private final Mac mac; public CGLibInterceptor(Mac mac) { this.mac = mac; } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("Before system running"); method.invoke(mac, args); System.out.println("After system running"); return null; } public static void main(String[] args) { Mac mac = new Mac(); //实例而非接口 MethodInterceptor handler = new CGLibInterceptor(mac); Mac m = (Mac)Enhancer.create(mac.getClass(), handler); m.work(); System.out.println(m.getClass()); } } 输出: Before system running Mac is running After system running class com.hqs.proxy.Mac$$EnhancerByCGLIB$$1f2c9d4a
首先须要引入cglib包,而后才能使用他的MethodInterptor,它也采用method.invoke实现对代理类的调用。它的代理类建立采用Enhancer的create方法,其中传入了须要建立的类的class,以及Callback对象,由于MethodInterceptor继承了Callback对象。用于指向方法先后进行调用的类。
public interface MethodInterceptor extends Callback
这是这两个类的基本实现,那么它们的区别是什么呢?
这些是它们的根本区别,可是Spring推荐使用JDK的动态代理,面向接口去编程。使用CGLib去作动态代理的时候须要注意,它生产的代理类存放在JVM的Perm space里边,那么是否是生成的代理对象就不进行回收了?其实不是的,不常常回收可是仍是回收的,当类被加载,加载类的classLoader何时变得对垃圾回收可用的时候才进行回收。也就是你本身建立全部类移除classLoader以后,那么这个classLoader就会被回收,通常很是精通CGLib的话能够进行这块内容深刻开发,由于它能够作出amzing的事情若是你熟悉的话。