Spring 基于XML配置的AOP框架详细讲解

学习Spring中的AOP,就是通过配置的方式(有基于XML配置的, 以及基于注解配置的),来实现相关的拦截切入功能。对原有的操作进行加强,但不影响原本的操作。

目录

学习Spring中的AOP,就是通过配置的方式(有基于XML配置的, 以及基于注解配置的),来实现相关的拦截切入功能。对原有的操作进行加强,但不影响原本的操作。

1.理解:Aop进行的加强就是如此,你仅仅是想要查询用户,但是在你进行的过程中我对你的过程进行的扩充操作。(小编个人的理解)

​ 2.必须的jar包

3.大致分为两类进行学习

4.xml环境配置

5.main函数调用

6.IUserService接口

7.IUserService接口实现类

8.织入文件MyLogger

9.aop配置解释:

10.切入点表达式(* 的使用)


1.理解:Aop进行的加强就是如此,你仅仅是想要查询用户,但是在你进行的过程中我对你的过程进行的扩充操作。(小编个人的理解)

 2.必须的jar包

3.大致分为两类进行学习

(1)前置,后置,异常,最终

  •  前置通知:在切入点方法执行之前执行
  • 后置通知:在切入点方法正常执行之后执行。它和异常通知永远只能执行一
  • 异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个
  • 最终通知:无论切入点方法是否正常执行它都会在其后面执行

(2)环绕

  • 前面是通过配置的方式指定增强的代码何时执行,而现在是通过代码控 制的方式来指定增强的代码何时执行。
  • 所以说环绕通知是Spring框架提供的一种可以在代码中手动控制增强方法何时执行的方式。

4.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
	   https://www.springframework.org/schema/beans/spring-beans.xsd
	   http://www.springframework.org/schema/aop
	   https://www.springframework.org/schema/aop/spring-aop.xsd">

	<bean id="userServiceImpl"
		class="com.bookmanagesystem.service.impl.UserServiceImpl"></bean>
	<bean id="mylogger"
		class="com.bookmanagesystem.util.MyLogger">


	</bean>
	<aop:config>
		<aop:pointcut
			expression="execution(* com.iflytek.bookmanagesystem.service.impl.*.*(..) )"
			id="pc1" />
		<!-- 3. 配置切面 -->
		<aop:aspect id="logAdvice" ref="mylogger">
			<!-- 配置通知的类型 (原始方法) -->
			<!-- <aop:before method="printLog" pointcut-ref="pc1" /> <aop:after-returning method="printLog" pointcut-ref="pc1" /> -->

			<!-- <aop:before method="printLog" pointcut="execution(public void com.bookmanagesystem.service.impl.UserServiceImpl.delete() 
				)" /> -->
			<!-- 1. 配置前置通知 -->
			<!-- <aop:before method="beforePrintLog" pointcut-ref="pc1" /> -->
			<!-- 2. 配置后置通知 -->
			<!-- <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pc1" 
				/> -->
			<!-- 3. 配置异常通知 -->
			<!-- <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pc1" 
				/> -->
			<!-- 4. 配置最终通知 -->
			<!-- <aop:after method="afterPrintLog" pointcut-ref="pc1" /> -->
			<!-- 5. 配置环绕通知 -->
			<aop:around method="aroundPrintLog" pointcut-ref="pc1" />
		</aop:aspect>
	</aop:config>

</beans>

5.main函数调用

public static void main(String[] args) {
        // 1. 获取核心容器对象
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 2. 根据id获取bean对象
        IUserService service = (IUserService) context.getBean("userServiceImpl");
        service.update();

        // 3.关闭容器(如果不记得关闭容器,最典型的问题就是数据库连接不能释放)
        ((ClassPathXmlApplicationContext) context).close();
    }

6.IUserService接口

public interface IUserService {
	
    void update();
    // 模拟更新用户操作
}

7.IUserService接口实现类

public class UserServiceImpl implements IUserService {
       //模拟的
	@Override
	public void update() {
        System.out.println("进行修改用户的操作");
	}

}

8.织入文件MyLogger

public class MyLogger {
	/**
	 * 用于打印日志,并且让其在切入点方法执行之前执行(切入点方法就是业务方法)
	 */

	// 前置通知
	public void beforePrintLog() {
		System.out.println("前置通知MyLogger类中的beforePrintLog方法开始记录日志了。。。");
	}

	// 后置通知
	public void afterReturningPrintLog() {
		System.out.println("后置通知MyLogger类中的afterReturningPrintLog方法开始记录日志了。。。");
	}

	// 异常通知
	public void afterThrowingPrintLog() {
		System.out.println("异常通知MyLogger类中的afterThrowingPrintLog方法开始记录日志了。。。");
	}

	// 最终通知
	public void afterPrintLog() {
		System.out.println("最终通知MyLogger类中的afterPrintLog方法开始记录日志了。。。");
	}
	//环绕通知
	public Object aroundPrintLog(ProceedingJoinPoint pjp) {
	Object result = null;
	try {
	Object[] args = pjp.getArgs(); // 得到方法执行所需的参数
	System.out.println("环绕通知记录日志前置");
	result = pjp.proceed(args); // 明确调用业务层方法(切入点方法)
	System.out.println("环绕通知记录日志后置");
	return result;
	} catch (Throwable e) { // proceed方法抛出了Throwable,这里用Exception拦不住
	System.out.println("环绕通知记录日志异常");
	e.printStackTrace();
	throw new RuntimeException(e);
	} finally {
	System.out.println("环绕通知记录日最终");
	} }
}

9.aop配置解释:

1. 把通知Bean也交给Spring管理
2. 使用 aop:config 标签表明开始AOP的配置
3. 使用 aop:aspect 标签表明配置切面
属性:
» id:给切面提供一个唯一标识
» ref :指定通知类bean的id
4. 在 aop:aspect 标签内部使用对应标签来配置通知的类型
例如现在想让pringLog方法在切入点方法执行之前执行,所以是前置通知
标签: aop:before 除此之外还有其他通知类型的标签
属性:
» method :指定Logger类中哪个方法是前置通知
Spring中基于XML的AOP配置步骤
继续在 aop:before 标签中添加一个属性: pointcut 切入点属性,用来指定一
切入点表达式 ,就是指要对业务层中哪些方法进行增强。
切入点表达式写法: execution(表达式) ,其中
表达式: 访问修饰符 返回值 包名.包名...类名.方法名(参数列表)
标准的表达式写法:public void com.bookmanagesystem.service.impl.UserServiceImpl.update()

<aop:config>//表明我开始配置Aop了
        <aop:pointcut expression="execution(* com.bookmanagesystem.service.impl.*.*(..) )"
            id="pc1" />//1.一种简单的写法也可以写成上面具体的 2.把我bookmanagesystem.service.impl.UserServiceImpl.包下面所有的方法我都给他起了一个名字叫做“pc1”
        <!-- 3. 配置切面 -->
        <aop:aspect id="logAdvice" ref="mylogger">//ref 让我的切面和织入文件联系起来好进行加强操作
            <aop:around method="aroundPrintLog" pointcut-ref="pc1" />//环绕调用的方法即是我MyLogger类中的public Object aroundPrintLog(ProceedingJoinPoint pjp) {}方法,因为pc1包含了所有包com.bookmanagesystem.service.impl.下我写的方法
        </aop:aspect>
    </aop:config>

虽然现在切入点表达式精简了,但是后面要配置多种类型的通知时,还是需
要对不同的通知类型配置一个 pointcut 属性,就要写一次切入点表达式,都
是重复的内容,还是需要进一步优化一下,将切入点表达式提取出来。
通过标签 <aop:pointcut> 来配置切入点表达式,之后在配置通知的时候使
用其中的 pointcut-ref 属性来指定已经配好的切入点表达式即可:
标签: <aop:pointcut />
属性:
» id :指定表达式的唯一标识
» expression :指定表达式内容
书写位置:在 <aop:config> 内部,且需注意:
» 如果此标签写在 <aop:aspect>标签内部 ,只能在当前切面中使用
» 如果写在 <aop:config>内、<aop:aspect>标签外部 ,则所有切面可以使
用,但是注意约束中要求必须出现在<aop:aspect>标签的前面

10.切入点表达式(* 的使用

切入点表达式的通配写法(总结:什么都可以用(*)表示,参数用(..)表示)
 标准写法: public int
com.bookmanagesystem.service.impl.UserServiceImpl.update()
1. 访问修饰符可以省略: int
com.bookmanagesystem.service.impl.UserServiceImpl.update()
2. 返回值可以使用通配符 * 表示任意返回值类型: *
com.bookmanagesystem.service.impl.UserServiceImpl.update()
3. 包名可以使用通配符表示任意包,但是有几级包就需要写几个*: *
*.*.*.*.*.UserServiceImpl.update()
4. 包名中还可以使用 .. 来表示当前包及其子包,因此可以使用 *.. 来表示任意
包及子包: * *..UserServiceImpl.update()
5. 类名和方法名都可以使用通配符: * *..*.*() 注意:此时只能增强2个方法,
因为第二个方法有参数
6. 关于参数:
可以直接写参数的数据类型:
» 基本类型直接写名称,如:* *..*.*(int)
» 引用类型写全限定名称,如:* *..*.*(java.lang.String)
可以使用通配符表示任意的参数类型,但是必须要有参数:* *..*.*(*)
可以使用 .. 来表示有无参数都可以,有参数的话可以是任意类型:* *..*.*(..)
7. 全通配写法 * *..*.*(..)
8. 实际开发中切入点表达式的通常写法:切到业务层实现类下的所有写法:
* com.bookmanagesystem.service.impl.*.*(..)
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值