Spring AOP开发 XML配置和注解配置

2022-07-02 10:19:16

1.AOP开发中的相关术语

Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只
支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置通知,后置
通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类
动态地添加一些方法或 Field.
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装在期织入

Proxy(代理) :一个类被 AOP 织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合

2.Spring使用AspectJ进行AOP的开发:XML的方式

2.1导包

spring-aop-4.2.4.RELEASE.jar com.springsource.org.aopalliance-1.0.0.jar

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar spring-aspects-4.2.4.RELEASE.jar

2.2引入Spring的配置文件 :AOP约束(xml catalog引入约束,xml配置文件中导入命名空间)

2.3编写目标类

package com.lzj.service.Impl;
import com.lzj.service.UserService;
@Service(value="useService")//注意在xml中配置自动beansao'miao<context:component-scan base-package="com.lzj"></context:component-scan>
public class UserServiceImpl implements UserService {

	@Override
	public void add() {
		System.out.println("添加记录");
	}

	@Override
	public void delete() {
		System.out.println("删除记录");
	}

	@Override
	public void find() {
		System.out.println("查找记录");
	}

	@Override
	public void update() {
		System.out.println("更新记录");
	}

}

2.4编写通知类

前置通知 :在目标方法执行之前执行.
后置通知 :在目标方法执行之后执行
环绕通知 :在目标方法执行前和执行后执行
异常抛出通知:在目标方法执行出现 异常的时候 执行
最终通知 :无论目标方法是否出现异常 最终通知都会 执行
.

package com.lzj.advice;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAdvise {
	
	//前置通知
	public void before(){
		System.out.println("前置通知");
	}
	//后置通知
	public void afterReturn(){
		System.out.println("后置通知 (只出现在没有发生异常)");
	}
	
	//环绕通知 常用
	//需要ProceedingJoinPoint接口作为参数 
	public Object around(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("环绕通知的之前部分");
		Object proceed = pjp.proceed();
		System.out.println("环绕通知的之后部分");
		return proceed;
	}
	
	//异常抛出通知
	public void afterException(){
		System.out.println("异常出现之后的通知");
	}
	
	//最终通知
	public void after(){
		System.out.println("后置通知(不管是否发生异常)");
	}


}

2.5切入点表达式execution(表达式)
表达式:
[
方法访问修饰符]方法返回值 包名.类名.方法名(方法的参数)
public * cn.itcast.spring.dao.*.*(..) //参数任意用..表示
* cn.itcast.spring.dao.*.*(..)
* cn.itcast.spring.dao.*UserDao.*(..)

* cn.itcast.spring.dao..*.*(..)

例如定义切入点表达式 execution (* com.sample.service.impl..*. *(..))
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型, *号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点..表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示类名,*号表示所有的类。
5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
2.6配置xml完成增强

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
	<context:component-scan base-package="com.lzj"></context:component-scan>
	<aop:config>
	 	<!-- 配置切入点  expression填写切入点表达式 --> 
	 	<!-- 切记写上execution前缀 刚刚就没写 启动报错! -->
		<aop:pointcut expression="execution(* com.lzj.service.Impl..*.add(..))" id="pointcut"/>
		
		<!-- 配置切面 切面是切入点和通知的结合 -->
		<aop:aspect ref="myAdvise">
		
		<!-- 指定名为before方法作为前置通知 -->
			<aop:before method="before" pointcut-ref="pointcut"/>
			<aop:around method="around" pointcut-ref="pointcut"/>
			<aop:after-returning method="afterReturn" pointcut-ref="pointcut"/>
			<aop:after-throwing method="afterException" pointcut-ref="pointcut"/>
			<aop:around method="around" pointcut-ref="pointcut"/>
		</aop:aspect>
	</aop:config>
</beans>

测试方法

package com.lzj.demo;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.lzj.service.UserService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
	
	@Resource(name="userService")
	private UserService us;
	
	@Test
	public void fun1(){
		us.add();
	}

}

测试结果:



3.注解方式配置

非常简单 擦亮你的双眼 !

只需要在通知类中注解写上切入点(PointCut)

第一步 很重要!!!!!!!!!!!!!!!!!!!我第一步忘记做了,足足debug了两个小时 真tm气!暴打梦婷

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" 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.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
	<context:component-scan base-package="com.lzj"></context:component-scan>

	<!-- 3.开启使用注解完成织入 这一步非常重要-->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
	
</beans>

第二步 在通知类中织入

package com.lzj.advice;

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.springframework.stereotype.Repository;

//声明这是一个切面(切入点+通知)

@Repository(value="myAdvise")
@Aspect
public class MyAdvise {
	
	@Before("execution(* com.lzj.service.*Impl..*.add(..))")
	//前置通知
	public void before(){
		System.out.println("前置通知");
	}
	//后置通知
	@AfterReturning("execution(* com.lzj.service.*Impl..*.add(..))")
	public void afterReturn(){
		System.out.println("后置通知 (只出现在没有发生异常)");
	}
	
	@Around("execution(* com.lzj.service.*Impl..*.add(..))")
	//环绕通知 常用
	//需要ProceedingJoinPoint接口作为参数 
	public Object around(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("环绕通知的之前部分");
		Object proceed = pjp.proceed();
		System.out.println("环绕通知的之后部分");
		return proceed;
	}
	
	@AfterThrowing("execution(* com.lzj.service.*Impl..*.add(..))")
	//异常抛出通知
	public void afterException(){
		System.out.println("异常出现之后的通知");
	}
	
	@After("execution(* com.lzj.service.*Impl..*.add(..))")
	//最终通知
	public void after(){
		System.out.println("后置通知(不管是否发生异常)");
	}


}
  • 作者:哈哈我是大健
  • 原文链接:https://blog.csdn.net/qq_35232663/article/details/79617270
    更新时间:2022-07-02 10:19:16