文章目录
不使用AOP的开发方式(引入)
在使用面向对象开发完成后,如果想给一个已经写好的方法增加一些新的功能时,往往我们会写一个此功能的方法,然后再原方法中调用即可。可如果很多方法都需要增加此功能呢,是不是每个方法中都要调用此功能的方法呢?这样会很麻烦,也会存在大量的代码冗余。
对于此问题,可以学习AOP来解决。
AOP概述
AOP: (Aspect Oriented Programming)面向切面编程 ,是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
通俗来讲,AOP是一种编程技术,是将程序中的非业务代码进行提取,在不需要修改原来代码的情况下,为程序添加额外的功能,也就是说可以将业务代码和非业务代码进行隔离,使得各个部分的耦合度降低,提高程序的复用性,提高开发效率。
业务代码:直接用于实现用户需求的代码;
非业务代码:辅助业务代码,一般可以脱离业务而存在的代码,如解决中文乱码等;
如何做到:是通过一个代理对象(告诉代理对象,调用哪个方法时,让代理对象去帮助我们调用哪个方法)来实现对非业务代码进行调用。
底层实现:使用的是动态代理模式。
AOP中的基本概念
1.连接点
连接点:在类中可以被增强功能的方法。
2.切入点
切入点:在类中实际被增强功能的方法(实际实现的连接点)。
3.通知
通知:一个切面在指定的连接点要做的事情(增强的功能),也就是提取的非业务的功能。
根据通知的时间,可以将通知分为5种:
①前置通知
方法执行前通知;
②后置通知
方法执行后通知,如果方法中出现异常不通知;
无异常:
有异常:
③异常通知
方法出现异常时通知;
④最终通知
方法执行后通知,出现异常时也会通知;
无异常:
有异常:
⑤环绕通知
方法执行的各个时候都可以通知,可以将以上四种结合;
4.切面
切面:把通知添加到切入点的整个过程。
5.目标
目标(类):代理的目标对象,也就是连接点,切入点的所在类。
6.代理
代理:向目标对象应用通知时,创建的代理对象,在框架中代理对象是不需要我们关心的。
AOP的实现方式
对于AOP这种编程思想,很多框架都实现了。Spring框架就是其中之一,可以完成面向切面编程。
AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能,实现方式更为简洁,使用更为方便,还可以注解式开发。所以,Spring将AspectJ对于AOP的实现也引入到框架中。
在使用之前,先要在pom.xml文件中配置,将AOP的相关jar包导入:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
1.基于AspectJ的 xml 配置方式实现
第一步:创建所需通知类;
/*
示例:这是一个通知类;
*/
public class MyUtil {
public void printLog() {
System.out.println("打印日志");
}
public void commit() {
System.out.println("提交事务");
}
//异常通知
public void exceptionAdvice(Throwable e) {
System.out.println("异常通知" + e.getMessage());
}
//环绕通知
public void AroundAdvice(ProceedingJoinPoint point) {
try {
System.out.println("前置通知");
point.proceed();//调用我们自己的业务方法
System.out.println("后置通知");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知" + throwable.getMessage());
}
System.out.println("最终通知");
}
}
第二步:创建一个xml文件,专门用来配置通知,并将通知类交给Spring管理;
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--把非业务代码(通知)交给spring框架管理-->
<bean id="myUtil" class="通知类全类名"></bean>
</beans>
第三步:在Spring配置文件中导入该xml文件;
<import resource="classpath:配置通知文件名"></import>
第四步:配置切入点;
<!--
execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))是定义的切入点表达式
-->
<aop:pointcut id="saveAdmin" expression="execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))"/>
第五步:将通知与切入点进行配置;
<aop:config>
<!--配置切入点-->
<aop:pointcut id="saveAdmin" expression="execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))"/>
<!--将通知与切入点进行配置,生成的代理对象就知道如何调用-->
<aop:aspect ref="myUtil">
<!--前置通知-->
<aop:before method="printLog" pointcut-ref="saveAdmin"></aop:before>
<!--后置通知-->
<aop:after-returning method="printLog" pointcut-ref="saveAdmin"></aop:after-returning>
<!--最终通知-->
<aop:after method="printLog" pointcut-ref="saveAdmin"></aop:after>
<!--异常通知-->
<aop:after-throwing method="exceptionAdvice" pointcut-ref="saveAdmin" throwing="e"></aop:after-throwing>
<!--环绕通知-->
<aop:around method="AroundAdvice" pointcut-ref="saveAdmin"></aop:around>
</aop:aspect>
</aop:config>
2.基于注解方式的实现
第一步:需要在Spring配置文件中启动 AspectJ 支持;
<!--开启自动代理-->
<aop:aspectj-autoproxy/>
第二步:在通知类上标注两个标签;
@Component //让spring管理生成对象
@Aspect //表明是装有通知的类
public class MyUtil {
......
}
第三步:在通知类中的通知上可以使用注解标签将通知和切入点进行配置,注解标签中的表达式与xml方式中的表达式相同;
@Component //让spring管理生成对象
@Aspect //表明是装有通知的类
public class MyUtil {
//前置通知
@Before("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")
//最终通知
@After("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")
public void printLog() {
System.out.println("打印日志");
}
//后置通知
@AfterReturning("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")
public void commit() {
System.out.println("提交事务");
}
//异常通知
@AfterThrowing(value = "execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))",throwing = "e")
public void exceptionAdvice(Throwable e) {
System.out.println("异常通知" + e.getMessage());
}
//环绕通知
@Around("execution(* com.ffyc.ssm.dao.AdminDao.saveAdmin(..))")
public void AroundAdvice(ProceedingJoinPoint point) {
try {
System.out.println("前置通知");
point.proceed();//调用我们自己的业务方法
System.out.println("后置通知");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知" + throwable.getMessage());
}
System.out.println("最终通知");
}
}