Spring Aop的简单使用

作为spring框架的核心功能,功能强大的AOP,你值得拥有。

SpringAop介绍

AOP意为面向切面编程,指的是在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
用大白话解释就是把程序中重复的代码抽取出来,在需要执行的时候,通过动态代理方式实现,这样就不需要修改原有代码了。

SpringAop作用

  • 解决代码重复性
  • 提高开发效率
  • 方便维护

SpringAop的术语

术语 说明
Joinpoint (连接点) 指的是类里面所有可代理的方法
Pointcut (切入点) 指的是需要代理拦截的方法
Advice (通知增强) 指的是增强代码,具备4种通知类型,下面会做说明
Introduction (引介) 指的是在不修改代码的前提下,可以动态地添加一些方法或Field
Weaving (织入) 指的是增强代码运行的过程就叫织入
Aspect (切面) 指的是切入点和增强代码的结合,即被 @Aspect 注解的类


Advice通知增强类型

类型 说明
前置增强(Before) 方法执行前调用
后置增强(After) 方法执行后调用
环绕增强(Around) 此方法可以在方法执行前后执行,且可以控制方法是否继续执行
返回增强(AfterReturning) 适用于有返回值的情况,可以接收返回结果
抛出增强(AfterThrowing) 当方法抛出异常的时候执行,可以接收方法的异常但不处理

代码实现

演示采用注解形式,注解大法好

使用场景:小明出来工作,每天都吃方便面,但是生日当天,小明想吃无敌海鲜麻辣烫

maven依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>

接口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.wwk.springAop.aop;

/**
* 吃饭接口,代理类
*/
public interface EatService {

/**
* 开始吃饭
* dishName 菜名
* @throws Exception
*/
String eat(String dishName) throws Exception;

}

实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.wwk.springAop.aop;

import javax.xml.bind.ValidationException;


public class EatServiceImpl implements EatService {


@Override
public String eat(String dishName) throws Exception {

if ("含笑百步颠".equals(dishName)) {
throw new ValidationException("不好,这饭有毒!");
}

System.out.println("小明今天吃:" + dishName);

return "好吃";
}

}

aop切面类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package com.wwk.springAop.aop;

import java.text.SimpleDateFormat;
import java.util.Date;

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 EatAspect{

//生日
private static String BIRTHDAY = "01-16";

/**
* 切入点
*/
@Pointcut(value = "execution(* com.wwk.springAop.aop.EatService.eat(..))")
public void toPointcut(){

}

/**
* 环绕增强,可以作为方法的切入点
* 此方法可以在方法执行前后执行,且可以控制方法是否继续执行
* @param joinPoint
* @return
* @throws Throwable
*/
@Around(value = "toPointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕增强:准备吃饭");

// System.out.println("方法类:"+joinPoint.getSignature().getDeclaringTypeName());
// System.out.println("方法名:"+joinPoint.getSignature().getName());

//获取参数值
Object[] values = joinPoint.getArgs();
String dishName = values[0].toString();

//判断日期,是否生日,是则开始加菜
Date currentDate = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd");

if (dateFormat.format(currentDate).equals(BIRTHDAY)) {
dishName = "无敌海鲜麻辣烫";
}

//重新赋值
values[0] = dishName;

//重新封装方法
Object rtProceed = joinPoint.proceed(values);

//继续调用方法,若不返回,则方法会停止执行
return rtProceed;
}

/**
* 前置增强,方法执行前调用
*/
@Before(value = "toPointcut()")
public void doBefore() {

System.out.println("前置增强:吃饭前洗手");
}

/**
* 后置增强,方法执行后调用
*/
@After(value = "toPointcut()")
public void doAfter() {
System.out.println("后置增强:吃完饭洗碗");
}

/**
* 最终增强,适用于有返回值的情况
* @param rtVal 返回值
*/
@AfterReturning(value = "toPointcut()", returning = "rtVal" )
public void doReturning(Object rtVal) {
System.out.println("最终增强:服务评分:"+rtVal);
}

/**
* 方法异常抛出的增强,可以接收方法的异常但不处理
* @param e 接收的异常
*/
@AfterThrowing(value = "toPointcut()", throwing = "e")
public void doThrowing(Exception e) {
System.out.println("异常增强:" + e.getMessage());
}

}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.wwk.springAop.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class EatAspectTest {


@SuppressWarnings("resource")
public static void main(String[] args) throws Exception {
//获取IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

EatService eatService = (EatService)context.getBean("eatService");
eatService.eat("方便面");
//eatService.eat("含笑百步颠");
}
}

配置xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"
default-lazy-init="true">

<!-- 开启自动代理,默认使用JDK基于接口的代理,不是接口话则采用CGLIB -->
<aop:aspectj-autoproxy />

<bean id="eatService" class="com.wwk.springAop.aop.EatServiceImpl"></bean>

<bean id="eatAspect" class="com.wwk.springAop.aop.EatAspect"></bean>

</beans>