spring之AOP(即面向切面编程)&&五大通知(前置通知、后置通知、异常通知、环绕通知、过滤通知)的案例

转载请标明出处:https://blog.csdn.net/men_ma/article/details/106847165.
本文出自 不怕报错 就怕不报错的小猿猿 的博客

目录 结构

在这里插入图片描述

1.AOP(即面向切面编程)的关键性概念

首先上图,我们来理解AOP
图中的专业名词解释:

  • 目标(Target)被通知(被代理)的对象      注1:完成具体的业务逻辑

  • 通知(Advice)在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理)      注2:完成切面编程

  • 代理(Proxy)将通知应用到目标对象后创建的对象(代理=目标+通知),
                     例子:外科医生+护士 注3:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的

  • 切入点(Pointcut)多个连接点的集合,定义了通知应该应用到那些连接点。
    (也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序)

  • 适配器(Advisor)适配器=通知(Advice)+切入点(Pointcut)

在这里插入图片描述

2. AOP的核心点(通知)

通知实现接口应用场景
前置通知实现org.springframework.aop.MethodBeforeAdvice接口买书、评论前加系统日志
后置通知实现org.springframework.aop.AfterReturningAdvice接口买书返利(存在bug)
环绕通知实现org.aopalliance.intercept.MethodInterceptor接口类似拦截器,会包括切入点,目标类前后都会执行代码。
异常通知实现org.springframework.aop.ThrowsAdvice接口出现异常执行系统提示,然后进行处理。价格异常为例
过滤通知(适配器)实现org.springframework.aop.support.RegexpMethodPointcutAdvisor接口处理买书返利的bug

各种通知的案例准备工作及用到的工具类(提前准备):

接口类IBookBiz:

package com.xiaoqing.aop.biz;

/**
 * 接口类
 * @author 晴sister
 * 2020年8月9日22:55:27
 *
 */
public interface IBookBiz {
	// 购书
	public boolean buy(String userName, String bookName, Double price);

	// 发表书评
	public void comment(String userName, String comments);
}

接口类IBookBiz的实现类BookBizImpl:

package com.xiaoqing.aop.biz;

import com.xiaoqing.aop.ex.PriceException;
/**
 * IBookBiz接口的实现类
 * @author 晴sister
 * 2020年8月9日22:55:59
 *
 */
public class BookBizImpl implements IBookBiz {

	public BookBizImpl() {
		super();
	}

	public boolean buy(String userName, String bookName, Double price) {
		// 通过控制台的输出方式模拟购书
		if (null == price || price <= 0) {
			throw new PriceException("book price exception");
		}
		System.out.println(userName + " buy " + bookName + ", spend " + price);
		return true;
	}

	public void comment(String userName, String comments) {
		// 通过控制台的输出方式模拟发表书评
		System.out.println(userName + " say:" + comments);
	}

}

异常通知所需的运行时异常PriceException类:

package com.xiaoqing.aop.ex;
/**
 * 运行时异常
 * @author 晴sister
 * 2020年8月9日22:50:27
 *
 */
public class PriceException extends RuntimeException {

	public PriceException() {
		super();
	}
	public PriceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}
	public PriceException(String message, Throwable cause) {
		super(message, cause);
	}
	public PriceException(String message) {
		super(message);
	}
	public PriceException(Throwable cause) {
		super(cause);
	}
}

spring-context.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"
	default-autowire="byType"
	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.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
		<!-- **************************AOP************************************  -->
	<!-- 目标对象 -->
	<bean id="bookBiz" class="com.xiaoqing.aop.biz.BookBizImpl"></bean>
	<!-- 通知 -->
	<bean id="myBefore" class="com.xiaoqing.aop.advice.MyMethodBeforeAdvice"></bean>
	<bean id="myAfter" class="com.xiaoqing.aop.advice.MyAfterReturningAdvice"></bean>
	<bean id="myInterceptor" class="com.xiaoqing.aop.advice.MythodInterceptor"></bean>
	<bean id="myThrowsAdvice" class="com.xiaoqing.aop.advice.MyThrowsAdvice"></bean>
	<bean id="myAfter2" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<property name="advice" ref="myAfter"></property>
		<property name="pattern" value=".*buy"></property>
	</bean>
	<!-- 由代理工厂来组装目标对象及通知 -->
	<bean id="bookProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 原料 -->
		<!-- target:目标对象是谁   ref:引用进来,本身不变-->
		<property name="target" ref="bookBiz"></property>
		<!-- proxyInterfaces:代理的哪个接口 -->
		<property name="proxyInterfaces">
			<!-- 有时实现的接口不只一个,所以我们用列表list -->
			<list>
				<value>com.xiaoqing.aop.biz.IBookBiz</value>
			</list>
		</property>
		<!--interceptorNames:需要应用到目标对象上的通知Bean的名字。(List)  -->
		<property name="interceptorNames">
			<list>
			<!--通知  -->
				<value>myBefore</value>
				<!-- <value>myAfter</value> -->
				<value>myInterceptor</value>
				<value>myThrowsAdvice</value>
				<value>myAfter2</value>
			</list>
		</property>
	</bean>
</beans>
	

测试AOP的main方法代码(由于每个通知的测试代码都是一样的,所以在这里博主就不每个通知的案例上都重复加上测试类AopTest.java了,在这里附上,所有的运行结果都是这个文件,为减少文章的篇幅长度):

测试类AopTest.java:

public class AopTest {
	public static void main(String[] args) {
//		获取到spring-Context.xml配置文件
		ApplicationContext springContext = new ClassPathXmlApplicationContext("/spring-context.xml");
//		根据bean的id得到实现类
		IBookBiz bean =(IBookBiz)springContext.getBean("bookProxy");
//		输出你得到的类的全包名(相当于类对象)
		System.out.println(bean.getClass());
//		用类对象去调用里面的买书方法
		boolean buy = bean.buy("寻坤","圣墟" , 66d);
//		用类对象去调用里面的发表书评
		bean.comment("寻坤", "真的虚了");
	}
}

在前面的这些工具类或者配置文件,博主先上了,后面我们讲案例时,我会截图哪里用到哪里,这样我们自己也不会混乱

2.1 前置通知(案例(需求):买书、评论前加系统日志)

前置通知MyMethodBeforeAdvice (要实现MethodBeforeAdvice接口):

/**
 * 前置通知
 * @author 晴sister
 * 2020年8月9日22:51:27
 *
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method method, Object[] arg1, Object target) throws Throwable {
		// TODO Auto-generated method stub
//		获取到哪个类被调用了
		String clazName = target.getClass().getName();
//		获取到哪个方法被调用了
		String methodName = method.getName();
//		看看带了哪些参数,如果不要Arrays.toString,打印的是一串地址
		String params = Arrays.toString(arg1);
		System.out.println("买书、评论前加系统日志:"+clazName+","+methodName+"("+params+")");
	}
}

注意spring-context.xml的修改:此上这个文档中的内容代码已附上
在这里插入图片描述
运行效果:
在这里插入图片描述

2.2 后置通知(案例(需求):买书返利(存在bug))

后置通知MyAfterReturningAdvice (要实现AfterReturningAdvice接口):

/**
 * 后置通知:
 * 		买书返利(存在bug)
 * @author 晴sister
 *2020年8月5日23:14:58
 */
public class MyAfterReturningAdvice implements AfterReturningAdvice{

	@Override
	public void afterReturning(Object returnValue, Method method, Object[] arg1, Object target) throws Throwable {
		// TODO Auto-generated method stub
		String clazName = target.getClass().getName();
		String methodName = method.getName();
		String params = Arrays.toString(arg1);
		System.out.println("买书返利的后置通知:"+clazName+","+methodName+"("+params+")"+"\t\t目标对象方法调用后的返回值"+returnValue);
	}

}


注意spring-context.xml的修改:此上这个文档中的内容代码已附上
在这里插入图片描述

运行效果:
在这里插入图片描述

2.3 环绕通知:包含前置+后置通知(案例(需求):类似拦截器,会包括切入点,目标类前后都会执行代码)

环绕通知MythodInterceptor(要实现MethodInterceptor接口):

/**
 * 环绕通知
 * @author 晴sister
 * 2020年8月9日22:51:02
 *
 */
public class MythodInterceptor implements MethodInterceptor{

	@Override
	public Object invoke(MethodInvocation invacation) throws Throwable {
		String clazName = invacation.getThis().getClass().getName();
		String methodName = invacation.getMethod().getName();
		String params = Arrays.toString(invacation.getArguments());
//		sessionFactory.openSession,session.beginTransation
		System.out.println("环绕通知:"+clazName+","+methodName+"("+params+")");
		Object returnValue = invacation.proceed();
//		transation.commit(),session.close()
		System.out.println("环绕通知:\t\t目标对象方法调用后的返回值"+returnValue);
		
		return returnValue;
	}

}


注意spring-context.xml的修改:此上这个文档中的内容代码已附上
在这里插入图片描述

运行效果(环绕通知运行结果):
在这里插入图片描述

2.4 异常通知:(案例(需求):出现异常执行系统提示,然后进行处理。价格异常为例)

在这里插入图片描述

异常通知MyThrowsAdvice(要实现ThrowsAdvice接口):


/**
 * 异常通知
 * @author Administrator
 * 	案例:张三向李四转账
 * 		biz.transfer(user1,user2)
 * 			UserDao.update(user1)
 * 			UserDao.update(user2)
 *
 */
public class MyThrowsAdvice implements ThrowsAdvice {
//		处理错误的代码,比如数据说明时候回滚就在这里处理
	public void afterThrowing( PriceException ex ) {
		System.out.println("价格输入有误,购买失败,请重新输入!!!");
	}
}

注意spring-context.xml的修改:此上这个文档中的内容代码已附上
在这里插入图片描述

测试类AopTest.java:
在这里插入图片描述

public class AopTest {
	public static void main(String[] args) {
//		获取到spring-Context.xml配置文件
		ApplicationContext springContext = new ClassPathXmlApplicationContext("/spring-context.xml");
//		根据bean的id得到实现类
		IBookBiz bean =(IBookBiz)springContext.getBean("bookProxy");
//		输出你得到的类的全包名(相当于类对象)
		System.out.println(bean.getClass());
//		用类对象去调用里面的买书方法
		boolean buy = bean.buy("寻坤","圣墟" , -66d);
//		用类对象去调用里面的发表书评
//		bean.comment("寻坤", "真的虚了");
	}
}

运行效果(异常通知运行结果):有解决方案
在这里插入图片描述

2.5 过滤通知:(案例(需求):处理买书返利的bug)

重现之前后置通知(myAfter)的运行结果有一bug如下:
在这里插入图片描述

现利用过滤通知(MyAfter2)来升级解决这个bug:

注意spring-context.xml的修改:此上这个文档中的内容代码已附上
在这里插入图片描述

运行效果(过滤通知运行结果):
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值