SSM学习第三天–Spring Aop
Spring Aop
一、Aop
Aop(Aspect Oriented Programming):面向切面编程
利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
二、Aop术语
来自https://blog.csdn.net/qq_41981107/article/details/87920537
连接点(Joinpoint) 程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强
连接点表示具体要拦截的方法,上面切点是定义一个范围,而连接点是具体到某个方法
切点(PointCut) 每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。比如,连接点–数据库的记录,切点–查询条件
切点用于来限定Spring-AOP启动的范围,通常我们采用表达式的方式来设置,所以关键词是范围
增强(Advice) 增强是织入到目标类连接点上的一段程序代码。在Spring中,像BeforeAdvice等还带有方位信息
通知是直译过来的结果,我个人感觉叫做“业务增强”更合适 对照代码就是拦截器定义的相关方法,通知分为如下几种:
前置通知(before) 在执行业务代码前做些操作,比如获取连接对象
后置通知(after) 在执行业务代码后做些操作,无论是否发生异常,它都会执行,比如关闭连接对象
异常通知(afterThrowing) 在执行业务代码后出现异常,需要做的操作,比如回滚事务
返回通知(afterReturning) 在执行业务代码后无异常,会执行的操作
环绕通知(around) 这个目前跟我们谈论的事务没有对应的操作,所以暂时不谈
目标对象(Target) 需要被加强的业务对象
织入(Weaving) 织入就是将增强添加到对目标类具体连接点上的过程。
织入是一个形象的说法,具体来说,就是生成代理对象并将切面内容融入到业务流程的过程。
代理类(Proxy) 一个类被AOP织入增强后,就产生了一个代理类。
切面(Aspect) 切面由切点和增强组成,它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是将切面所定义的横切逻辑织入到切面所制定的连接点中。
比如上文讨论的数据库事务,这个数据库事务代码贯穿了我们的整个代码,我们就可以这个叫做切面。 SpringAOP将切面定义的内容织入到我们的代码中,从而实现前后的控制逻辑。 比如我们常写的拦截器Interceptor,这就是一个切面类
三、AspectJ开发
AspectJ是一种基于Java语言的AOP框架,提供强大的AOP功能。使用AspectJ实现AOP有两种方式:
1)基于XML的声明式AspectJ
通过XML文件来定义切面、切入点、通知。所有定义都是在<aop:config>
元素内,其子元素包括<aop:pointcut>、<aop:advisor>
和<aop:aspect>
常用配置代码(示例):
<!-- 1 切面 -->
<bean id="myAspect" class="com.ssm.aspectj.xml.MyAspect" />
<!-- 2aop编程 -->
<aop:config>
<!-- 1.配置切面 -->
<aop:aspect id="aspect" ref="myAspect">
<!-- 2.配置切入点 -->
<aop:pointcut expression="execution(* com.ssm.aspectj.*.*(..))" id="myPointCut" />
<!-- 3.配置通知 -->
<!-- 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<!--后置通知-->
<aop:after-returning method="myReturning" pointcut-ref="myPointCut" returning="returnVal"/>
<!--环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!--异常通知 -->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="throwable" />
<!--最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
具体步骤:
1、配置切面
<aop:aspect>
元素包含两个属性:id(用来定义该切面的唯一标识名称)、ref(用于引用普通的Spring Bean如上述代码中的myAspect)
2、配置切入点
通过<aop:pointcut>
元素来定义,当<aop:pointcut>
元素作为<aop:config>
元素的子元素时,表示该切入点是全局切入点,可多个切面公用。当<aop:pointcut>
作为<aop:aspect>
的子元素,则为当前切面有效。
包含两个属性:id(用来定义该切入点的唯一标识名称)、expression(用于指定切入点关联的切入点表达式)
<aop:pointcut expression="execution(* com.ssm.aspectj.*.*(..))"/>
表示匹配com.ssm.aspectj包中任意类的任意方法的执行。
expression表达式主体;
第1个*
表示返回类型、使用*
代表所有类型;com.ssm.aspectj表需要拦截的包名;
后第2个*
表类名,使用*
表所有的类;
第3个*
表方法名,使用*
表所有方法;
后面()
表方法的参数,中的..
表任意参数
注意第一个*
与包名之间有空格
3、配置通知
通知的常用属性及其描述
演示代码
(1)创建一个名为chapter03的动态Web项目;
在导入jar包时要在基础包中加入
(2)在chapter03项目的src目录下,创建一个com.ssm.aspectj包,在包里创建UserDao类,创建两个方法;
package com.ssm.aspectj;
public interface UserDao {
public void insert();
public void delete();
}
(3)在 com.ssm.aspectj包中,创建 UserDao接口的实现类 UserDaolmpl;
package com.ssm.aspectj;
import org.springframework.stereotype.Repository;
@Repository("userDao")
public class UserDaoImpl implements UserDao{
public void insert() {
//int i=10/0;
System.out.println("添加用户");
}
public void delete() {
System.out.println("删除用户");
}
}
(4)在chapter03项目的src目录下,创建一个 com. ssm.aspectj.xml包,包中创建切面类MyAspect;
package com.ssm.aspectj.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 切面类,此类编写通知
* */
public class MyAspect {
//前置通知
public void myBefore(JoinPoint joinPoint){
System.out.print("前置通知:模拟执行权限检查");
System.out.print("目标类为:"+joinPoint.getTarget());
System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
//后置通知
public void myReturning(JoinPoint joinPoint){
System.out.print("后置通知:模拟记录日志");
System.out.println("被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
//环绕通知
public Object myAround(ProceedingJoinPoint point)throws Throwable{
System.out.println("环绕开始:执行目标方法,模拟开启事务");
Object obj=point.proceed();
System.out.println("环绕结束,执行目标方法之后,模拟关闭事务");
return obj;
}
//异常通知
public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){
System.out.println("异常通知,错误了"+throwable.getMessage());
}
//最终通知
public void myAfter(){
System.out.println("最终通知,模拟方法结束后释放资源");
}
}
(5)在 com. ssm.aspectj.xml包中,创建配置文件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-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 1 目标类 -->
<bean id="userDao" class="com.ssm.aspectj.UserDaoImpl" />
<!-- 2 切面 -->
<bean id="myAspect" class="com.ssm.aspectj.xml.MyAspect" />
<!-- 3 aop编程 -->
<aop:config>
<!-- 1.配置切面 -->
<aop:aspect id="aspect" ref="myAspect">
<!-- 2.配置切入点 -->
<aop:pointcut expression="execution(* com.ssm.aspectj.*.*(..))" id="myPointCut" />
<!-- 3.配置通知 -->
<!-- 前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<!--后置通知-->
<aop:after-returning method="myReturning" pointcut-ref="myPointCut" returning="returnVal"/>
<!--环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!--异常通知 -->
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="throwable" />
<!--最终通知 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
(6)在com. ssm.aspectj. xm包下,创建测试类TestXmlAspect。
package com.ssm.aspectj.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ssm.aspectj.UserDao;
public class TextXmlAspect {
public static void main(String[] args) {
String xmlPath="com/ssm/aspectj/xml/applicationContext.xml";
ApplicationContext app=new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao=(UserDao)app.getBean("userDao");
userDao.insert();
userDao.delete();
}
}
运行结果如下:
三月 20, 2022 2:10:06 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@817b38: startup date [Sun Mar 20 14:10:06 CST 2022]; root of context hierarchy
三月 20, 2022 2:10:07 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/ssm/aspectj/xml/applicationContext.xml]
前置通知:模拟执行权限检查目标类为:com.ssm.aspectj.UserDaoImpl@1c072cb,被植入增强处理的目标方法为:insert
环绕开始:执行目标方法,模拟开启事务
添加用户
最终通知,模拟方法结束后释放资源
环绕结束,执行目标方法之后,模拟关闭事务
后置通知:模拟记录日志被植入增强处理的目标方法为:insert
前置通知:模拟执行权限检查目标类为:com.ssm.aspectj.UserDaoImpl@1c072cb,被植入增强处理的目标方法为:delete
环绕开始:执行目标方法,模拟开启事务
删除用户
最终通知,模拟方法结束后释放资源
环绕结束,执行目标方法之后,模拟关闭事务
后置通知:模拟记录日志被植入增强处理的目标方法为:delete
包结构:
2)基于注解的声明式AspectJ
注释名 | 描述 |
---|---|
@Aspect | 定义一个切面 |
@PointCut | 定义切入点表达式 |
@Before | 定义前置通知 |
@AfterReturning | 定义后置通知 |
@Around | 定义环绕通知 |
@AfterThrowing | 定义异常通知 |
@After | 定义最终通知 |
演示代码
创建 com.ssm.aspectj.annotation包新建MyAspect类进行修改
package com.ssm.aspectj.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//定义切面类
@Aspect
//由于该类在Spring中作为组件使用,要添加@Component注解才能够生效
@Component
public class MyAspect {
//定义切入点
@Pointcut("execution(* com.ssm.aspectj.*.*(..))")
//切入点方法
public void myPointCut(){}
//前置通知
@Before("myPointCut()")
public void myBefore(JoinPoint joinPoint){
System.out.print("前置通知:模拟执行权限检查");
System.out.print("目标类为:"+joinPoint.getTarget());
System.out.println(",被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
//后置通知
@AfterReturning("myPointCut()")
public void myReturning(JoinPoint joinPoint){
System.out.print("后置通知:模拟记录日志");
System.out.println("被植入增强处理的目标方法为:"+joinPoint.getSignature().getName());
}
//环绕通知
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint point)throws Throwable{
System.out.println("环绕开始:执行目标方法,模拟开启事务");
Object obj=point.proceed();
System.out.println("环绕结束,执行目标方法之后,模拟关闭事务");
return obj;
}
//异常通知
@AfterThrowing(value="myPointCut()",throwing="throwable")
public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){
System.out.println("异常通知,错误了"+throwable.getMessage());
}
//最终通知
@After("myPointCut()")
public void myAfter(){
System.out.println("最终通知,模拟方法结束后释放资源");
}
}
在com.ssm.aspectj.annotation包中创建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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 扫描特定包,使注解生效 -->
<context:component-scan base-package="com.ssm" />
<!-- 启用基于注解的声明式AspectJ支持 -->
<aop:aspectj-autoproxy />
</beans>
测试类TextAnnotation
package com.ssm.aspectj.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ssm.aspectj.UserDao;
public class TextAnnotation {
public static void main(String[] args) {
String xmlPath="com/ssm/aspectj/annotation/applicationContext.xml";
ApplicationContext app=new ClassPathXmlApplicationContext(xmlPath);
UserDao userDao=(UserDao)app.getBean("userDao");
userDao.insert();
userDao.delete();
}
}
三月 20, 2022 2:31:50 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@817b38: startup date [Sun Mar 20 14:31:50 CST 2022]; root of context hierarchy
三月 20, 2022 2:31:50 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [com/ssm/aspectj/annotation/applicationContext.xml]
环绕开始:执行目标方法,模拟开启事务
前置通知:模拟执行权限检查目标类为:com.ssm.aspectj.UserDaoImpl@19e68d0,被植入增强处理的目标方法为:insert
添加用户
环绕结束,执行目标方法之后,模拟关闭事务
最终通知,模拟方法结束后释放资源
后置通知:模拟记录日志被植入增强处理的目标方法为:insert
环绕开始:执行目标方法,模拟开启事务
前置通知:模拟执行权限检查目标类为:com.ssm.aspectj.UserDaoImpl@19e68d0,被植入增强处理的目标方法为:delete
删除用户
环绕结束,执行目标方法之后,模拟关闭事务
最终通知,模拟方法结束后释放资源
后置通知:模拟记录日志被植入增强处理的目标方法为:delete