切面編程(AOP)是基於動態代理的一種編程,它是把不同對象的共同功能摘出來,通過切點觸發的形式進行調用,切點可以是對象,可以是註解等。通過切面編程,可以降低代碼的耦合性。
例如,日誌的打印,銀行系統中,所有進行了存取款的操作,需要進行日誌的打印。
寫一個例子
在pom.xml中添加依賴
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectj-tools</artifactId>
<version>1.0.6</version>
</dependency>
創建一個接口
package com.test.sprintboot;
public interface Flyable {
void fly();
}
寫一個自定義註解,切點通過註解的方式進行攔截
package com.test.sprintboot.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = {ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
String value();
}
創建一個接口的實現類
package com.test.sprintboot.services;
import org.springframework.stereotype.Component;
import com.test.sprintboot.Flyable;
import com.test.sprintboot.annotation.Value;
@Component
public class Parrot implements Flyable {
@Override
@Value(value="18")
public void fly() {
System.out.println("鸚鵡在飛翔...");
}
}
切面
package com.test.sprintboot;
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import com.test.sprintboot.annotation.Value;
@Aspect
@Component
public class AspectDemo {
public AspectDemo(){
}
@Pointcut("@annotation(com.test.sprintboot.annotation.Value)")//匹配方法上有對應註釋
public void targetValue(){
}
@Around("targetValue()")
public void aroundTarget(ProceedingJoinPoint joinPoint) throws Throwable{
Class<?> aclass = joinPoint.getTarget().getClass();
String name = joinPoint.getSignature().getName();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Class[] parameterType = methodSignature.getParameterTypes();
Method method = aclass.getMethod(name, parameterType);
joinPoint.proceed();
}
}
調用方法
package com.test.sprintboot;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.test.sprintboot.services.Eagle;
import com.test.sprintboot.services.Parrot;
import com.test.sprintboot.services.Prox;
/**
* Hello world!
*
*/
@RestController
public class App
{
@RequestMapping("/parrot")
public void parrot(){
ApplicationContext contxt = new ClassPathXmlApplicationContext("applicationContext.xml");
Parrot parrot = contxt.getBean(Parrot.class);
parrot.fly();
}
}
自此,一個簡單的通過註解進行攔截的切面的代碼就寫好了,通過控制檯調用localhost:8080/parrot即可進行調用(端口根據自己springboot的端口進行修改)
1、“within(com.elim.spring.aop.service.UserServiceImpl)”匹配UserServiceImpl類對應對象的所有方法外部調用,而且這個對象只能是UserServiceImpl類型,不能是其子類型。
2、“within(com.elim..*)”匹配com.elim包及其子包下面所有的類的所有方法的外部調用。
1、“@within(com.elim.spring.support.MyAnnotation)”匹配被調用的方法聲明的類上擁有MyAnnotation註解的情況。比如有一個ClassA上使用了註解MyAnnotation標註,並且定義了一個方法a(),那麼在調用ClassA.a()方法時將匹配該Pointcut;如果有一個ClassB上沒有MyAnnotation註解,但是它繼承自ClassA,同時它上面定義了一個方法b(),那麼在調用ClassB().b()方法時不會匹配該Pointcut,但是在調用ClassB().a()時將匹配該方法調用,因爲a()是定義在父類型ClassA上的,且ClassA上使用了MyAnnotation註解。但是如果子類ClassB覆寫了父類ClassA的a()方法,則調用ClassB.a()方法時也不匹配該Pointcut。
1、“execution(* add())”匹配所有的不帶參數的add()方法。
2、“execution(public * com.elim..*.add*(..))”匹配所有com.elim包及其子包下所有類的以add開頭的所有public方法。
3、“execution(* *(..) throws Exception)”匹配所有拋出Exception的方法。
1、“target(com.elim.spring.aop.service.IUserService)”則匹配所有被代理的目標對象能夠轉換爲IUserService類型的所有方法的外部調用。
1、“@target(com.elim.spring.support.MyAnnotation)”匹配被代理的目標對象對應的類型上擁有MyAnnotation註解時。
args用來匹配方法參數的。
1、“args()”匹配任何不帶參數的方法。
2、“args(java.lang.String)”匹配任何只帶一個參數,而且這個參數的類型是String的方法。
3、“args(..)”帶任意參數的方法。
4、“args(java.lang.String,..)”匹配帶任意個參數,但是第一個參數的類型是String的方法。
5、“args(..,java.lang.String)”匹配帶任意個參數,但是最後一個參數的類型是String的方法。
1、“@args(com.elim.spring.support.MyAnnotation)”匹配方法參數類型上擁有MyAnnotation註解的方法調用。如我們有一個方法add(MyParam param)接收一個MyParam類型的參數,而MyParam這個類是擁有註解MyAnnotation的,則它可以被Pointcut表達式“@args(com.elim.spring.support.MyAnnotation)”匹配上。
1、“@annotation(com.elim.spring.support.MyAnnotation)”匹配所有的方法上擁有MyAnnotation註解的方法外部調用。
bean用於匹配當調用的是指定的Spring的某個bean的方法時。
1、“bean(abc)”匹配Spring Bean容器中id或name爲abc的bean的方法調用。
2、“bean(user*)”匹配所有id或name爲以user開頭的bean的方法調用。
表達式組合使用
1、“bean(userService) && args()”匹配id或name爲userService的bean的所有無參方法。
2、“bean(userService) || @annotation(MyAnnotation)”匹配id或name爲userService的bean的方法調用,或者是方法上使用了MyAnnotation註解的方法調用。
3、“bean(userService) && !args()”匹配id或name爲userService的bean的所有有參方法調用。