Aop(面向切面编程):
它是一种编程思想,通过Aop将核心业务代码和其他代码(日志、事务等)分离出来,使得代码低耦合,高可用,提高了开发的效率
使用场景:日志记录,性能统计,安全控制,事务处理,异常处理等等
百度百科Aop:
Aop实现方案:
【重点讲解第三种方案的实现方式】
一:AspectJ强大的表达式:
表达式模板:execution(修饰符 返回值 包.类.方法名(参数) throws异常)
修饰符,一般省略
public 公共方法
* 任意
返回值,不能省略
void 返回没有值
String 返回值字符串
* 任意
包
com.zby.service 固定包
com.zby.oa.*.service oa包下面子包 (例如:com.zby.oa.flow.service)
com.zby.oa.. oa包下面的所有子包(含自己)
com.zby.oa.*.service.. oa包下面任意子包,固定目录service,service目录任意包
类
UserServiceImpl 指定类
*Impl 以Impl结尾
User* 以User开头
* 任意
方法名,不能省略
addUser 固定方法
add* 以add开头
*Do 以Do结尾
* 任意
(参数)
() 无参
(int) 一个整型
(int ,int) 两个
(..) 参数任意
throws ,可省略,一般不写
二:AspectJ支持5种类型的通知注解
before:前置通知
在方法执行前执行
afterReturning:后置通知
方法正常返回后执行,如果方法中抛出异常,通知无法执行,必须在方法执行后才执行,所以可以获得方法的返回值。
afterThrowing:抛出异常通知
方法抛出异常后执行,如果方法没有抛出异常,无法执行
after:最终通知
方法执行完毕后执行,无论方法中是否出现异常
around:环绕通知(很强大,可以直接代替其他四个通知)
方法执行前后分别执行,可以阻止方法的执行,必须手动执行目标方法
三:项目导入maven依赖
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
四:基于xml配置实现AspectJ
1:定义UserController
public class UserController {
public int addUser() {
return 1;
}
public int deleteUser() {
return 1;
}
public int updateUser() {
return 1;
}
}
2:定义切面类 实现功能:保存用户操作记录
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class AopLogging {
// 前置通知
public void before(JoinPoint joinPoint) {
// 获取方法名称
String name = joinPoint.getSignature().getName();
// 获取方法参数
Object[] args = joinPoint.getArgs();
// 实际项目中根据session中的用户信息,保存操作记录到数据库
if (name.equals("add")) {
System.out.println("【用户执行添加操作】");
} else if (name.equals("update")) {
System.out.println("【用户执行更新操作】");
} else {
System.out.println("【用户执行删除操作】");
}
}
// 返回通知 result:方法执行返回的结果
public void afterReturning(JoinPoint joinPoint, Object result) {
// 获取方法名称
String name = joinPoint.getSignature().getName();
// 打印方法执行结果
System.out.println(name + "运行结果:" + result);
}
// 异常通知 exception:方法运行出错时抛出的异常
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
// 获取方法名称
String name = joinPoint.getSignature().getName();
// 打印异常信息
System.out.println(name + "运行异常信息:" + exception);
}
// 最终通知
public void after(JoinPoint joinPoint) {
System.out.println("最终通知");
}
//环绕通知
public Object around(ProceedingJoinPoint joinPoint){
//这里操作相当于前置通知
System.out.println("环绕==前置通知");
Object obj = null;
try {
obj = joinPoint.proceed();
//这里操作是后置通知
System.out.println("环绕==后置通知");
} catch (Throwable e) {
//这里操作是异常通知
System.out.println("环绕==异常通知");
e.printStackTrace();
}
System.out.println("环绕==最终通知");
return obj;
}
}
3:配置xml
<!-- 配置Bean -->
<bean id="userController" class="com.mote.controller.UserController" />
<bean id="aopLogging" class="com.mote.aop.AopLogging" />
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pointcut"
expression="execution(public * com.mote.controller.UserController.*(..)) " />
<!-- 配置切面 -->
<aop:aspect ref="aopLogging">
<!-- 配置相应通知 -->
<aop:before pointcut-ref="pointcut" method="before" />
<!-- result和exception需要和方法中的参数一致 -->
<aop:after-returning pointcut-ref="pointcut" method="afterReturning" returning="result" />
<aop:after-throwing pointcut-ref="pointcut" method="afterThrowing" throwing="exception" />
<aop:after pointcut-ref="pointcut" method="after" />
<aop:around pointcut-ref="pointcut" method="around"/>
</aop:aspect>
</aop:config>
4:测试打印
@Test
public void testXml() {
//创建容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//从容器中获取bean
UserController userController = context.getBean(UserController.class);
//调用方法
userController.deleteUser();
}
五:纯注解方式实现AspectJ
1:定义UserController,同上
2:定义切面类
import org.aspectj.lang.JoinPoint;
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.aspectj.lang.annotation.Pointcut;
@Aspect //标注这是一个切面类
public class AopLogging {
//配置公共切点
@Pointcut("execution(public * com.mote.controller.UserController.*(..))")
public void pointcut(){}
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
// 获取方法名称
String name = joinPoint.getSignature().getName();
// 获取方法参数
Object[] args = joinPoint.getArgs();
// 实际项目中根据session中的用户信息,保存操作记录到数据库
if (name.equals("add")) {
System.out.println("【用户执行添加操作】");
} else if (name.equals("update")) {
System.out.println("【用户执行更新操作】");
} else {
System.out.println("【用户执行删除操作】");
}
}
@AfterReturning(pointcut="pointcut()",returning="result")
public void afterReturning(JoinPoint joinPoint, Object result) {
// 获取方法名称
String name = joinPoint.getSignature().getName();
// 打印方法执行结果
System.out.println(name + "运行结果:" + result);
}
@AfterThrowing(pointcut="pointcut()",throwing="exception")
public void afterThrowing(JoinPoint joinPoint, Exception exception) {
// 获取方法名称
String name = joinPoint.getSignature().getName();
// 打印异常信息
System.out.println(name + "运行异常信息:" + exception);
}
@After("pointcut()")
public void after(JoinPoint joinPoint) {
System.out.println("最终通知");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
//这里操作相当于前置通知
System.out.println("环绕==前置通知");
Object obj = null;
try {
obj = joinPoint.proceed();
//这里操作相当于后置通知
System.out.println("环绕==后置通知");
} catch (Throwable e) {
//这里操作相当于异常通知
System.out.println("环绕==异常通知");
e.printStackTrace();
}
//这里操作相当于最终通知
System.out.println("环绕==最终通知");
return obj;
}
}
3:注解配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import com.mote.aop.AopLogging;
import com.mote.controller.UserController;
@EnableAspectJAutoProxy //开启Spring支持AspectJ注解
@Configuration
public class MainConfig {
@Bean //将UserController添加到spring容器
public UserController userController(){
return new UserController();
}
@Bean //将切面添加到容器
public AopLogging aopLogging(){
return new AopLogging();
}
}
或者使用xml
<!-- 开启Spring支持AspectJ注解 -->
<aop:aspectj-autoproxy/>
<!-- 配置Bean -->
<bean id="userController" class="com.mote.controller.UserController" />
<bean id="aopLogging" class="com.mote.aop.AopLogging" />
4:测试打印
1:注解配置类对应的测试方法
@Test
public void test() {
// 创建容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
// 从容器中获取bean
UserController userController = context.getBean(UserController.class);
// 调用方法
userController.deleteUser();
}
2:xml配置对应的测试类
@Test
public void testXml() {
// 创建容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"beans.xml");
// 从容器中获取bean
UserController userController = context.getBean(UserController.class);
// 调用方法
userController.deleteUser();
}
注意一:JoinPoint joinPoint在方法的参数列表中,必须放在第一位,否则会报错
注意二:@AfterReturning和@AfterThrowing必须配置对应的returning和throwing参数