1. 程式人生 > >Spring AOP的一些總結

Spring AOP的一些總結

前言

其實從開始使用spring開始,對spring中的aop一直就有了解,但是每次使用過後都遺忘了。導致再次使用的時候,又得重新翻找資料,重新開始寫demo,今天來個總結,搞定aop的基本例項。真正從原始碼基本進行學習還得等一段時間。

一些概念

網上已經有很多部落格針對aop的一些概念有介紹,自己也能理解一二。從實用的角度來說,概念的介紹並不是這篇部落格的重點,針對這些概念的介紹可以參考以下部落格,個人覺得講解的比較通俗的是這一篇:aop概念通俗版,講解的稍微全面一點的是這一篇:aop相關概念

簡單例項

這裡還是弄一個簡單例項吧。針對一些細節後續會進行總結

先建立一個maven專案,pom檔案如下:

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.learn</groupId>
	<artifactId>spring_aop</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<url>http://maven.apache.org</url>

	<properties>
		<spring.version>3.1.1.RELEASE</spring.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.6.12</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.6.12</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2</version>
		</dependency>
	</dependencies>
</project>

在spring的配置檔案中加入如下配置:

<aop:aspectj-autoproxy />
<context:component-scan base-package="com.learn" />

編寫一個簡單的業務類

package com.learn.service;

import org.springframework.stereotype.Service;

@Service
public class PersonService {

	public void addPerson(String personName) {
		System.out.println("add person");
	}
	
	public boolean deletePerson(String personName) {
		System.out.println("delete person");
		return true;
	}
	
	public void editPerson(String personName) {
		System.out.println("edit person "+personName);
		throw new RuntimeException("edit person infomation error!");
	}
	
}

編寫切面類

package com.learn.aspect;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
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.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class SimpleAspect {
	
	@Pointcut("execution(* com.learn.service.*Service*.*(..))")
	public void pointCut() {
		
	}
	
	@Pointcut("execution(* com.learn.service.*Service*.edit*(..))")
	public void pointCutException() {
	}
	
	@After("pointCut()")
	public void after(JoinPoint joinPoint) {
		System.out.println("after aspect executed");
	}
	
	@Before("pointCut()")
	public void before(JoinPoint joinPoint) {
		System.out.println("before aspect executing");
	}
	
	@AfterReturning(pointcut="pointCut()",returning = "returnVal")
	public void afterReturning(JoinPoint joinPoint ,Object returnVal) {
		
		System.out.println("afterReturning executed,return result is "+returnVal);
	}

	@Around("pointCut()")
	public void around(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("around start..");
		try {
			pjp.proceed();
		} catch (Throwable ex) {
			System.out.println("error in around");
			throw ex;
		}
		System.out.println("around end");
	}
	
	@SuppressWarnings("rawtypes")
	@AfterThrowing(pointcut="pointCutException()",throwing="error")
	public void afterThrowing(JoinPoint jp,Throwable error) {
		Object[] args = jp.getArgs();
		System.out.println("目標函式中的引數資訊:");
		for(Object o:args) {
			System.out.println(o.toString());
		}
		Object target = jp.getTarget();
		System.out.println("目標函式中的target資訊:"+target.toString());
		Class class1 = jp.getClass();
		
		String kind = jp.getKind();
		System.out.println("切面的型別:"+kind);
		System.out.println("目標函式的類資訊:"+class1.getName());
		System.out.println("切面捕獲異常資訊");
		System.out.println("error:"+error);
	}
}

切面類中的註解需要注意一下,@Component是將其交給spring管理,@Aspect是將其標記為切面,其實還有xml配置的方式實現,這裡就不做詳細探討了,XML配置更加簡單。

測試方法

package com.learn.func;

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

import com.learn.service.PersonService;

public class App {
	public static void main(String[] args) {
		try {
			ApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:appContext.xml");
	    	PersonService personService = appContext.getBean(PersonService.class);
	    	String personName = "liman";
	    	personService.addPerson(personName);
	    	personService.deletePerson(personName);
	    	personService.editPerson(personName);
		} catch (Exception e) {
			System.out.println("異常資訊:"+e.getMessage());
		}
	}
}

程式執行結果


補充

spring aop中重要的就是切入點表示式,切入點表示式是聯絡目標類方法和切面增強方法的橋樑。因此需要針對切入點表示式進行一些補充

execution(方法修飾符    方法返回值    方法所屬類    匹配方法名    (方法的引數列表)    方法丟擲的異常)

其中紅色字型的不能省略,每個部分都支援萬用字元('*')來匹配

方法修飾符:public private protected。

引數列表中:'*'——表示一個任意型別的引數,".."——表示零個或多個任意型別的引數

(*,Integer)匹配一個接受兩個引數的方法,第一個引數可以為任意型別,第二個必須為Integer型別。

execution只是切入點表示式的一種,還有within,this,target,args。

針對within,this,target和args的介紹,我建議還是參考官網文件吧,國內有些文件將這幾種切入點表示式翻譯的很怪。

如果出現一個增強類需要切入到多個目標類,可以進行切入點表示式的組合,||,&&等邏輯語法在切入點表示式中依舊適用

針對切面類中需要獲取目標方法的相關資訊,一般是通過JoinPoint這個引數來獲取,同時在切面類中,為了呼叫目標類中的相關方法,可能需要用到反射。

最後說一句:這次的使用是基於在新網實習中,涉及到異常日誌資訊處理的問題來實現的,當時腦子裡首先反應的就是利用AOP,經歷過各種苛刻碰碰,勉強算是完成了功能,在後面迭代中進一步完善