詳述Spring AOP的使用及類型

目錄

一、爲什麼要用aop 

 二、aop的使用

​ 三、aop的幾種類型

1.@Before

2.@After

3.@AfterReturning

4.@AfterThrowing

5.@Around


一、爲什麼要用aop 

首先來描述一個問題:

在項目的開發中經常會出現代碼冗餘的問題,就像下面的代碼,ComputerService實現類中add()方法和div()方法都要執行相同的輸出語句,aop就是用來解決這個問題。

package club.affengkuang.computer.service;

/**
 * 服務層計算器接口
 *
 * @author 
 */
public interface IComputerService {

	int add(int a,int b);
	
	int div(int a,int b);
}



package club.affengkuang.computer.service;

import org.springframework.stereotype.Service;

/**
 * 服務層計算器接口實現類
 *
 * @author 
 */
@Service
public class ComputerService implements IComputerService{

	public int add(int a, int b) {
		System.out.println(this.getClass().getName()+":The add method begins.");
		System.out.println(this.getClass().getName()+":Parameters of the add method: ["+a+","+b+"]");
		int result = a+b;
		return result;
	}

	public int div(int a, int b) {
		System.out.println(this.getClass().getName()+":The add method begins.");
		System.out.println(this.getClass().getName()+":Parameters of the add method: ["+a+","+b+"]");
		int result = a/b;
		return result;
	}
}

 二、aop的使用

首先在application.xml文件添加如下兩個標籤,其中context:component-scan標籤的作用見博客:詳述context:component-scan作用

而第十二行的標籤作用是將@Aspect註解修飾的類變成一個代理類,生成的這個代理類便可解決上面所說的代碼冗餘的問題。

<?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: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.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<context:component-scan base-package="club.affengkuang"></context:component-scan>
	
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

然後再來創建如下ComputerAOP類,該類就是上面說的代理類:

第九行:用@Aspect註解修飾,使該類能被aop:aspectj-autoproxy標籤獲取

第十行:用@Component註解修飾,使該類被context:component-scan標籤掃描到IOC容器中

第十四行:@Before註解修飾,其中的execution表達式指定了一個目標類"club.affengkuang.computer.service.ComputerService",而aop:aspectj-autoproxy標籤可以獲取該表達式指定的目標類,並將ComputerAOP類變成該目標類的代理類;而在目標類中還指定了返回值爲int的public方法,表示before()方法將會在目標方法之前執行。

第十六行:用getArgs()方法可獲取目標方法中傳入的參數,並形成一個Object類數組

第十七、十八行:獲取所調用的方法的方法名(即:add、div)

package club.affengkuang.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ComputerAOP {

	//在目標方法執行之前執行
	@Before("execution(public int club.affengkuang.computer.service.ComputerService.*(..))")
	public void before(JoinPoint jp) {
		Object [] args = jp.getArgs();
		Signature signature =jp.getSignature();
		String name = signature.getName();
		System.out.println(this.getClass().getName()+":The "+name+" method begins.");
		System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
	}
}

有了代理類,在add()和div()方法中的兩句輸出語句就可以省略了,在測試類中創建ComputerService類對象時,IOC中會自動先創建一個ComputerAOP代理類對象,它們會在add()和div()方法被調用時,先調用before()方法

在如下測試類中將computerService對象打印,就會發現創建的並不是ComputerService類對象而是代理類對象:

package club.affengkuang.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import club.affengkuang.computer.service.IComputerService;

public class Test {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
		IComputerService computerService = applicationContext.getBean(IComputerService.class);
		System.out.println(computerService.getClass().getName());
		int result = computerService.div(1,2);
		System.out.println(result);
		applicationContext.close();
	}
}

 三、aop的幾種類型

1.@Before

第16-25行:就是上面演示的before()方法,作用是在目標方法被調用時,在其之前執行。

2.@After

第27-33行:顧名思義,該方法會在目標方法被調用結束後執行

注意:該方法無論目標方法是否出現異常,都會執行

3.@AfterReturning

第35-41行:該方法會在目標方法返回結果後執行。

該方法與上一個@After的區別就是,如果目標方法出現異常, 則該方法無法執行

其中,returning=“result”獲取的就是目標方法的返回值

4.@AfterThrowing

第43-46行:該方法會在目標方法拋出異常時執行

其中,e就是目標方法拋出的異常

package club.affengkuang.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ComputerAOP {

	        //在目標方法執行之前執行
		@Before("execution(public int club.affengkuang.computer.service.ComputerService.*(..))")
		public void before(JoinPoint jp) {
			Object [] args = jp.getArgs();
			
			Signature signature =jp.getSignature();
			String name = signature.getName();
			System.out.println(this.getClass().getName()+":The "+name+" method begins.");
			System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
		}
		
		//目標方法執行完;無論目標方法是否出現異常,都會執行
		@After("execution(public int club.affengkuang.computer.service.ComputerService.*(..))")
		public void after(JoinPoint jp) {
			Signature signature = jp.getSignature();
			String name = signature.getName();
			System.out.println(this.getClass().getName()+":The "+name+" method ends.");
		}
		
		//目標方法返回結果時執行;如果目標方法出現異常, 則無法執行
		@AfterReturning(value = "execution(public int club.affengkuang.computer.service.ComputerService.*(..))",returning="result")
		public void afterReturning(JoinPoint jp,Object result) {
			Signature signature = jp.getSignature();
			String name = signature.getName();
			System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
		}
		
		@AfterThrowing(value = "execution(public int club.affengkuang.computer.service.ComputerService.*(..))",throwing="e")
		public void afterThrowing(JoinPoint jp, Exception e) {
			System.out.println(e.getMessage());
		}
}

5.@Around

該類型的方法與前幾個不太一樣,它一個方法可以代替上面所有方法:

第15-17行:和前面的代碼一樣,獲取目標方法的參數列表及方法名

第18行:getTarget()可獲取目標類創建的對象

第25行:pjp.proceed()方法的作用是調用目標方法,這也是@Around方法的特色之處,有了它,想在目標方法被調用之前執行的代碼就寫在該行之前,想在目標方法被調用之後執行的代碼就寫在該行之後,想在目標方法拋出異常時執行的代碼,就寫在try-catch中

package club.affengkuang.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ComputerAOP {

	@Around(value = "execution(public int club.affengkuang.computer.service.ComputerService.*(..))")
	public Object around(ProceedingJoinPoint pjp) {
		Object [] args = pjp.getArgs();//傳入目標方法的參數
		Signature signature = pjp.getSignature();
		String name = signature.getName();
		Object object = pjp.getTarget();//目標類創建的對象
		System.out.println("#################"+object.getClass().getName());
		try {
			Object result=null;
			try {
				System.out.println(this.getClass().getName()+":The "+name+" method begins.");
				System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
				result = pjp.proceed();//調用目標方法,並且返回目標方法的結果
			} finally {
				System.out.println(this.getClass().getName()+":The "+name+" method ends.");
			}
			System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
			return result;
		} catch (Throwable e) {
			System.out.println(e.getMessage());
		}
		
		return -1;
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章