要说知道AOP这个词却是好久好久之前了,可是直到今天我也不敢说很是的理解它,其中的各类概念即抽象又太拗口。
在几回面试中都被问及AOP,可是真的没有答上来,或者都在面上,这给面试官的感受就是java基础不行。可见这仍是挺重要的一个概念。java
在看工做中也遇到了相关的问题,在RPC的一种实现机制里应用了AOP,结果各类类一直绕来绕去看着头都大了,这也就是没有对动态代理和aop有了解致使的。面试
因此要好好的去掌握它,不然吃亏的仍是本身。spring
面向侧面的程序设计(aspect-oriented programming,AOP,又译做面向方面的程序设计、观点导向编程、剖面导向程序设计)是计算机科学中的一个术语,指一种程序设计范型。该范型以一种称为侧面(aspect,又译做方面)的语言构造为基础,侧面是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)。——维基百科编程
不知道看完这段话你能理解AOP是个啥吗?并不能,最多知道AOP是一种规范。因此说要理解AOP最重要仍是要从具体的实现入手,才能真正明白AOP究竟是干啥子的。app
其实就是一种代码加强方式,能够实现动态的代理,在运行期完成;也有一些是静态的为对象加强,在编绎期间完成。说的白话一点就是在对象进行代码上的扩展加强,就是说原先可能只能跑10行代码,加强后就能够多跑些代码,并且这种加强是经过织入的方式完成,而不是直接修改目标对象的代码。ide
为此仍是要先理解代理这个概念,能够更好的理解AOP,那么就从学习代理开始吧。模块化
//建立一个接口 public interface ISay { void say(); } //接口的实现类(能够理解为业务类) public class SayImpl implements ISay{ @Override public void say() { System.out.print("我是5207."); } } //只说一个名字太单调了,须要多多拉票,建立一个代理类来加强一下 class StaticSayImplProxy implements ISay { private ISay target; public StaticSayImplProxy(ISay target) { this.target = target; } @Override public void say() { SayHello(); this.target.say(); ThumbUp(); } void SayHello() { System.out.print("你们好:"); } void ThumbUp() { System.out.print("但愿你们多多点赞."); } } //调用代码 public class StaticProxy { public static void main(String[] args) { ISay aop = new SayImpl(); ISay aopProxy = new StaticSayImplProxy(aop); aopProxy.say(); } }
代码很简单,静态代理代码中经过增长一个StaticSayImplProxy类来对原先的实现类进行加强。注意这里的加强很重要。也就是本来只会说一句“我是5207.”,经过代理的类后就完整了许多。函数
可是静态代理的缺点是,代理时必须知道其类型是,好比上面代码中就必须知道ISay这个接口,最终才能在代理类里才能调用:this.target.say()这样的代码。那么也就是说若是还有另外的IOtherSay接口也须要用这个代理类就无法使用了。工具
因此若是能把代理集中一个类中,只要将要代理的对象给这个类就能代理是否是比较方便,代码少写好多。学习
办法固然是有的,JDK提供了一种动态代理的技术,能够动态的为接口建立代理对象,从而实现代理模式。那么接着上面的例子咱们须要作成动态代理代码以下:
class DynamicProxyImpl implements InvocationHandler { private Object target; public DynamicProxyImpl(Object target) { this.target = target; } @SuppressWarnings("unchecked") public <T> T getObject() { return (T) Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this ); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SayHello(); Object result = method.invoke(target, args); ThumbUp(); return result; } void SayHello() { System.out.print("你们好:"); } void ThumbUp() { System.out.println("但愿你们多多点赞."); } }
这里能够发现DynamicProxyImpl类并无指定具体代理的接口类型,而使用Object类型。这样就不用关心传入给这个代理类的具体对象了。因此说这个代理类的范围一会儿就大了许多,只要是相似的加强功能均可以用这个代理类来完成。举个例子:
public interface IOtherSay { void applause(); } public class OtherSayImpl implements IOtherSay { @Override public void applause() { System.out.print("你们鼓掌."); } } public static void main(String[] args) { IOtherSay oSay = new OtherSayImpl(); DynamicProxyImpl oSayProxy = new DynamicProxyImpl(oSay); IOtherSay oSay2 = oSayProxy.getObject(); oSay2.applause(); }
能够看到使用同一个代理类也能够代理IOtherSay接口派生的对象啦。
InvocationHandler接口
另一点就是DynamicProxyImpl实现了InvocationHandler接口,这个接口是关键,其实应当是JDK代理对象时的一个调用处理程序,这应当是暴露给开发者的代码加强接口啦。InvocationHandler接口只有一个invoke方法,咱们须要作的就是在这个方法中增长须要的加强代码。
Proxy工具类
对于getObject方法才是真正的代理对象生成的过程,能够看到最终是Proxy.newProxyInstance这个方法来完成代理对象生成并返回的。那么这里能够看看Proxy的设计与原理。
由于暂时只用到了newProxyInstance方法,就从它开始吧:
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
方法的三个参数中要注意interfaces和h
interfaces
是指的传入须要代理的接口列表,==这里有一点比较重要JDK的动态代理只能代理接口哦==。
h
另一个h则是指调用处理程序,发如今上面的DynamicProxyImpl类就是一个调用处理程序的实现,这里的关键就是InvocationHandler。
接着看方法体中的代码主要是几个过程:
这里还有些反射相关的知识就再也不说明了,可是有一个点比较好奇,就是最终代理对象是如何经过invoke把目标对象的方法代理的呢?
篇幅有点多就引用一篇吧JDK动态代理实现原理。
其实大致意思是最终JDK会自动的为指定的接口生成代理对象,而这个生成的代理对象就和前面手写的动态代理方法相似,只不过生成的代理类调用的是InvocationHandler的invoke。jdk帮咱们作了自动生成的过程,这样就能够在运行期生成代理类。
到此,最为重要的代理差很少说完了,这也就是AOP的奥秘所在。
了解了代理后再来看AOP的相关概念仍是理解不了,什么切面、链接点、通知、切入点之类的,因此说仍是抛开吧,这样要轻松许多。相信你们看aop的时候确定是看到了Advice这个东东吧?嗯,就从它入手吧。
在Spring中Advice下面这些类型:
嗯,都是些啥意思?Berfore Advice能够理解为SayHello方法,那么ThumbUp就是After Advice,两个加一块儿就是Around Advice。对于Throws Advice是针对异常抛出时的加强。最后还有一个比较牛的就是Introduction Advice则是能够对即有的对象进行增长方法,这个貌似更强大点。
从这些描述能够总结出来和前面动态代理时有殊途同归之处,在Spring中其实就是利用了动态代理的技术,结合AOP的概念对代码提供了一种更友好的扩展方式。固然也能够说换了一个角度来理解代码。举个例子说,但愿在现有系统中监控全部action的执行时间,就能够拦截全部的action,加一个Before Advice记录一下进入的时间,再加一个After Advice计算一下完成的时间。这样就不会对现有action代码作什么修改,很是优雅啊,这对于系统设计时会有很是有用。
为了可以更深刻的理解Spring AOP,仍是须要更深刻的去阅读源代码。这里再以DynamicProxyImpl为例子,若是使用Spring如何实现它的功能呢?以Spring xml配置方式试一下吧。
package aop.demo; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class SayImplAroundAdvice implements MethodInterceptor{ public Object invoke(MethodInvocation invocation) throws Throwable { SayHello(); Object result = invocation.proceed(); ThumbUp(); return result; } void SayHello() { System.out.print("你们好:"); } void ThumbUp() { System.out.println("但愿你们多多点赞."); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 声明被代理的目标对象 --> <bean id="sayImpl" class="aop.demo.SayImpl"></bean> <!-- 声明用于加强的拦截器对象 --> <bean id="sayImplAroundAdvice" class="aop.demo.SayImplAroundAdvice"></bean> <!-- 声明代理对象 --> <bean id="sayProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="aop.demo.ISay"/> <!-- 这个就是被代理的接口 --> <property name="target" ref="sayImpl"/> <!-- 这个就是被代理的对象 --> <property name="interceptorNames" value="sayImplAroundAdvice"/><!-- 这个就是代理的加强器 --> </bean> </beans>
package aop.demo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Client { @SuppressWarnings("resource") public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("aop/demo/spring.xml"); ISay say = (ISay)context.getBean("sayProxy"); say.say(); } }
最后执行一下程序输出的结果是:
你们好:我是5207.但愿你们多多点赞.
哎呀呀和前面的动态代理一致,效果达成。^_^
package aop.demo; import org.springframework.aop.framework.ProxyFactory; public class ClientCode { public static void main(String[] args) { ProxyFactory proxyFactory = new ProxyFactory(); // 建立代理工厂 proxyFactory.setTarget(new SayImpl()); // 射入目标类对象 proxyFactory.addAdvice(new SayImplAroundAdvice()); ISay say = (ISay) proxyFactory.getProxy(); say.say(); } }
写了一天才写到这里,只不过仍是值得的。原来一直觉得AOP就是动态代理,没想到本身错了,AOP是一种规范,而动态代理只是实现AOP的一种方式而已。
接下来继续研究spring aop,进一步学习ProxyFactoryBean和ProxyFactory。
AOP 那点事儿
Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理
JDK动态代理实现原理
Spring AOP代理详解
注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文连接! 若您以为这篇文章还不错请点击下右下角的推荐,很是感谢! http://www.cnblogs.com/5207