Spring学习(六)
AOP和OOP
1、OOP:Object-Oriented Programming,面向对象程序设计,是静态的,一旦类写死了就不能改变了,要更改就得修改代码重新编译,父类类型引用指向对象来实现动态性。核心思想是将客观存在的不同事物抽象成相互独立的类,然后把与事物相关的属性和行为封装到类里,并通过继承和多态来定义类彼此间的关系,最后通过操作类的实例来完成实际业务逻辑的功能需求。
2、AOP:Aspect-Oriented Programming,面向切面编程,以OOP为基础修复本身不具备的能力具有动态语言的特点,和OOP互为补充,Aop的最大意义是在不改变原来代码的前提下,也不对源代码做任何协议接口要求, 对OOP而实现了类似插件的方式,来修改源代码,给源代码插入新的执行代码。核心思想是将业务逻辑中与类不相关的通用功能切面式的提取分离出来,让多个类共享一个行为,一旦这个行为发生改变,不必修改类,而只需要修改这个行为即可。Spring的AOP其代码实质,即代理模式的应用。
3、OOP和AOP的区别
- 面向目标不同:简单来说OOP是面向名词领域,AOP面向动词领域。
- 思想结构不同:OOP是纵向结构,AOP是横向结构。
- 注重方面不同:OOP注重业务逻辑单元的划分,AOP偏重业务处理过程的某个步骤或阶段。
4、OOP和AOP的联系:两者之间是一个相互补充和完善的关系
5、AOP优点:利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
6、AOP应用
日志记录、事务处理、异常处理、安全控制和性能统计方面。
在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务和事务进行内聚性的开发。
AOP的重要概念
1、切面 : 切点(Pointcut) + Advice【 在哪里 、加什么 】
2、Advice: 在 切点 选中的 连接点 "加入" 的 代码 就是 Advice【确定 加什么 】
3、切点( Pointcut ) : 用来 筛选 连接点 的条件就是切点, 类似于sql语句,where条件就可以理解为切点【 确定 在哪里加 ,在那些方法里面加入代码】
4、连接点( Join Point ) : 执行点 + 方位信息 所有被拦截的方法被加入了其他的方法,通过代理的方法将想要加入的想要的代码,加入的代码和原有的代码形成了连接点。一个方法有5个连接点,四个方法就有20个
- 一个正在执行的方法 (执行点) 执行前 (方位信息)
- 一个正在执行的方法 (执行点) 执行前、后 (方位信息)
- 一个正在执行的方法 (执行点) 执行并返回后 (方位信息)
- 一个正在执行的方法 (执行点) 执行抛出异常后 (方位信息)
- 一个正在执行的方法 (执行点) 执行后 (方位信息)
5、方位信息
- 方法执行前 ( before ) 、
- 执行前和执行后,执行前后都有环绕,两个结合起来才可以使用,两者之间是有联系的 ( around ) 、
- 正常执行并返回后 ( after-returning ) 、
- 方法抛出异常后 ( after-throwing ) 、
- 方法执行后 ( after )
6、执行点:一个可以执行的方法在执行时就是一个执行点
7、代理目标: 哪个对象将被其它对象所代理 ( 谁将被别人代理 ) [ target ]
8、代理对象: 哪个对象将要去代理别的对象 ( 谁将去代理别人 ) [ proxy ]
代理模式
1、什么是代理呢?
例如:苹果公司的手机交由富士康工厂按照苹果公司的要求去生产手机,富士康工厂生产的手机由京东按照苹果公司的要求去完成销售,京东就可以看成是富士康的代理商(代理对象),而富士康就是代理目标。消费者在京东买手机,感觉是由京东提供的手机,实际上是由富士康生产的,由京东代理,这种思想就是代理思想
2、具体代码实例
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 {//需要实现苹果公司接口 @Overridepublic IPhone create() {System.out.println( "富士康" );IPhone phone = new IPhone();return phone ;}@Overridepublic String toString() {return "FoxconnFactory";}}
JingDong 京东类
package ecut.aop.proxy;public class JingDong implements AppleCompany {private FoxconnFactory factory ;@Overridepublic IPhone create() {System.out.println( "京东" );return factory.create() ;}//通过getter , setter 方法注入FoxconnFactorypublic 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,这个是代理的前提
3、代理模式代码的主要特点是:不改变原有类的前提下,在原有类某些方法执行前后,插入任意代码。所以代理模式需要写新的类对原有的类进行包装。Struts2中的拦截器,Spring中的赖加载都是用代理模式实现
4、代理模式目前实现的方式有三种:
- 静态代理:需要增强原有类的哪个方法,就需要对在代理类中包装哪个方法。个人理解,从功能上来说,原有类和代理类不一定要实现共同接口,但是为了赋予代理和和被代理类之间的逻辑关系,增加程序的可读性,可理解性,逻辑性,增加代理对象和被代理对象之间的关系,以更加符合面向对象编程是思维,而应该实现共同接口。
- 动态代理:使用反射机制,方法和对象都是传入的变量,就可以经过传入的对象和方法而动态调用被代理对象的任何方法,jdk中提供了实现此动态代理的api,被代理类必须实现接口
- Cglib代理:返回对象是代理对象的子类,不需要代理对象实现接口。当调用原对象方法时,实际上调用的是代理子类的方法。
JDK动态代理
1、目的:通过反射实现动态产生一个代理对象来代理富士康工厂(代理目标)
2、动态代理主要是通过Proxy类中的newProxyInstance创建一个代理对象,newProxyInstance方法需要传三个参数ClassLoader loader,Class<?>[] interfaces,InvocationHandler h。
- ClassLoader loader:代理目标对应的 类的 "类加载器"
- Class<?>[] interfaces:代理目标对应的类所直接实现的接口
- InvocationHandler h:请求指派器,将代理对象的请求派遣个代理目标
3、InvocationHandler 请求指派器的作用就是作用就是要将代理对象的请求派遣个代理目标(调用的proxy方法的时候调用target方法,表面上感觉是proxy调用的实际上target完成具体实现的)。首先要通过匿名内部类实现InvocationHandler接口,并实现接口中的方法invoke( Object proxy , Method method , Object[] args )
- Object proxy:代理对象
- Method method:调用的方法
- Object[] args:方法中要传入的参数
在invoke方法中要是proxy和target产生联系通过method.invoke( target , args ) 来实现将代理对象的请求派遣个代理目标
4、测试代码如下
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 );}}
5、动态代理的实现和源码分析
查看Proxy类的newProxyInstance方法,从生成对象方法中,我们看到三个关键的地方:
- Class<?> cl = getProxyClass0(loader, interfaces);//得到代理类
- final Constructor<?> cons = cl.getConstructor(constructorParams);
- newInstance(cons, h);//将InvocationHandler h传入代理对象中
首先是通过Proxy.newProxyInstance( loader , interfaces , handler ) 将handler传入了$Proxy0 中的 InvocationHandler h属性中,然后当我们调用方法时候实际上是通过h.invoke( this , m3 , args )来实现的即调用handler里面的invoke方法,而在handler的invoke方法中通过method.invoke( target , args )方法,最终调用的是target里面的方法。
结合动态代理理解AOP相关概念
1、理解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(){@Overridepublic Object invoke( Object proxy , Method method , Object[] args ) throws Throwable {System.out.println( "方法[ " + method.getName() + " ]将要执行了" ); // AdviceObject result = method.invoke( target , args ) ; // target.create();System.out.println( "方法[ " + method.getName() + " ]执行结束并返回了: " + result ); // Advicereturn result;}} ;// 代理对象 ( 谁将去代理别人 ),这四个方法执行其前后就被我们拦截下来了 ,方法前后增加了输出语句,没有动方法,只是在动态代理类handler里面增加了代码//我们所增加的代码如果按照规范来封装好就是AdviceObject 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(){@Overridepublic Object invoke( Object proxy , Method method , Object[] args ) throws Throwable {System.out.println( "方法[ " + method.getName() + " ]将要执行了" ); // before Object result = null ;try{long start = System.nanoTime(); // aroundresult = method.invoke( target , args ) ; // 执行点long end = System.nanoTime(); // aroundSystem.out.println( "执行用时: " + ( end - start ) + " 毫微妙" ); // aroundSystem.out.println( "执行后并返回: " + result); // after-returing} catch ( Throwable t ){System.out.println( "发生错误: " + t.getMessage() ); // after-throwing }System.out.println( "方法[ " + method.getName() + " ]执行结束" ); // afterreturn 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
转载请于明显处标明出处:
https://www.cnblogs.com/AmyZheng/p/9264215.html
转载于:https://www.cnblogs.com/AmyZheng/p/9264215.html
Spring学习(六)相关推荐
- Spring学习(六):WebFlux
文章目录 WebFlux概述 简介 异步非阻塞 响应式编程 简介 Reactor实现 简介 三种信号特点 操作符 示例代码 SpringWebflux执行流程和核心API 执行流程 核心API Spr ...
- (转)MyBatis框架的学习(六)——MyBatis整合Spring
http://blog.csdn.net/yerenyuan_pku/article/details/71904315 本文将手把手教你如何使用MyBatis整合Spring,这儿,我本人使用的MyB ...
- Spring学习(六)bean装配详解之 【通过注解装配 Bean】【基础配置方式】
本文借鉴:Spring学习(特此感谢!) 通过注解装配 Bean 1.前言 优势 1.可以减少 XML 的配置,当配置项多的时候,XML配置过多会导致项目臃肿难以维护 2.功能更加强大,既能实现 XM ...
- Spring学习笔记之MyBatis
系列文章目录 Spring学习笔记 之 Springhttps://blog.csdn.net/weixin_43985478/article/details/124411746?spm=1001.2 ...
- 一箭双雕 刷完阿里P8架构师spring学习笔记+源码剖析,涨薪8K
关于Spring的叙述: 我之前死磕spring的时候,刷各种资料看的我是一头雾水的,后面从阿里的P8架构师那里拿到这两份资料,从源码到案例详细的讲述了spring的各个细节,是我学Spring的启蒙 ...
- 第四阶段--Spring学习-01
第四阶段--Spring学习 1.概述 2.Spring相关概念 2.1 初识Spring 2.2 Spring系统架构 2.2.1 系统架构图 2.2.3 Spring核心概念-IOC.IOC容器. ...
- Spring 学习总结笔记【七、AOP面向切面编程】
往期文章: Spring 学习总结笔记[一.快速入门] Spring 学习总结笔记[二.IoC-控制反转] Spring 学习总结笔记[三.注解开发] Spring 学习总结笔记[四.整合Junit] ...
- JavaEE——Spring学习笔记03【AOP开发】
JavaEE--Spring学习笔记01[Ioc开发的模式] JavaEE--Spring学习笔记02[Spring和Mybatis的整合] JavaEE--Spring学习笔记03[AOP开发] J ...
- JavaEE——Spring学习笔记01【Ioc开发的模式】
JavaEE--Spring学习笔记01[Ioc开发的模式] JavaEE--Spring学习笔记02[Spring和Mybatis的整合] JavaEE--Spring学习笔记03[AOP开发] J ...
最新文章
- draw.io项目本地部署
- linux 逻辑运算符and,布尔逻辑运算符
- 柴油发电机组自动控制系统工作原理
- 蓝牙解析(part10):BLE ATT/GATT
- 山西省职高计算机分数线,2020年山西五年制高职和普通中专录取分数线
- 【Redis】9.10.Redis持久化方式之RDBAOFAOF和RDB区别场景分析
- MySQL的my.cnf文件(解决5.7.18下没有my-default.cnf)
- 错误删除linux分区致Win7引导失败的修复方法
- ++递归 字符串全排列_超全递归技巧整理,这次一起拿下递归
- 在PPT中,用好PNG图片素材,免抠才是王道
- 创业始于自信 成功缘于诚信
- UIView动画设置
- python打印列出目录及其子目录里面的内容
- 数据仓库与数据挖掘知识点梳理
- 近日总结3.17-3.19(windows server)
- 龙芯 python_在UOS20-龙芯(MIPS64EL)上安装 opencv-python
- [jzoj 4244] 【五校联考6day2】yi {贪心}
- QTreeWidget实现搜索子节点功能
- 目标检测YOLOv3
- 下载m3u8视频及在Linux下将ts合并为mp4格式
热门文章
- 遗传算法(Genetic Algorithm)
- 腾讯、网易、新浪新闻网站爬虫编写记录及评论格式分析
- Bitmap详解(中)之像素级操作
- c++ regex用法实例
- python求职者的建议_Python 求职和建议-从认识自己出发
- onvif学习笔记7:一个C++封装的onvif代码的阅读笔记
- 初识openwrt(下)
- 磁力计 姿态 matlab,磁力计如何用来计算姿态(1)
- 【Flink】Flink 没有keyBy 某个 subTask 没有数据
- 【Flink】FLink SQL TableException: Table sink doesn‘t support consuming update changes which is