AOP:面向切面編程,相對於OOP面向對象編程 Spring的AOP的存在目的是爲了解耦。AOP可讓一組類共享相同的行爲。在OOP中只能繼承和實現接口,且類繼承只能單繼承,阻礙更多行爲添加到一組類上,AOP彌補了OOP的不足。html
還有就是爲了清晰的邏輯,讓業務邏輯關注業務自己,不用去關心其它的事情,好比事務。java
Spring的AOP是經過JDK的動態代理和CGLIB實現的。git
aop 有一堆術語,很是難以理解,簡單說一下github
通知(有的地方叫加強)(Advice)web
須要完成的工做叫作通知,就是你寫的業務邏輯中須要好比事務、日誌等先定義好,而後須要的地方再去用spring
鏈接點(Join point)編程
就是spring中容許使用通知的地方,基本上每一個方法先後拋異常時均可以是鏈接點springboot
切點(Poincut)bash
其實就是篩選出的鏈接點,一個類中的全部方法都是鏈接點,但又不全須要,會篩選出某些做爲鏈接點作爲切點。若是說通知定義了切面的動做或者執行時機的話,切點則定義了執行的地點app
切面(Aspect)
其實就是通知和切點的結合,通知和切點共同定義了切面的所有內容,它是幹什麼的,何時在哪執行
引入(Introduction)
在不改變一個現有類代碼的狀況下,爲該類添加屬性和方法,能夠在無需修改現有類的前提下,讓它們具備新的行爲和狀態。其實就是把切面(也就是新方法屬性:通知定義的)用到目標類中去
目標(target)
被通知的對象。也就是須要加入額外代碼的對象,也就是真正的業務邏輯被組織織入切面。
織入(Weaving)
把切面加入程序代碼的過程。切面在指定的鏈接點被織入到目標對象中,在目標對象的生命週期裏有多個點能夠進行織入:
例:
public class UserService{
void save(){}
List list(){}
....
}
複製代碼
在UserService中的save()方法前須要開啓事務,在方法後關閉事務,在拋異常時回滾事務。
那麼,UserService中的全部方法都是鏈接點(JoinPoint),save()方法就是切點(Poincut)。須要在save()方法先後執行的方法就是通知(Advice),切點和通知合起來就是一個切面(Aspect)。save()方法就是目標(target)。把想要執行的代碼動態的加入到save()方法先後就是織入(Weaving)。
有的地方把通知稱做加強是有道理的,在業務方法先後加上其它方法,其實就是對該方法的加強。
around > before > around > after > afterReturning
@Aspect
@Component
public class IntroductionAop {
@DeclareParents(value = "com.jiuxian..service..*", defaultImpl = DoSthServiceImpl.class)
public DoSthService doSthService;
}
複製代碼
public interface DoSthService {
void doSth();
}
@Service
public class DoSthServiceImpl implements DoSthService {
@Override
public void doSth() {
System.out.println("do sth ....");
}
}
public interface UserService {
void testIntroduction();
}
@Service
public class UserServiceImpl implements UserService {
@Override
public void testIntroduction() {
System.out.println("do testIntroduction");
}
}
複製代碼
@Test
public void testIntroduction() {
userService.testIntroduction();
//Aop 讓UserService方法擁有 DoSthService的方法
DoSthService doSthService = (DoSthService) userService;
doSthService.doSth();
}
複製代碼
do testIntroduction
do sth ....
複製代碼
(1) 對方法
@Aspect
@Component
public class TransactionAop {
@Pointcut("execution(* com.jiuxian..service.*.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void beginTransaction() {
System.out.println("before beginTransaction");
}
@After("pointcut()")
public void commit() {
System.out.println("after commit");
}
@AfterReturning("pointcut()", returning = "returnObject")
public void afterReturning(JoinPoint joinPoint, Object returnObject) {
System.out.println("afterReturning");
}
@AfterThrowing("pointcut()")
public void afterThrowing() {
System.out.println("afterThrowing afterThrowing rollback");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
System.out.println("around");
return joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
throw e;
} finally {
System.out.println("around");
}
}
}
複製代碼
(2) 對註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
String value() default "";
}
複製代碼
@Aspect
@Component
public class AnnotationAop {
@Pointcut(value = "@annotation(log)", argNames = "log")
public void pointcut(Log log) {
}
@Around(value = "pointcut(log)", argNames = "joinPoint,log")
public Object around(ProceedingJoinPoint joinPoint, Log log) throws Throwable {
try {
System.out.println(log.value());
System.out.println("around");
return joinPoint.proceed();
} catch (Throwable throwable) {
throw throwable;
} finally {
System.out.println("around");
}
}
}
@Before("@annotation(com.jiuxian.annotation.Log)")
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Log log = method.getAnnotation(Log.class);
System.out.println("註解式攔截 " + log.value());
}
複製代碼
public interface UserService {
String save(String user);
void testAnnotationAop();
}
@Service
public class UserServiceImpl implements UserService {
@Override
public String save(String user) {
System.out.println("保存用戶信息");
if ("a".equals(user)) {
throw new RuntimeException();
}
return user;
}
@Log(value = "test")
@Override
public void testAnnotationAop() {
System.out.println("testAnnotationAop");
}
}
複製代碼
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootAopApplicationTests {
@Resource
private UserService userService;
@Test
public void testAop1() {
userService.save("張三");
Assert.assertTrue(true);
}
@Test
public void testAop2() {
userService.save("a");
}
@Test
public void testAop3() {
userService.testAnnotationAop();
}
}
複製代碼
around
before beginTransaction
保存用戶信息
around
after commit
afterReturning :: 張三
複製代碼
around
before beginTransaction
保存用戶信息
around
after commit
afterThrowing rollback
複製代碼
test
around
testAnnotationAop
around
複製代碼
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
複製代碼
例: execution(* com.jiuxian..service.*.*(..))
例: execution(* com.jiuxian..service.*Service.add*(String))
表示: com.jiuxian 包及其子包下的service包下,類名以Service結尾,方法以add開頭,參數類型爲String的方法的切點。
@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
@Pointcut("within(com.xyz.someapp.trading..*)")
private void inTrading() {}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
複製代碼
可使用 &&, ||, ! 運算符來定義切點
以上代碼基於Springboot 2.0