咱们知道,Spring AOP的主要做用就是不经过修改源代码的方式、将非核心功能代码织入来实现对方法的加强。那么Spring AOP的底层如何实现对方法的加强?实现的关键在于使用了代理模式
代理模式的做用就是为其它对象提供一种代理,以控制对这个对象的访问,用于解决在直接访问对象时带来的各类问题。代理主要分为两种方式:静态代理和动态代理java
静态代理主要经过将目标类与代理类实现同一个接口,让代理类持有真实类对象,而后在代理类方法中调用真实类方法,在调用真实类方法的先后添加咱们所须要的功能扩展代码来达到加强的目的
示例代码:程序员
/** * 代理类与目标类的共同接口 */ public interface Subject { void request(); void response(); } /** * 目标类 */ public class RealSubject implements Subject { @Override public void request() { System.out.println("执行目标对象的request方法......"); } @Override public void response() { System.out.println("执行目标对象的response方法......"); } } /** * 代理类 */ public class ProxySubject implements Subject { private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } @Override public void request() { System.out.println("before 前置加强"); subject.request(); System.out.println("after 后置加强"); } @Override public void response() { System.out.println("before 前置加强"); subject.response(); System.out.println("after 后置加强"); } } public class Main { public static void main(String[] args) { //目标对象 Subject realSubject = new RealSubject(); //代理对象 经过构造器注入目标对象 Subject proxySubject = new ProxySubject(realSubject); proxySubject.request(); proxySubject.response(); } }
运行结果:spring
before 前置加强 执行目标对象的request方法...... after 后置加强 before 前置加强 执行目标对象的response方法...... after 后置加强
经过以上的代码示例,咱们不难发现静态代理的缺点。假如咱们的Subject接口要增长其它的方法,则ProxySubject代理类也必须同时代理这些新增的方法。同时咱们也看到,request方法和response方法所织入的代码是同样的,这会使得代理类中出现大量冗余的代码,很是不利于扩展和维护。为了解决静态代理的这些缺陷,因而有了动态代理ide
与静态代理相比,动态代理的代理类不须要程序员本身手动定义,而是在程序运行时动态生成
动态代理能够分为JDK动态代理和CgLib动态代理this
JDK动态代理与静态代理同样,目标类须要实现一个代理接口,它的开发步骤以下:
1.定义一个java.lang.reflect.InvocationHandler接口的实现类,重写invoke方法
2.将InvocationHandler对象做为参数传入java.lang.reflect.Proxy的newProxyInstance方法中
3.经过调用java.lang.reflect.Proxy的newProxyInstance方法得到动态代理对象
4.经过代理对象调用目标方法
示例代码:代理
/** * 自定义InvocationHandler的实现类 */ public class JdkProxySubject implements InvocationHandler { private Subject subject; public JdkProxySubject(Subject subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before 前置通知"); Object result = null; try { result = method.invoke(subject, args); }catch (Exception ex) { System.out.println("ex: " + ex.getMessage()); throw ex; }finally { System.out.println("after 后置通知"); } return result; } } public class Main { public static void main(String[] args) { //获取InvocationHandler对象 在构造方法中注入目标对象 InvocationHandler handler = new JdkProxySubject(new RealSubject()); //获取代理类对象 Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler); //调用目标方法 proxySubject.request(); proxySubject.response(); } }
运行结果:code
before 前置通知 执行目标对象的request方法...... after 后置通知 before 前置通知 执行目标对象的response方法...... after 后置通知
CgLib动态代理的原理是对指定的业务类生成一个子类,并覆盖其中的业务方法来实现代理。它的开发步骤:
1.定义一个org.springframework.cglib.proxy.MethodInterceptor接口的实现类,重写intercept方法
2.获取org.springframework.cglib.proxy.Enhancer类的对象
3.分别调用Enhancer对象的setSuperclass和setCallback方法,使用create方法获取代理对象
4.经过代理对象调用目标方法
示例代码:对象
/** * 自定义MethodInterceptor实现类 */ public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before 前置通知"); Object result = null; try { result = methodProxy.invokeSuper(obj, args); }catch (Exception ex) { System.out.println("ex: " + ex.getMessage()); throw ex; }finally { System.out.println("after 后置通知"); } return result; } } public class Main { public static void main(String[] args) { //获取Enhancer 对象 Enhancer enhancer = new Enhancer(); //设置代理类的父类(目标类) enhancer.setSuperclass(RealSubject.class); //设置回调方法 enhancer.setCallback(new MyMethodInterceptor()); //获取代理对象 Subject proxySubject = (Subject)enhancer.create(); //调用目标方法 proxySubject.request(); proxySubject.response(); } }
运行结果:继承
before 前置通知 执行目标对象的request方法...... after 后置通知 before 前置通知 执行目标对象的response方法...... after 后置通知
JDK动态代理和CgLib动态代理的主要区别:
JDK动态代理只能针对实现了接口的类的接口方法进行代理
CgLib动态代理基于继承来实现代理,因此没法对final类、private方法和static方法实现代理接口
Spring AOP中的代理使用的默认策略是:
若是目标对象实现了接口,则默认采用JDK动态代理
若是目标对象没有实现接口,则采用CgLib进行动态代理
若是目标对象实现了接口,且强制CgLib代理,则采用CgLib进行动态代理