基於Spring註解@AspectJ的AOP
新增依賴
<!-- aspectj 方式的AOP,需要加入下面三個包 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjtools</artifactId> <version>1.8.9</version> </dependency>
XML啟用
Spring上下文建立一個AnnotationAwareAspectJAutoProxyCreator類
<aop:aspectj-autoproxy/>
切入點宣告
@AspectJ風格的命名切入點使用org.aspectj.lang.annotation包下的@Pointcut方法(方法必須是返回void型別)實現。
@Pointcut(value="切入點表示式", argNames = "引數名列表")
使用註解
@Pointcut("@annotation(com.apus.dap.hella.common.aspect.annotation.JmxMetrical) || @target(com.apus.dap.hella.common.aspect.annotation.JmxMetrical)") public void annotationProcessor() { }
註解類@JmxMetrical
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface JmxMetrical {
String name() default "";
boolean displayArgs() default false;
/**
* 增刪改的資料的型別
*/
Class<?> clazz();
}
通知宣告
- 前置通知
使用org.aspectj.lang.annotation 包下的@Before註解宣告;
@Before(value = "切入點表示式或命名切入點", argNames = "引數列表引數名")
-
環繞通知
像Spring基於XML的AOP一樣,@AspectJ註解的使用不僅只限定與定義前置和後置通知型別。我們還可以建立環繞通知,使用環繞通知需要使用@Around。
@Around (
value="切入點表示式或命名切入點",
argNames="引數列表引數名")
使用org.aspectj.lang.annotation 包下的@AfterReturning註解宣告;
@AfterReturning(
value="切入點表示式或命名切入點",
pointcut="切入點表示式或命名切入點",
argNames="引數列表引數名",
returning="返回值對應引數名")
使用org.aspectj.lang.annotation 包下的@After註解宣告;
@After (
value="切入點表示式或命名切入點",
argNames="引數列表引數名")
使用org.aspectj.lang.annotation 包下的@AfterThrowing註解宣告
@AfterThrowing (
value="切入點表示式或命名切入點",
pointcut="切入點表示式或命名切入點",
argNames="引數列表引數名",
throwing="異常對應引數名")
@Aspect
@Component
public class JmxMetricalAOP {
private static final Logger LOG = LoggerFactory.getLogger(JmxMetricalAOP.class);
private static MetricalCounterManager metricalCounterManager;
private static final String POINTCUT_METHOD =
"(execution(public * " + API_PROTOCOL_CLASS_NAME + ".*(..)))";
@PostConstruct
public void init() {
//初始化一個全域性度量器管理類
metricalCounterManager = new MetricalCounterManager(Maps.newHashMap());
LOG.info("JmxMetricalAOP.methodAnnotated init:success");
}
@Pointcut("@annotation(com.apus.dap.hella.common.aspect.annotation.JmxMetrical) || @target(com.apus.dap.hella.common.aspect.annotation.JmxMetrical)")
public void annotationProcessor() {
}
@Pointcut(POINTCUT_METHOD)
public void publicMethod() {
LOG.info("JmxMetricalAOP.methodJoinPointed");
}
@Around("publicMethod() && annotationProcessor()")
public Object timedJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
Signature signature = joinPoint.getSignature();
Method method = ((MethodSignature) signature).getMethod();
final String methodName = signature.getName();
/**
* 1.獲取唯一介面全路徑
*/
String fullApiName = getFullApiName(joinPoint, signature, methodName);
if (method.getDeclaringClass().isInterface()) {
method = joinPoint.getTarget().getClass()
.getDeclaredMethod(methodName, method.getParameterTypes());
}
/**
* 2. 方法上的註解優先順序比類上的註解高,可以覆蓋類上註解的值,獲取相關值
*/
//是否註解
Boolean isJmxMetrical = false;
//註解物件名
String jmxMetricalName = null;
//註解物件引數
Object[] jmxMetricalArgs = null;
//註解物件class
Class<? extends JmxMetrical> aClass = null;
JmxMetrical jmxMetrical = null;
//處理方法上的註解
if (method.isAnnotationPresent(JmxMetrical.class)) {
isJmxMetrical = true;
jmxMetrical = method.getAnnotation(JmxMetrical.class);
jmxMetricalName = jmxMetrical.name();
aClass = jmxMetrical.getClass();
if (jmxMetrical.displayArgs()) {
jmxMetricalArgs = joinPoint.getArgs();
}
} else {
//處理類上面的註解
Object target = joinPoint.getTarget();
if (target.getClass().isAnnotationPresent(JmxMetrical.class)) {
isJmxMetrical = true;
jmxMetrical = target.getClass().getAnnotation(JmxMetrical.class);
jmxMetricalName = jmxMetrical.name();
aClass = jmxMetrical.getClass();
if (jmxMetrical.displayArgs()) {
jmxMetricalArgs = joinPoint.getArgs();
}
}
}
/**
* 3. 切入點度量
*/
//切入點方法執行結果
Object result = null;
Boolean thrown = false;
try {
result = joinPoint.proceed();
} catch (Throwable throwable) {
thrown = true;
//如果需要度量
}
//如果不需要度量
if (!isJmxMetrical) {
return result;
}
metricalCounterManager.metricApi(fullApiName, result, thrown);
return result;
}
private String getFullApiName(ProceedingJoinPoint joinPoint, Signature signature,
String methodName) {
//通過包路徑方式獲取
String[] packageName = signature.getDeclaringTypeName().split("\\.");
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < packageName.length; ++i) {
if (i < packageName.length - 1) {
stringBuilder.append(packageName[i].substring(0, 1));
} else {
stringBuilder.append(packageName[i]);
}
if (i == packageName.length - 1) {
stringBuilder.append(FULL_POINT_SEP);
continue;
}
stringBuilder.append(".");
}
String fullApiName = stringBuilder + signature.getName();
LOG.info("Executing: " + fullApiName);
//通過介面方式獲取
// String className = joinPoint.getTarget().getClass().getInterfaces()[0].getName();
// String fullApiName = className + SEP + methodName;
return fullApiName;
}
/**
* 後置異常通知
*/
@AfterThrowing("publicMethod() && annotationProcessor()")
public void demandRefund() {
LOG.info("com.apus.hella.protocol.dto.*#{} exception!!!");
}
public static List<MetricalCounter> getMonitorList() {
List<MetricalCounter> monitor_profiles = getMonitorMap().values().stream()
.collect(Collectors.toList());
return monitor_profiles;
}