AOP和OOPhtml
一、OOP:Object-Oriented Programming,面向对象程序设计,是静态的,一旦类写死了就不能改变了,要更改就得修改代码从新编译,父类类型引用指向对象来实现动态性。核心思想是将客观存在的不一样事物抽象成相互独立的类,而后把与事物相关的属性和行为封装到类里,并经过继承和多态来定义类彼此间的关系,最后经过操做类的实例来完成实际业务逻辑的功能需求。java
二、AOP:Aspect-Oriented Programming,面向切面编程,以OOP为基础修复自己不具有的能力具备动态语言的特色,和OOP互为补充,Aop的最大意义是在不改变原来代码的前提下,也不对源代码作任何协议接口要求, 对OOP而实现了相似插件的方式,来修改源代码,给源代码插入新的执行代码。核心思想是将业务逻辑中与类不相关的通用功能切面式的提取分离出来,让多个类共享一个行为,一旦这个行为发生改变,没必要修改类,而只须要修改这个行为便可。Spring的AOP其代码实质,即代理模式的应用。sql
三、OOP和AOP的区别编程
四、OOP和AOP的联系:二者之间是一个相互补充和完善的关系api
五、AOP优势:利用AOP能够对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。数组
六、AOP应用安全
日志记录、事务处理、异常处理、安全控制和性能统计方面。ide
在Spring中提供了面向切面编程的丰富支持,容许经过分离应用的业务逻辑与系统级服务和事务进行内聚性的开发。源码分析
AOP的重要概念性能
一、切面 : 切点(Pointcut) + Advice【 在哪里 、加什么 】
二、Advice: 在 切点 选中的 链接点 "加入" 的 代码 就是 Advice【肯定 加什么 】
三、切点( Pointcut ) : 用来 筛选 链接点 的条件就是切点, 相似于sql语句,where条件就能够理解为切点【 肯定 在哪里加 ,在那些方法里面加入代码】
四、链接点( Join Point ) : 执行点 + 方位信息 全部被拦截的方法被加入了其余的方法,经过代理的方法将想要加入的想要的代码,加入的代码和原有的代码造成了链接点。一个方法有5个链接点,四个方法就有20个
五、方位信息
六、执行点:一个能够执行的方法在执行时就是一个执行点
七、代理目标: 哪一个对象将被其它对象所代理 ( 谁将被别人代理 ) [ target ]
八、代理对象: 哪一个对象将要去代理别的对象 ( 谁将去代理别人 ) [ proxy ]
代理模式
一、什么是代理呢?
例如:苹果公司的手机交由富士康工厂按照苹果公司的要求去生产手机,富士康工厂生产的手机由京东按照苹果公司的要求去完成销售,京东就能够当作是富士康的代理商(代理对象),而富士康就是代理目标。消费者在京东买手机,感受是由京东提供的手机,其实是由富士康生产的,由京东代理,这种思想就是代理思想
二、具体代码实例
IPhone 苹果手机类
package ecut.aop.proxy; public class IPhone { public IPhone(){ super(); System.out.println( "爱疯" ); } public void charge() { System.out.println( "charge" ); } public void call() { System.out.println( "call" ); } public void message() { System.out.println( "message" ); } }
AppleCompany 苹果公司接口
package ecut.aop.proxy; public interface AppleCompany { //由苹果公司去设计而后交由富士康生产 public IPhone create() ; }
FoxconnFactory 富士康工厂类
package ecut.aop.proxy; public class FoxconnFactory implements AppleCompany { //须要实现苹果公司接口 @Override public IPhone create() { System.out.println( "富士康" ); IPhone phone = new IPhone(); return phone ; } @Override public String toString() { return "FoxconnFactory"; } }
JingDong 京东类
package ecut.aop.proxy; public class JingDong implements AppleCompany { private FoxconnFactory factory ; @Override public IPhone create() { System.out.println( "京东" ); return factory.create() ; } //经过getter , setter 方法注入FoxconnFactory public FoxconnFactory getFactory() { return factory; } public void setFactory(FoxconnFactory factory) { this.factory = factory; } }
Main主类
package ecut.aop.proxy; public class Main { public static void main(String[] args) { //被代理的目标target, 京东买的手机由富士康生产 FoxconnFactory factory = new FoxconnFactory(); //京东是富士康的代理商,代理对象proxy,代理商和代理对象都实现了苹果公司的这个接口(前提),富士康按照苹果公司要求生产,京东按照苹果公司的要求买手机 JingDong jd = new JingDong(); jd.setFactory(factory); //至关于消费者买手机 IPhone p = jd.create(); System.out.println( p ); p.call(); } }
先调用JingDong的create方法,实际上调用的FoxconnFactory的create方法(调用的proxy方法的时候调用target方法,表面上感受是proxy调用的实际上target完成具体实现的)。用JingDong之因此能够代理FoxconnFactory,是由于FoxconnFactory和JingDong都实现了AppleCompany,这个是代理的前提
三、代理模式代码的主要特色是:不改变原有类的前提下,在原有类某些方法执行先后,插入任意代码。因此代理模式须要写新的类对原有的类进行包装。Struts2中的拦截器,Spring中的赖加载都是用代理模式实现
四、代理模式目前实现的方式有三种:
JDK动态代理
一、目的:经过反射实现动态产生一个代理对象来代理富士康工厂(代理目标)
二、动态代理主要是经过Proxy类中的newProxyInstance建立一个代理对象,newProxyInstance方法须要传三个参数ClassLoader loader,Class<?>[] interfaces,InvocationHandler h。
三、InvocationHandler 请求指派器的做用就是做用就是要将代理对象的请求派遣个代理目标(调用的proxy方法的时候调用target方法,表面上感受是proxy调用的实际上target完成具体实现的)。首先要经过匿名内部类实现InvocationHandler接口,并实现接口中的方法invoke( Object proxy , Method method , Object[] args )
在invoke方法中要是proxy和target产生联系经过method.invoke( target , args ) 来实现将代理对象的请求派遣个代理目标
四、测试代码以下
package ecut.aop.proxy; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class ProxyTest { public static void main(String[] args) { // 代理目标 ( 谁将被别人代理 ) final Object target = new FoxconnFactory(); // 代理目标 的类型 : 得到 被代理的 对象的 运行时 类型,jdk代理的缺陷(前提)代理的目标类得实现接口,Spring提供了子类代理父类(Cglib) Class<?> targetClass = target.getClass(); /** 用 代理目标 类型对应的类加载器 去加载 运行阶段动态产生的 代理类 */ ClassLoader loader = targetClass.getClassLoader() ; // 得到 代理目标 对应的 类的 "类加载器" /** 指示 未来产生 动态代理类 所须要实现的接口 */ Class<?>[] interfaces = targetClass.getInterfaces() ; // 得到 代理目标 对应的类所直接实现的接口 System.out.println( Arrays.toString( interfaces ) ); /** 请求指派器 (快递公司),调用的proxy方法的时候调用target方法,给你感受是proxy调用的实际上target实现的,是使proxy 和 target产生联系,InvocationHandler将请求派给target * method被调用的方法是谁, args 方法中的参数"借尸还魂"匿名内部类 */ InvocationHandler handler = new InvocationHandler(){ @Override //proxy对象 public Object invoke( Object proxy , Method method , Object[] args ) throws Throwable { //target.method(args);//被调用的是method方法;传入的参数是args;调用的是target所引用的对象的method方法 Object result = method.invoke( target , args ) ; // target.create();指派给target,由target调用方法 return result; } } ; // 代理对象 ( 谁将去代理别人 )Proxy动态代理类的父类Proxy.newProxyInstance建立一个代理对象 Object proxy = Proxy.newProxyInstance( loader , interfaces , handler ) ; System.out.println( proxy ); // proxy.toString()会被指派,经过InvocationHandler.invoke方法指派给target.toString(),所以返回的是target的tostring方法 // proxy.getClass()不会被指派。代理类 : ( 在 运行阶段 动态产生 ( 没有 .java 文件、没有 .class 文件 ,直接产生字节码放在内存中去) ) Class<?> proxyClass = proxy.getClass(); System.out.println( proxyClass ); // 全部的 由 java.lang.refrect.Proxy 产生的 动态代理类 的直接父类都是 Proxy System.out.println( proxyClass.getSuperclass() ); // 得到 动态代理类 所实现过的 接口 ( 在 建立 代理对象时指定的数组中有哪些接口,这里就有哪些接口 ) System.out.println( Arrays.toString( proxyClass.getInterfaces() ) ); System.out.println( "~~~构造方法~~~~~~~~~~~~" ); Constructor<?>[] cons = proxyClass.getDeclaredConstructors(); for( Constructor<?> c : cons ){ System.out.println( c ); } System.out.println( "~~~ 属性 ( Field ) ~~~~~~~~~~~~" ); //属性都由private static修饰 ,代理对象 $Proxy0继承了父类的Proxy类中的protected InvocationHandler h属性,其余m0,m1....都与方法相对应 Field[] fields = proxyClass.getDeclaredFields(); for( Field c : fields ){ System.out.println( c ); } System.out.println( "~~~ 方法 ( Method ) ~~~~~~~~~~~~" ); //重写了toString,hashCode,equals方法,都由public final修饰 Method[] methods = proxyClass.getDeclaredMethods(); for( Method c : methods ){ System.out.println( c ); } } }
运行结果以下:
[interface ecut.aop.proxy.AppleCompany]
FoxconnFactory
class com.sun.proxy.$Proxy0
class java.lang.reflect.Proxy
[interface ecut.aop.proxy.AppleCompany]
~~~构造方法~~~~~~~~~~~~
public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
~~~ 属性 ( Field ) ~~~~~~~~~~~~
private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m1
private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m2
private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m3
private static java.lang.reflect.Method com.sun.proxy.$Proxy0.m0
~~~ 方法 ( Method ) ~~~~~~~~~~~~
public final boolean com.sun.proxy.$Proxy0.equals(java.lang.Object)
public final java.lang.String com.sun.proxy.$Proxy0.toString()
public final int com.sun.proxy.$Proxy0.hashCode()
public final ecut.aop.proxy.IPhone com.sun.proxy.$Proxy0.create()
由以上输出能够推测出来代理类的类名是$Proxy0,且有一个有参构造,参数是InvocationHandler类型,且有四个属性,四个方法,还继承了父类的Proxy类中的protected InvocationHandler h属性,推测其余m0,m1....都与方法相对应。$Proxy0源码可能相似于
Proxy类
public class Proxy { protected InvocationHandler h ; // 父类中 public 修饰的 和 protected 修饰 均可以被子类继承 protected Proxy( InvocationHandler h ) { this.h = h ; } }
$Proxy0类
package com.sun.proxy ; public class $Proxy0 extends Proxy implements AppleCompany { public $Proxy0( InvocationHandler h ){ super( h ); } private static Method m0 ; private static Method m1 ; private static Method m2 ; private static Method m3 ; /* .......... */ static { Class<?> c = $Proxy0.class ; m0 = c.getMethod( "equals" , Object.class ); m1 = c.getMethod( "toString" ); m2 = c.getMethod( "hashCode" ); m3 = c.getMethod( "create" ); /* .......... */ } public final boolean equals( Object another ) { Object[] args = new Object[]{ another }; return h.invoke( this , m0 , args ); } public final String toString() { Object[] args = new Object[ 0 ]; return h.invoke( this , m1 , args ); } public final int hashCode() { Object[] args = new Object[ 0 ]; return h.invoke( this , m2 , args ); } public final IPhone create() { Object[] args = new Object[ 0 ]; return h.invoke( this , m3 , args ); } }
五、动态代理的实现和源码分析
查看Proxy类的newProxyInstance方法,从生成对象方法中,咱们看到三个关键的地方:
首先是经过Proxy.newProxyInstance( loader , interfaces , handler ) 将handler传入了$Proxy0 中的 InvocationHandler h属性中,而后当咱们调用方法时候其实是经过h.invoke( this , m3 , args )来实现的即调用handler里面的invoke方法,而在handler的invoke方法中经过method.invoke( target , args )方法,最终调用的是target里面的方法。
结合动态代理理解AOP相关概念
一、理解Advice测试案例
package ecut.aop.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyTest2 { public static void main(String[] args) { // 代理目标 ( 谁将被别人代理 ) final Object target = new FoxconnFactory(); // 代理目标 的类型 : 得到 被代理的 对象的 运行时 类型 Class<?> targetClass = target.getClass(); /** 用 代理目标 类型对应的类加载器 去加载 运行阶段动态产生的 代理类 */ ClassLoader loader = targetClass.getClassLoader() ; // 得到 代理目标 对应的 类的 "类加载器" /** 指示 未来产生 动态代理类 所须要实现的接口 */ Class<?>[] interfaces = targetClass.getInterfaces() ; // 得到 代理目标 对应的类所实现的接口 /** 请求指派器 */ InvocationHandler handler = new InvocationHandler(){ @Override public Object invoke( Object proxy , Method method , Object[] args ) throws Throwable { System.out.println( "方法[ " + method.getName() + " ]将要执行了" ); // Advice Object result = method.invoke( target , args ) ; // target.create(); System.out.println( "方法[ " + method.getName() + " ]执行结束并返回了: " + result ); // Advice return result; } } ; // 代理对象 ( 谁将去代理别人 ),这四个方法执行其先后就被咱们拦截下来了 ,方法先后增长了输出语句,没有动方法,只是在动态代理类handler里面增长了代码 //咱们所增长的代码若是按照规范来封装好就是Advice Object proxy = Proxy.newProxyInstance( loader , interfaces , handler ) ; if( proxy instanceof AppleCompany ) { AppleCompany ac = (AppleCompany) proxy ; System.out.println( ac == proxy ); IPhone p = ac.create(); System.out.println( p ); System.out.println( ac.toString() ); System.out.println( ac.hashCode() ); System.out.println( ac.equals(target) );//target.equals(target); } } }
这四个方法执行其先后就被咱们拦截下来了 ,方法先后增长了输出语句,没有动方法,只是在动态代理类handler里面增长了代码 ,咱们所增长的代码若是按照规范来封装好就是Advice。
理解链接点测试案例:
package ecut.aop.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyTest3 { public static void main(String[] args) { // 代理目标 ( 谁将被别人代理 ) final Object target = new FoxconnFactory(); Class<?> targetClass = target.getClass(); ClassLoader loader = targetClass.getClassLoader() ; Class<?>[] interfaces = targetClass.getInterfaces() ; InvocationHandler handler = new InvocationHandler(){ @Override public Object invoke( Object proxy , Method method , Object[] args ) throws Throwable { System.out.println( "方法[ " + method.getName() + " ]将要执行了" ); // before Object result = null ; try{ long start = System.nanoTime(); // around result = method.invoke( target , args ) ; // 执行点 long end = System.nanoTime(); // around System.out.println( "执行用时: " + ( end - start ) + " 毫微妙" ); // around System.out.println( "执行后并返回: " + result); // after-returing } catch ( Throwable t ){ System.out.println( "发生错误: " + t.getMessage() ); // after-throwing } System.out.println( "方法[ " + method.getName() + " ]执行结束" ); // after return result; } } ; Object proxy = Proxy.newProxyInstance( loader , interfaces , handler ) ; if( proxy instanceof AppleCompany ) { AppleCompany ac = (AppleCompany) proxy ; System.out.println( ac == proxy ); //经过create调用了target中create的方法 IPhone p = ac.create(); System.out.println( p ); //代理对象 $Proxy0中的四个方法是可执行的,方法在执行时候就能够视为是一个执行点 String s = ac.toString() ; System.out.println( s ); } } }
链接点 ( Join Point ) : 执行点 + 方位信息 全部被拦截的方法被加入了其余的方法,经过代理的方法将想要加入的想要的代码,加入的代码和原有的代码造成了链接点
执行点就是一个能够执行的方法在执行时就是一个执行点,当create方法在执行的时候就是一个执行点,方位信息就是加入Advice的位置就是方位信息,执行点加上分为信息就是链接点。
理解切点测试案例:
package ecut.aop.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyTest4 { public static void main(String[] args) { // 代理目标 ( 谁将被别人代理 ) final Object target = new FoxconnFactory(); Class<?> targetClass = target.getClass(); ClassLoader loader = targetClass.getClassLoader() ; Class<?>[] interfaces = targetClass.getInterfaces() ; InvocationHandler handler = new InvocationHandler(){ @Override //只保留了before和after的方位信息,20个链接点里挑选了8个,增长了了判断语句后只对create方法增长Advice,这些筛选条件就是切点 public Object invoke( Object proxy , Method method , Object[] args ) throws Throwable { if( "create".equals( method.getName() ) ) { System.out.println( "方法[ " + method.getName() + " ]将要执行了" ); // before } Object result = method.invoke( target , args ) ; // 执行点 if( "create".equals( method.getName() ) ){ System.out.println( "方法[ " + method.getName() + " ]执行结束" ); // after } return result; } } ; Object proxy = Proxy.newProxyInstance( loader , interfaces , handler ) ; if( proxy instanceof AppleCompany ) { AppleCompany ac = (AppleCompany) proxy ; System.out.println( ac == proxy ); IPhone p = ac.create(); System.out.println( p ); String s = ac.toString() ; System.out.println( s ); System.out.println( ac.hashCode() ); } } }
切点就是筛选链接点的条件,好比增长了了判断语句后只对create方法增长Advice,这些筛选条件就是切点。
参考博客连接:
https://blog.csdn.net/pdsygt/article/details/46433537
https://blog.csdn.net/u011266694/article/details/78918394
转载请于明显处标明出处: