先说几个常用的概念以及铺垫的知识
横切点:就是我们要给方法前加一个打印日志的功能,或者先校验等等,这些功能
切面(Aspect):将横切关注点进行模块化的一个类
通知(Adviser):就是切面里面具体的方法,用来切入到具体位置的方法里面
切入点(PointCut):一个业务类中的具体方法,就是把通知切入到这个方法里面
连接点(JoinPoint):通过连接点我们可以知道切入点方法对象的很多信息。
通知类型就是想要加的代码(校验、日志等) 是在对象方法的前面还是后面执行的类型,这就是通知类型。
在方法执行前执行
如果方法出现了异常,不会影响前置通知的执行
应用:通常应用在执行方法前各种校验
在方法执行完毕之后执行
无论方法是否出现异常终止都会执行
应用:清理现场,关闭、释放资源
方法执行前后分别执行
如果方法中出现异常终止,那么末尾的通知就不执行了
应用:各个方面,结合了前置和后置的优点,还可以拿到对象方法的各种参数及信息
方法正常返回后执行
如果方法抛出异常,那么无法执行
应用:常规结果数据处理
方法抛出异常后执行
如果方法没有抛出异常,无法执行
应用:抛出异常后对信息进行包装
用来匹配具体切入点(方法)的具体位置
execution(切点表达式)
execution([修饰符] 返回类型 包名.类名.方法名(参数类型))
方法访问修饰符可以进行省略,但是后面的东西必须得有
*(一个星号): 可以匹配任意 返回值、包名、类名、方法名、参数类型
…(两个点):表示任意多个层级、任意多个参数
具体使用
我们想要把切点定义到 com.bit.service 包下的 UserService 类中的 void add(int a) 方法
有很多种写法
1.每一部分都写的具体
execution("void com.bit.service.UserService.add(int)")
2.完全使用通配符
execution("* *..service.*(..)")
//* 任意返回类型
//* 任意一个包
// .. 包后面跟多层
// * 类名
// * 方法名
//(..)任意多个任意参数
execution("* *..*.*(..)")
//* 任意返回类型
//* 任意一个包
// .. 包后面跟多层
// * 类名
// * 方法名
//(..)任意多个任意参数
execution("* *..*(..)")
//* 任意返回类型
//* 任意一个包
// .. 包后面跟多层包+类名(接口名)
// * 方法名
//(..)任意多个任意参数
前置通知,写一个方法,在切入点之前执行通知即可
后置通知,写一个方法,在切入点之前执行通知即可
那么环绕通知的代码如何进行环绕呢?
这里就要用到 连接点(JoinPoint) 的一些使用了
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
JoinPoint 只有获取切入点对象方法信息的一些功能,不能帮助我们进行环绕写代码
能够获取对象方法参数、方法签名等信息
ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中,
添加了
Object proceed() throws Throwable //执行目标方法
Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法
两个方法.
调用proceed方法,相当于执行切入点方法
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around=====方法环绕前====");
joinPoint.proceed(); // 执行方法
System.out.println("Around======方法环绕后=====");
}
1、使用 Object [] args 接收原方法传入的参数
Object[] args = joinPoint.getArgs();
2、对数组中的参数进行修改
for (int i = 0; i <args.length ; i++) {
args[i] = (Integer)args[i]+10;
}
3、执行proceed带参数的方法,将修改过的object[] args进行传入
joinPoint.proceed(args); // 执行方法
在编写SpringAOP面向切面编程时,需要导入一个aspectjweaver.jar的包,它的主要作用是负责解析切入点表达式。
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.9.1version>
dependency>
可以在xml使用 (1)先写一个业务的接口 (2)给这个接口创建创建一个实现类,类中的方法作为切入点。 (3)自定义一个类作为切面,在里面实现一些方法作为具体的通知。 (4)通过xml配置定义切点、切面、通知,以及使得切面里的通知绑定到切点上。 (5)进行测试,执行add方法,查看通知增强是否绑定切入点成功 (6)我们可以确定,通过xml注解的方式,前置通知、后置通知、环绕通知的执行顺序 环绕前和环绕后被 before after包裹着 (1)使用注解 @Aspect 将自定义类作为切面,@PointCut 定义切点的位置,@Before / @After 定义通知作用到切点上 (2)xml文件中可以完全不同写aop:config 的配置内容,但是必须加上aop约束和aop注解支持 (3)进行测试,执行add方法,查看通知增强是否绑定切入点成功 (4) 我们可以确定,通过注解开发的方式,前置通知、后置通知、环绕通知的执行顺序和之前xml配置就不一样了 环绕通知在 before 和 after的外面包裹
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
<context:annotation-config/>
<context:component-scan base-package="com.*"/>
beans>
六、Spring中如何使用AOP
(1)xml配置使用AOP
package com.service;
public interface UserService {
void add();
};
package com.service;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("执行了add方法");
}
}
package com.config;
import org.aspectj.lang.ProceedingJoinPoint;
public class DiyAspect {
// 该类作为切面,所有横切关注点的集合的一个模块
public void before(){
System.out.println("Before=====方法执行前====");
}
public void after(){
System.out.println("After=====方法执行后====");
}
public void around(ProceedingJoinPoint pj) throws Throwable {
System.out.println("Around=====方法环绕前====");
pj.proceed(); // 执行方法
System.out.println("Around======方法环绕后=====");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="diyAspect" class="com.config.DiyAspect"/>
<aop:aspectj-autoproxy />
<aop:config >
<aop:pointcut id="pointcut" expression="execution(* *..service.*.*(..))"/>
<aop:aspect id="diy" ref="diyAspect">
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:around method="around" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
aop:aspect>
aop:config>
<context:annotation-config/>
<context:component-scan base-package="com.*"/>
beans>
package com.controller;
import com.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
public static void main(String[] args) {
ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");
UserService userService = context.getBean("userServiceImpl", UserService.class);
userService.add();
}
}
(2)注解开发使用AOP
package com.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DiyAspect {
// 该类作为切面,所有横切关注点的集合的一个模块
// 可以使用@PointCut 定义一个切点,在之后只需要调用这个方法即可,不需要重复的写切点表达式
@Pointcut("execution( * com.service.UserServiceImpl.add(..))")
public void pointCut(){
}
@Before("pointCut()")
public void before(){
System.out.println("Before=====方法执行前====");
}
@After("pointCut()")
public void after(){
System.out.println("After=====方法执行后====");
}
@Around("pointCut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around=====方法环绕前====");
joinPoint.proceed(); // 执行方法
System.out.println("Around======方法环绕后=====");
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
<context:annotation-config/>
<context:component-scan base-package="com.*"/>
beans>
package com.controller;
import com.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
public static void main(String[] args) {
ApplicationContext context =new ClassPathXmlApplicationContext("spring-config.xml");
UserService userService = context.getBean("userServiceImpl", UserService.class);
userService.add();
}
}