使用AspectJ实现AOP
概述
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/53328502040dcb3fc366ed612e3725a2.png)
- 分为注解方式+XML方式
- AspectJ是一种基于Java语言的AOP框架(本身是独立的AOP框架)
- Spring整合AspectJ框架
- 引入包
搭建环境
- 创建maven工程,选择模板
- 选择maven的本地仓库
- java文件夹,源码路径
- 引入基本包+AOP包(AspectJ是基于AOP实现的)
- 配置文件(默认引入beans约束)引入AOP约束(可到HTML文件夹下找XSD文件中的内容),并开启AspectJ的自动代理
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2f2a1021b9667b9c1ab0c15d03cb105d.png)
AspectJ的通知类型
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/3a0177d2f34378fef2afb5c7cc913bd1.png)
- 六种,不同于Spring的传统AOP通知类型
- 环绕通知:用于事务管理,执行前,开启事务,执行后,提交或回滚事务
定义切入点的表达式写法
- 定义某类的某方法使用增强
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/1666641a4f0dbd1148d030b173979594.png)
- 访问修饰符可没有
- 两个点表示包和子包
- *表示 任意返回值类型
- Generic+表示子类或实现类
- save*表示save开头的方法
前置通知
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/7559f5d7f0f8243237151d752d25d09f.jpeg)
@Before(value="myPointcut1()")
public void before(JoinPoint joinPoint){//传入jointpoint对象
System.out.println("前置通知=================="+joinPoint);
}
- 创建目标类
- 创建切面类
- @ Aspect表示切面类
- 切面类中编写通知方法,方法名任意,@Before(value="[execution表达式]")(方法名任意)
- 可在通知方法中传入jointpoint对象,获得切点信息 - 配置文件:配置目标类,配置切面类
/**
* 切面类
*/
@Aspect
public class MyAspectAnno {
@Before(value="myPointcut1()")
public void before(JoinPoint joinPoint){//传入jointpoint对象
System.out.println("前置通知=================="+joinPoint);
}
@AfterReturning(value="myPointcut2()",returning = "result")
public void afterReturing(Object result){ //形参名result与上述的returning名字要相同
System.out.println("后置通知=================="+result);
}
@Around(value="myPointcut3()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知================");
Object obj = joinPoint.proceed(); // 执行目标方法
System.out.println("环绕后通知================");
return obj;
}
@AfterThrowing(value="myPointcut4()",throwing = "e")
public void afterThrowing(Throwable e){
System.out.println("异常抛出通知=============="+e.getMessage());
}
@After(value="myPointcut5()")
public void after(){
System.out.println("最终通知==================");
}
@Pointcut(value="execution(* com.imooc.aspectJ.demo1.ProductDao.save(..))")
private void myPointcut1(){}
@Pointcut(value="execution(* com.imooc.aspectJ.demo1.ProductDao.update(..))")
private void myPointcut2(){}
@Pointcut(value="execution(* com.imooc.aspectJ.demo1.ProductDao.delete(..))")
private void myPointcut3(){}
@Pointcut(value="execution(* com.imooc.aspectJ.demo1.ProductDao.findOne(..))")
private void myPointcut4(){}
@Pointcut(value="execution(* com.imooc.aspectJ.demo1.ProductDao.findAll(..))")
private void myPointcut5(){}
}
<?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 http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启AspectJ的注解开发,自动代理=====================-->
<aop:aspectj-autoproxy/>
<!--目标类===================-->
<bean id="productDao" class="com.imooc.aspectJ.demo1.ProductDao"/>
<!--定义切面-->
<bean class="com.imooc.aspectJ.demo1.MyAspectAnno"/>
</beans>
- AOP是注解方式,IOC是配置方式
- 注入目标类
- 可传入jointpoint对象,获得切点信息
后置通知
- 可获得方法的返回值
- returning=“名字任意,但对应下面的参数”
- 因为返回值可能是任何类型——》Object类接收
@AfterReturning(value="myPointcut2()",returning = "result")
public void afterReturing(Object result){ //形参名result与上述的returning名字要相同
System.out.println("后置通知=================="+result);
}
环绕通知
@Around(value="myPointcut3()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知================");
Object obj = joinPoint.proceed(); // 执行目标方法
System.out.println("环绕后通知================");
return obj;
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8207631884dcfc26b848b085dacbae69.png)
- 环绕通知=前置+后置
- 返回值同目标类的方法,又因为返回类型可能是任意类型,所以返回值类型使用Object(return obj)
- 执行目标方法:proceed
- 该方法有参数为ProceedingJoinPoint
- 要求做异常处理
异常抛出通知
@AfterThrowing(value="myPointcut4()",throwing = "e")
public void afterThrowing(Throwable e){
System.out.println("异常抛出通知=============="+e.getMessage());
}
- 出现异常,才执行,若没有异常,不会执行
- 使用环绕和异常抛出通知——》解决事务问题,若有异常,回滚事务,但spring已提供了事务处理机制
- 可获取异常信息(Throwable e)
最终通知
@After(value="myPointcut5()")
public void after(){
System.out.println("最终通知==================");
}
切点命名
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/cae3b31b698f1098644ea5f7ebe3f57d.png)
- 该注解定义切点,从而统一管理
- 又因为一定要有属性或方法和注解搭配——》使用一个private void无参数的方法
- 外部不用调用——》private修饰
- 定义切点方法,方法名任意(理解为切入点集合的名称) ——》统一管理切入点
- 类比安卓的样式文件,更改样式
@Pointcut(value="execution(* com.imooc.aspectJ.demo1.ProductDao.findOne(..))")
private void myPointcut4(){}
XML方式的AOP方式
public class MyAspectXml {
// 前置通知
public void before(JoinPoint joinPoint){//before这个方法名任意
System.out.println("XML方式的前置通知=============="+joinPoint);
}
// 后置通知
public void afterReturing(Object result){
System.out.println("XML方式的后置通知=============="+result);
}
// 环绕通知
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("XML方式的环绕前通知==============");
Object obj = joinPoint.proceed();
System.out.println("XML方式的环绕后通知==============");
return obj;
}
// 异常抛出通知
public void afterThrowing(Throwable e){
System.out.println("XML方式的异常抛出通知============="+e.getMessage());
}
// 最终通知
public void after(){
System.out.println("XML方式的最终通知=================");
}
}
- XML方式即配置文件方式
- 步骤:定义目标类,定义切面类,配置切面
- 导入包,同注解方式的导包
配置文件
- 使用aop:config标签完成aop配置
- pointcut:切入点,即类的哪些方法用于增强
- aspect:切面,多个切入点和多个通知的组合,说明切面类,应用的增强类型
- advisor:只是一个切入点和一个通知的组合,不常用
<?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 http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--XML的配置方式完成AOP的开发===============-->
<!--配置目标类=================-->
<bean id="customerDao" class="com.imooc.aspectJ.demo2.CustomerDaoImpl"/>
<!--配置切面类-->
<bean id="myAspectXml" class="com.imooc.aspectJ.demo2.MyAspectXml"/>//此处要有id,后面会引用该bean
<!--aop的相关配置=================-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pointcut1" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.save(..))"/>
<aop:pointcut id="pointcut2" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.update(..))"/>
<aop:pointcut id="pointcut3" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.delete(..))"/>
<aop:pointcut id="pointcut4" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.findOne(..))"/>
<aop:pointcut id="pointcut5" expression="execution(* com.imooc.aspectJ.demo2.CustomerDao.findAll(..))"/>
<!--配置AOP的切面-->
<aop:aspect ref="myAspectXml">
<!--配置前置通知-->
<!-method属性值为在切面类中自定义的方法名>
<aop:before method="before" pointcut-ref="pointcut1"/> //在pointcut1上应用before方法来前置增强
<!--配置后置通知-->
<aop:after-returning method="afterReturing" pointcut-ref="pointcut2" returning="result"/> //此result名字同方法中的形参名
<!--配置环绕通知-->
<aop:around method="around" pointcut-ref="pointcut3"/>
<!--配置异常抛出通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="e"/>
<!--配置最终通知-->
<aop:after method="after" pointcut-ref="pointcut5"/>
</aop:aspect>
</aop:config>
</beans>