博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2019.6.14 spring-AOP
阅读量:4160 次
发布时间:2019-05-26

本文共 6304 字,大约阅读时间需要 21 分钟。

Aop

Aop(Aspect oriented programming):面向切面编程,通过在运行期动态代理实现程序功能的维护的一种技术。是面向对象编程的拓展和延续,利用Aop技术可以使各业务逻辑之间进行隔离和降低业务逻辑各部分之间的耦合,提高程序的可重用性。

Aop演进过程

(1)首先AOP采用的是横向抽取机制(代理)取代传统纵向继承的方式来增强代码的功能,横向抽取降低各部分之间(纵向维度上)的耦合性,纵向继承必须要在纵向上增加之间的联系。

(2)Aop作用:主要是在不改变源码的情况下,对程序进行增强(常见对于某一个方法的增强)

  • 传统增强方法的方式就是在每一个类中再创建那个增强的功能方法,然后直接调用就OK了,但这种情况要在所有需要增强的方法中都完成这样创建然后调用的过程,很繁琐。
  • 之后演进出继承,即将要增强的功能方法独立成一个类,然后继承,调用就OK了,免去不同类中都要创建功能方法的过程,直接调用就欧了。但是还是不灵活,如果客户突然不需要这样一个功能,那还有将所有方法中增强的功能部分给一个一个删除,这样必须改源代码。
  • 最后出现了动态代理的方式,只要对同一个接口下的所有类,都被都一个动态代理对象来代理,在代理中,只要筛选出指定要增强的方法,然后只对这个方法进行增强,其他方法原样输出,这样就算后期客户不需要这个方法,也只需改的这个代理类中一处代码就可以了。这样既拓展了代码的功能也提高了代码的灵活和效率。

(3)spring中Aop有两套,一套是自己设计的aop框架,但效率不高,不常用了,还有一套就是Aspectj创建的Aop框架,spring也引入这一高效的框架。

Spring中AOP

(1)AOP底层实现的原理就是动态代理的方式;(2)IOC底层是工厂类+反射+配置文件的方式奥!

Spring 的 AOP 的底层用到两种代理机制:

  • JDK 的动态代理 :针对实现了接口的类产生代理.这个是jdk自身的jar包,不用引入
  • Cglib 的动态代理 :针对没有实现接口的类产生代理. 应用的是底层的字节码增强的技术 生成当前类
    的子类对象.,这是第三方的jar包,使用需要引入,但已经集成在spring总的框架包中了。

下面实验通过jdk动态的方式实现代理

首先接口准备和实现目标类(待增强的类)的准备

接口准备

package spring.ov;public interface car {public void run();public void start();public void stop();}

待增强的类准备

package spring.ov;public class mycar implements car {	@Override	public void start() {		System.out.println("我的车启动了。。。");		}	@Override	public void run() {		System.out.println("我的车上路了。。。");			}	@Override	public void stop() {	System.out.println("我的车熄火了。。。");	}}

代理类的实现类的书写

package jdkproxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import spring.ov.car;public class mycarproxy implements InvocationHandler{	private car car= null;	public mycarproxy(car car) {		this.car = car;	}		public Object createmycarproxy() {//三个参数说明://第一个参数是传入类加载器,java中类加载器有三种,一般自建的类的加载器是不同于java中原有的,所以只要传入同一类型的类加载器就OK了,此处不细分哪一个类,只依赖加载器类型。//第二个参数是待增强类的所有实现的接口种类,getInterface()返回的是Class类型,是待增强类实现的所有接口的Class实例对象。//第三个参数:就是传入一个实现了invocationHandler接口的实现类,就可以了,然后这个接口下的invoke(x,x,x)方法就是代理类(待增强类增强后对应的类)真正执行的方法。		car mycarproxy = (car) Proxy.newProxyInstance(car.getClass().getClassLoader(), 		car.getClass().getInterfaces(), this);				return mycarproxy;			}	@Override	public car invoke(Object proxy, Method method, Object[] args) throws Throwable {	//这边参数:第一个proxy对象不知道干什么用的。	//第二个method对象是将原待增强类的方法反射成一个方法对象形式。	//第三参数就是带增强类中每一个方法中的参数对象。		if("run".equals(method.getName())) {				System.out.println("汽车进行预热监测1111。。。");				return (car) method.invoke(car, args);					}		return (car) method.invoke(car, args);	}}

上面就实现了jdk动态代理的全过程,实际还有无接口类的cglib代理就不阐述了。

spring中基于Aspectj的AOP开发

开发中的术语: 核心 —— 切点(要加入的具体或某些符合的位置) 和 通知(要增强的内容)

  • 连接点(Joinpoint):通俗认为就是包含业务逻辑的所有能够增强的方法(能够增强的所有每一个方法);
  • 切入点(pointcut):通俗将就是真正被增强的方法;
  • 通知(advice):就是对切入点中加入的增强的(内容/方法);通知是标注有某种注解的简单的 Java 方法.
  • 目标(target):被增强的对象;
  • 织入(waving):就是将增强的内容添加到目标的过程;
  • proxy:就是最终生成增强后的(代理)对象;
  • 切面(aspect):通知和切入点的组合

在这里插入图片描述

在这里插入图片描述

spring的AOP入门

1.创建工程和环境搭建

在这里插入图片描述
上图中最下面四个jar包是ioc开发的包,上面那四个是aop的开发包,还有两个是日志相关的。
(1)虽然上面分析aop底层是动态代理的实现原理,但是spring中不需要我们自己编写代理的代码,只要配置明确的相关信息,spring框架就帮我们自动生成代理对象了。
(2)搭建好环境后,就要设置配置文件了。首先引入Aop约束进入xml文件中,方便我们配置的书写。
(3)开始准备目标对象(待增强的类)

//最初始的接口public interface car {public void run();public void start();public void stop();}//通知对象(通知(增强的内容(通常增强的内容独立于某个类中某个方法的形式来表现)))//所以会编写一个通知的类。public class enhance {	public void enhan() {	System.out.println("加速度。。。。");}}//接口的实现类,也就像目标对象(待增强的类)public class mycar implements car {	@Override	public void start() {		System.out.println("我的车启动了。。。");		}	@Override	public void run() {		System.out.println("我的车上路了。。。");			}	@Override	public void stop() {		System.out.println("我的车熄火了。。。");	}}

(4)先通过spring的ioc控制反转对象的创建权给spring容器。所以要配置xml

(5)开始对待测试类方法调用的测试

下面是spring中最基础的ioc的实现,也是未使用aop(来增强方法)前,目标对象的原本方法显示:

public class aopdemo {@Testpublic void demo1() {	ClassPathXmlApplicationContext applicationcontext = 			new ClassPathXmlApplicationContext("spring/resource/applicationcontext3.xml");	car car0 = (car)applicationcontext.getBean("mycar");	car0.start();	car0.run();	car0.stop();///结果显示:我的车启动了。。。我的车上路了。。。我的车熄火了。。。}}

(6)下面开始对目标对象mycar类的run()方法的AOP的增强了。spring中AOP对目标对象的代理增强是底层完成的,不用我们像之前自己jdk动态代理那样编写出代理对象;只要在spring配置文件中配置代理实现的基本信息告知spring就可以了。

(7)要想实现spring代理,那么需要告知spring代理过程中的那些信息呢?前面我们自己实现动态代理时,我们要知道切入点(具体哪个待增强类(目标对象)中哪个要增强的方法)、通知(增强的内容(通常增强的内容独立于某个类中某个方法的形式来表现))

(1)首先要明确目标对象(待增强的类) 
(2)上面分析通知(增强的内容的术语)通常独立于某个类中某个方法上,所以通知对象也要明确表示出来
(3)
aop配置(面向前面)过程中,要知道切点(那个待增强的方法)、通知(增强的内容(以方法形式表现)) 所以@1.pointcut切点标签中要表明-----待增强的方法信息(哪个类的哪个方法,是否有参数等信息) @2.切面中通知的信息---------通知以哪个类下的哪个方法表示的。如enhance类下的 enhan方法内表示增强的内容。 @3.切面是切点和通知的组合,所以切面的标签中包含有切点的信息(对应哪个切点)和 通知的信息(对应哪个通知)

(8)综上设置好aop的配置内容后,就实现了将目标对象上的方法的增强,再调用原目标对象时,已经发生了增强。所以不不需要更改源代码。

和上面为通过AOP配置的原始目标对象的测试代码一样public class aopdemo {@Testpublic void demo1() {	ClassPathXmlApplicationContext applicationcontext = 			new ClassPathXmlApplicationContext("spring/resource/applicationcontext3.xml");	car car0 = (car)applicationcontext.getBean("mycar");	car0.start();	car0.run();	car0.stop();	///结果显示:我的车启动了。。。加速度。。。。我的车上路了。。。我的车熄火了。。。	}}

(9)通过简单的AOP实现我们发现:面向切面的技术可以不修改源代码,而能增强目标对象的功能。实际上AOP实现:就是多了通知类的bean配置和<aop:config >aop属性的设置就成功了。其他都和原目标对象的实现一样。

小结

AOP:面向切面编程;本质是动态代理,指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;

  • 某段代码——切面类也叫通知方法(aspect),切面类里面的方法需要动态感知业务逻辑方法的运行状态。
  • 指定方法指定位置——包含业务逻辑的方法也叫目标方法:切入点(pointcut)就是包含业务逻辑的方法
  • 切面类里面的方法也叫通知方法:前置通知、后置通知(无论正常结束还是异常结束都调用)、返回通知、异常通知、环绕通知;

AOP核心三步过程

在这里插入图片描述

  • 首先面向切面AOP本质是在程序运行过程中将某段代码切入到指定方法指定位置上;
  • AOP首先有区分出:切面类、目标业务逻辑方法;切面类就是加到业务代码周围的部分;
  • 切面类中需要指定:业务方法的全路径类名(在切入点pointcut出设定)、切入点和连接点(joinpoint)都是代表业务代码的;通知(有五个通知如下)、将本切面类上加上@aspect注解表示本bean是一个切面类,且记得注入容器哦!!!
在核心类上加上@EnableAspectJAutoProxy的aop注解@Aspect@Componentpublic class SchoolAop {	@Pointcut("execution(public * sunxtestspringboot.controller.SchoolController.*(..))")	public void pointcut() {}		@Before("pointcut()")	public void beforeTest(JoinPoint joinPoint) {		System.out.println(joinPoint.getSignature().getName()+" 方法被调用了...beforeTest");	}	@After("pointcut()")	public void afterTest(JoinPoint joinPoint) {		System.out.println(joinPoint.getSignature().getName()+" 方法被调用了...afterTest");	}	@AfterReturning(pointcut ="pointcut()",returning="resoult")	public void afterreturn(JoinPoint joinPoint,Object resoult) {		System.out.println(joinPoint.getSignature().getName()+" 方法被调用了"+"结果   :  "+resoult);	}	}

在这里插入图片描述

各通知的执行顺序: @Around ->@Before->主方法体->@Around中pjp.proceed()->@After->@AfterReturning

@After->@AfterReturning;即后置通知在返回或者异常通知前优先执行的

在这里插入图片描述

在这里插入图片描述

转载地址:http://dhdxi.baihongyu.com/

你可能感兴趣的文章
boost库在工作(21)任务之一
查看>>
boost库在工作(22)任务之二
查看>>
boost库在工作(23)任务之三
查看>>
boost库在工作(24)任务之四
查看>>
boost库在工作(25)任务之五
查看>>
boost库在工作(26)网络客户端之一
查看>>
boost库在工作(27)网络客户端之二
查看>>
boost库在工作(28)网络客户端之三
查看>>
boost库在工作(29)网络客户端之四
查看>>
boost库在工作(30)网络客户端之五
查看>>
boost库在工作(31)网络服务端之一
查看>>
boost库在工作(32)网络服务端之二
查看>>
Nginx读书笔记(3) —— 模块添加之config文件的写法
查看>>
Nginx读书笔记(4) —— HTTP模块的数据结构
查看>>
Ubuntu /boot空间不足时解决办法
查看>>
Nginx读书笔记(5) —— 定义自己的HTTP模块
查看>>
python 获取邮箱附件
查看>>
基于Arduino的循迹小车
查看>>
WinNT & Win2K下实现进程的完全隐藏
查看>>
在NT系列操作系统里让自己“消失”
查看>>