一、注解配置AOP
Spring可以使用注解代替配置文件配置切面:
1. 开启注解支持
在xml中开启AOP注解支持
以下是bean1.xml文件
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 扫描包 -->
<context:component-scan base-package="com.example"></context:component-scan>
<!-- 开启注解配置Aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
2. 在类和方法加入注解
在通知类上方加入注解 @Aspect:配置切面 在通知方法上方加入注解
- @Before:前置通知
- @AfterReturning:后置通知
- @AfterThrowing:异常通知
- @After:最终通知
- @Around:环绕通知
MyAspectAdvice通知类
代码语言:javascript复制package com.example.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class MyAspectJAdvice {
// 后置通知
@AfterReturning("execution(* com.example.dao.UserDao.*(..))")
public void myAfterReturning(JoinPoint joinPoint){
System.out.println("切点方法名:" joinPoint.getSignature().getName());
System.out.println("目标对象:" joinPoint.getTarget());
System.out.println("打印日志···" joinPoint.getSignature().getName() "方法被执行了!");
}
// 前置通知
@Before("execution(* com.example.dao.UserDao.*(..))")
public void myBefore(){
System.out.println("前置通知···");
}
// 异常通知
@AfterThrowing(value = "execution(* com.example.dao.UserDao.*(..))",throwing = "e")
public void myAfterThrowing(Exception e){
System.out.println("异常通知···");
System.out.println(e.getMessage());
}
// 最终通知
@After("execution(* com.example.dao.UserDao.*(..))")
public void myAfter(){
System.out.println("最终通知···");
}
// 环绕通知
@Around("execution(* com.example.dao.UserDao.*(..))")
public Object myAround(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕前···");
// 执行方法
Object obj = point.proceed();
System.out.println("环绕后···");
return obj;
}
}
3. 测试
测试方法
代码语言:javascript复制 // 测试注解开发AOP
@Test
public void testAdd2(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
//userDao.update();
userDao.delete();
}
测试结果(无异常):
使用update方法测试结果(有异常):
添加描述
可以看到环绕后没有打印,因为此时碰到异常终止了程序
4. 为一个类下的所有方法统一配置切点
如何为一个类下的所有方法统一配置切点:
在通知类中添加方法配置切点
代码语言:javascript复制 // 添加方法配置切点
@Pointcut("execution(* com.example.dao.UserDao.*(..))")
public void pointcut(){
}
在通知方法上使用定义好的切点,就是把注解括号里面得内容替换成 "pointCut()" 即可。
二、原生Spring实现AOP
除了AspectJ,Spring支持原生方式实现AOP。但是要注意的是原生方式实现AOP只有四种通知类型:前置通知、后置通知、环绕通知,异常通知。少了最终通知。
1. 引入依赖
代码语言:javascript复制 <!-- AOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.13</version>
</dependency>
2. 编写SpringAOP通知类
代码语言:javascript复制Spring原生方式实现AOP时,只支持四种通知类型: 通知类型实现接口前置通知MethodBeforeAdvice后置通知AfterReturningAdvice异常通知ThrowsAdvice环绕通知MethodInterceptor
package com.example.aspect;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
public class SpringAop implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice, MethodInterceptor {
/**
* 环绕通知
* @param invocation 目标方法
* @return
* @throws Throwable
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("环绕前");
Object proceed = invocation.proceed();
System.out.println("环绕后");
return proceed;
}
/**
* 后置通知
* @param returnValue 目标方法的返回值
* @param method 目标方法
* @param args 目标方法的参数列表
* @param target 目标对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置通知");
}
/**
* 前置通知
* @param method 目标方法
* @param args 目标方法的参数列表
* @param target 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知");
}
/**
* 异常通知
* @param e 异常对象
*/
public void afterThrowing(Exception e){
System.out.println("发生异常了!");
}
}
3. 编写配置类bean2.xml
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.example"></context:component-scan>
<!-- 通知对象 -->
<bean id="springAop" class="com.example.aspect.SpringAop"/>
<!-- 配置代理对象 -->
<bean id="userDaoProxy"class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 配置目标对象 -->
<property name="target" ref="userDao"/>
<!-- 配置通知 -->
<property name="interceptorNames">
<list>
<value>springAop</value>
</list>
</property>
<!-- 代理对象的生成方式 true:使用CGLib false:使用原生JDK生成 -->
<property name="proxyTargetClass" value="true"/>
<!-- bug -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</bean>
</beans>
4 测试
测试类UserDaoTest2
代码语言:javascript复制import com.example.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class userDaoTest2 {
// 原生AOP测试
@Test
public void testAdd(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
UserDao userDao = (UserDao) ac.getBean("userDaoProxy");
userDao.update();
}
}
测试结果
添加描述
可以看到环绕后没有打印,因为此时碰到异常终止了程序
4. 为一个类下的所有方法统一配置切点
如何为一个类下的所有方法统一配置切点:
在通知类中添加方法配置切点
代码语言:javascript复制 // 添加方法配置切点
@Pointcut("execution(* com.example.dao.UserDao.*(..))")
public void pointcut(){
}
在通知方法上使用定义好的切点,就是把注解括号里面得内容替换成 "pointCut()" 即可。
二、原生Spring实现AOP
除了AspectJ,Spring支持原生方式实现AOP。但是要注意的是原生方式实现AOP只有四种通知类型:前置通知、后置通知、环绕通知,异常通知。少了最终通知。
1. 引入依赖
代码语言:javascript复制 <!-- AOP -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.13</version>
</dependency>
2. 编写SpringAOP通知类
代码语言:javascript复制Spring原生方式实现AOP时,只支持四种通知类型: 通知类型实现接口前置通知MethodBeforeAdvice后置通知AfterReturningAdvice异常通知ThrowsAdvice环绕通知MethodInterceptor
package com.example.aspect;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
public class SpringAop implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice, MethodInterceptor {
/**
* 环绕通知
* @param invocation 目标方法
* @return
* @throws Throwable
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("环绕前");
Object proceed = invocation.proceed();
System.out.println("环绕后");
return proceed;
}
/**
* 后置通知
* @param returnValue 目标方法的返回值
* @param method 目标方法
* @param args 目标方法的参数列表
* @param target 目标对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置通知");
}
/**
* 前置通知
* @param method 目标方法
* @param args 目标方法的参数列表
* @param target 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知");
}
/**
* 异常通知
* @param e 异常对象
*/
public void afterThrowing(Exception e){
System.out.println("发生异常了!");
}
}
3. 编写配置类bean2.xml
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.example"></context:component-scan>
<!-- 通知对象 -->
<bean id="springAop" class="com.example.aspect.SpringAop"/>
<!-- 配置代理对象 -->
<bean id="userDaoProxy"class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 配置目标对象 -->
<property name="target" ref="userDao"/>
<!-- 配置通知 -->
<property name="interceptorNames">
<list>
<value>springAop</value>
</list>
</property>
<!-- 代理对象的生成方式 true:使用CGLib false:使用原生JDK生成 -->
<property name="proxyTargetClass" value="true"/>
<!-- bug -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</bean>
</beans>
4 测试
测试类UserDaoTest2
代码语言:javascript复制import com.example.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class userDaoTest2 {
// 原生AOP测试
@Test
public void testAdd(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
UserDao userDao = (UserDao) ac.getBean("userDaoProxy");
userDao.update();
}
}
测试结果
添加描述
OK,这里有个惊喜,如果敲到这里同学,就知道了 其实标签那个bug,是因为原生方法配置类要加上那个标签才可以识别,否则会报一个错误。
三、SchemaBased实现AOP
SchemaBased(基础模式)配置方式是指使用Spring原生方式定义通知,而使用AspectJ框架配置切面。因此这里通知类和上面一样,看上面的即可。
1. 配置切面
aop3.xml配置文件
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.example"></context:component-scan>
<!-- 通知对象 -->
<bean id="springAop2" class="com.example.aspect.SpringAop"/>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut id="myPointcut" expression="execution(* com.example.dao.UserDao.*(..))"/>
<!-- 配置切面:advice-ref:通知对象 pointcut-ref:切点 -->
<aop:advisor advice-ref="springAop2" pointcut-ref="myPointcut"/>
</aop:config>
</beans>
2. 测试
测试方法
代码语言:javascript复制 // 使用AspectJ框架配置切面测试
@Test
public void t6(){
ApplicationContext ac = new ClassPathXmlApplicationContext("aop3.xml");
UserDao userDao = (UserDao) ac.getBean("userDao");
userDao.add();
}
测试结果
OK,这里的输出应该是我上面定义的不够完美,有些可能重复定义了,所以这里就重复输出了一些东西
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!