AOP概念
⚫ AOP(Aspect Oriented Programing)面向切面编程,一种编程范式,隶属于软工范畴,指导开发者如
何组织程序结构
⚫ AOP弥补了OOP的不足,基于OOP基础之上进行横向开发
◆ OOP规定程序开发以类为主体模型,一切围绕对象进行,完成某个任务先构建模型
◆ AOP程序开发主要关注基于OOP开发中的共性功能,一切围绕共性功能进行,完成某个任务先
构建可能遇到的所有共性功能(当所有功能都开发出来也就没有共性与非共性之分)
AOP动态代理的方式(spring的AOP默认是JDK Proxy)
<!-- 配置AOP
proxy-target-class="false"配置为false就是使用的JDK Proxy
由源码得:
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
<xsd:annotation>
<xsd:documentation><![CDATA[
是否要创建基于类(CGLIB)的代理?默认情况下,标准创建基于Java接口的代理。
Are class-based (CGLIB) proxies to be created? By default, standard
Java interface-based proxies are created.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
-->
<aop:config proxy-target-class="false">
</aop:config>
JDK的动态代理,需要有实现接口
动态代理——JDK Proxy
⚫ JDKProxy动态代理是针对对象做代理,要求原始对象具有接口实现,并对接口方法进行增强
在aop动态代理的时候发现这个类是实现接口的类,就选择JDK的动态代理,从ioc中获取这个动态代理的实现类就需要按照接口.class去获取
CGLIB动态代理方式,继承方式
动态代理——CGLIB
⚫ CGLIB(Code Generation Library),Code生成类库
⚫ CGLIB动态代理不限定是否具有接口,可以对任意操作进行增强
⚫ CGLIB动态代理无需要原始被代理对象,动态创建出新的代理对象
需要将配置文件中这个设置为true才开启CGLIB方式动态代理
<aop:config proxy-target-class="true">
CGLIB动态代理方式,继承方式,就直接用这个类.class去获取
AOP作用
⚫ 伴随着AOP时代的降临,可以从各个行业的标准化、规范化开始入手,一步一步将所有共性功能逐一开
发完毕,最终以功能组合来完成个别业务模块乃至整体业务系统的开发
⚫ 目标:将软件开发由手工制作走向半自动化/全自动化阶段,实现“插拔式组件体系结构”搭建
AOP概念图解
AOP
⚫ Joinpoint(连接点):就是方法
⚫ Pointcut(切入点):就是挖掉共性功能的方法
⚫ Advice(通知):就是共性功能,最终以一个方法的形式呈现
⚫ Aspect(切面):就是共性功能与挖的位置的对应关系
⚫ Target(目标对象):就是挖掉功能的方法对应的类产生的对象,这种对象是无法直接完成最终工作的
⚫ Weaving(织入):就是将挖掉的功能回填的动态过程
⚫ Proxy(代理):目标对象无法直接完成工作,需要对其进行功能回填,通过创建原始对象的代理对象实现
⚫ Introduction(引入/引介) :就是对原始对象无中生有的添加成员变量或成员方法
AOP配置之XML方式
AOP开发过程
⚫ 开发阶段(开发者完成)
◆ 正常的制作程序
◆ 将非共性功能开发到对应的目标对象类中,并制作成切入点方法
◆ 将共性功能独立开发出来,制作成通知
◆ 在配置文件中,声明切入点
◆ 在配置文件中,声明切入点与通知间的关系(含通知类型),即切面
⚫ 运行阶段(AOP完成)
◆ Spring容器加载配置文件,监控所有配置的切入点方法的执行
◆ 当监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应
位置将通知对应的功能织入,完成完整的代码逻辑并运行
XML标签
切入点表达式
通知类型
⚫ AOP的通知类型共5种
◆ 前置通知:原始方法执行前执行,如果通知中抛出异常,阻止原始方法运行
应用:数据校验
◆ 后置通知:原始方法执行后执行,无论原始方法中是否出现异常,都将执行通知
应用:现场清理
◆ 返回后通知:原始方法正常执行完毕并返回结果后执行,如果原始方法中抛出异常,无法执行
应用:返回值相关数据处理
◆ 抛出异常后通知:原始方法抛出异常后执行,如果原始方法没有抛出异常,无法执行
应用:对原始方法中出现的异常信息进行处理
◆ 环绕通知:在原始方法执行前后均有对应执行执行,还可以阻止原始方法的执行
应用:十分强大,可以做任何事情
通知类型XML
一 在pom文件中导入相关坐标
AspectJ
⚫ Aspect(切面)用于描述切入点与通知间的关系,是AOP编程中的一个概念
⚫ AspectJ是基于java语言对Aspect的实现
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- aop切面包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
二 将需要对切入点(方法)进行添加的功能制作成一个切面类,并存放在ioc容器中
切面类
package com.fs.aop;
import org.aspectj.lang.ProceedingJoinPoint;
/*
这个类是AOP的切面类
里面编辑一些织入的方法
*/
public class AOPDemoAdvice {
public void proxyAOPBefore(){
System.out.println("给切入点(方法)添加了前置通知~~~");
}
public void proxyAOPAfter(){
/*
这个后置通知是无论代码是否出现异常,都会执行这个通知
*/
System.out.println("给切入点(方法)添加了后置通知~~~");
}
public void proxyAOPBeforeReturning() {
/*
BeforeReturning
表示的是代码运行成功无任何异常,会执行的通知
*/
System.out.println("给切入点(方法)添加了代码运行成功后的后置通知~~~");
}
public void proxyAOPBeforeThrowing() {
System.out.println("给切入点(方法)添加了代码运行异常后的后置通知~~~");
}
/*
环绕通知
环绕通知可以将上面的通知方法全部实现
ProceedingJoinPoint
这类就代表切入点,可以给这个切入点执行配置通知
*/
public Object proxyAOPAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object proceed = null;
try {
//proceedingJoinPoint切入点执行前配置前置通知
System.out.println("环绕通知的前置通知执行的代码");
//切入点(被动态代理的方法)执行
proceed = proceedingJoinPoint.proceed();
//proceedingJoinPoint切入点执行成功无报错后配置前置通知
System.out.println("环绕通知的-成功运行-后执行的代码");
} catch (Throwable throwable) {
//proceedingJoinPoint切入点执行前配置异常通知
System.out.println("环绕通知的切入点执行-异常后-执行的代码");
throwable.printStackTrace();
} finally {
//proceedingJoinPoint切入点执行前后通知,就是代码无论是异常,还成功运行,都会执行的增强代码
System.out.println("环绕通知的后置通知执行的代码");
}
return proceed;
}
}
目标对象(这里模拟三层架构的service,对service中的某些方法进行aop)
AopService接口
package com.fs.service;
public interface AopService {
void findAll();
void add();
void findById();
void del();
void update();
}
AopServiceImpl实现类
package com.fs.service.impl;
import com.fs.service.AopService;
public class AopServiceImpl implements AopService {
@Override
public void findAll() {
System.out.println("业务层执行了findAll~~~");
}
@Override
public void add() {
System.out.println("业务层执行了add~~~");
}
@Override
public void findById() {
System.out.println("业务层执行了findById~~~");
//制造异常来是异常通知生效
// int i = 1/0;
}
@Override
public void del() {
System.out.println("业务层执行了del~~~");
}
@Override
public void update() {
System.out.println("业务层执行了update~~~");
}
}
编写applicationContext.xml配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 将业务层实现类存放在ioc容器中,因为需要对某个类中的方法进行aop需要这个类也在ioc容器中-->
<bean id="aopService" class="com.fs.service.impl.AopServiceImpl"/>
<!-- 将切面类也交给ioc管理-->
<bean id="aopDemoAdvice" class="com.fs.aop.AOPDemoAdvice"/>
<aop:config>
<!-- 配置切入点(方法),那些方法需要增强
execution(* com.fs.service.impl.*.find*(..))
任意修饰符,任意返回值 com.fs.service.impl包下的任意类的find开头的方法(任意参数)-->
<aop:pointcut id="pt" expression="execution(* com.fs.service.impl.*.find*(..))"/>
<!-- 配置切面类(切入点与通知的关系指定),里面有增强的代码(共性代码)-->
<aop:aspect ref="aopDemoAdvice">
<!-- 给切入点(方法)执行的时候配置通知
通俗点讲就是给上面配置的切入点(方法)动态的对方法进行增强,并且创建一个动态代理对象存放
在ioc的容器中-->
<!-- 配置前置通知-->
<!-- <aop:before method="proxyAOPBefore" pointcut-ref="pt"/>-->
<!-- 配置后置通知-->
<!-- <aop:before method="proxyAOPAfter" pointcut-ref="pt"/>-->
<!-- 配置运行成功通知-->
<!-- <aop:after-returning method="proxyAOPBeforeReturning" pointcut-ref="pt"/>-->
<!-- 配置运行异常通知-->
<!-- <aop:after-throwing method="proxyAOPBeforeThrowing" pointcut-ref="pt"/>-->
<!-- 配置环绕通知(可以实现上面的任意通知方式)-->
<aop:around method="proxyAOPAround" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
测试代码
@Test
public void method02() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationAop.xml");
/*
JDK的动态代理,需要有实现接口
在aop动态代理的时候发现这个类是实现接口的类,就选择JDK的动态代理
若aop动态代的时候发现这个类是普通类,就选择CGLIB动态代理方式,继承方式,就不会
因为当前业务实现类被aop动态代理增强方法后生成了一个类,这个类的不是AopServiceImpl
所以在ioc中获取使用AopServiceImpl.class是拿不到的,只能通过AopService接口去ioc中找
因为aop动态代理生成的实现类还是实现了AopService,所以可以通过接口类型去ioc中找到
这个aop动态代理的实现类
*/
// AopServiceImpl aopService = applicationContext.getBean(AopServiceImpl.class);
AopService aopService = applicationContext.getBean(AopService.class);
// aopService.findAll();
aopService.findById();
}
测试效果
findById()方法正常运行
若在findById()方法中制作一个异常
@Override
public void findById() {
System.out.println("业务层执行了findById~~~");
//制造异常来是异常通知生效
int i = 1/0;
}
图解Aop
通知获取参数数据(了解)
若有多个参数,JoinPoint一定要在第一个参数的位子
通知获取返回值数据
通知获取异常数据