Java Web實戰06--Spring之AOP使用

一、AOP簡介

Spring AOP是面向切面編程,主要思想是,將代碼中的與主業務邏輯無關的公共代碼,抽離出來,單獨模塊化爲類即切面,在運行的時候動態的將切面的功能即通知加入到業務執行邏輯中。AOP模塊常用於日誌處理、事務管理、權限驗證、參數驗證等。優點:

–每個事物邏輯位於一個位置, 代碼不分散, 便於維護和升級
–業務模塊更簡潔, 只包含核心業務代碼.

以下是Aop中的主要概念:

•切面(Aspect):  橫切關注點(跨越應用程序多個模塊的功能)被模塊化的特殊對象
•通知(Advice):  切面必須要完成的工作
•目標(Target):被通知的對象
•代理(Proxy): 向目標對象應用通知之後創建的對象
•連接點(Joinpoint):程序執行的某個特定位置:如類某個方法調用前、調用後、方法拋出異常後等。連接點由兩個信息確定:方法表示的程序執行點;相對點表示的方位。
切點(pointcut):每個類都擁有多個連接點:例如 ArithmethicCalculator 的所有方法實際上都是連接點,即連接點是程序類中客觀存在的事務。AOP 通過切點定位到特定的連接點。

AOP的實現原理爲代理模式,一個用代理實現的代碼如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class ArithmeticCalculatorLoggingProxy {
	
	private ArithmeticCalculator target;
	public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
		super();
		this.target = target;
	}
	//返回代理對象
	public ArithmeticCalculator getLoggingProxy(){
		ArithmeticCalculator proxy = null;
		
		ClassLoader loader = target.getClass().getClassLoader();
		Class [] interfaces = new Class[]{ArithmeticCalculator.class};
		InvocationHandler h = new InvocationHandler() {
			/**
			 * proxy: 代理對象。 一般不使用該對象
			 * method: 正在被調用的方法
			 * args: 調用方法傳入的參數
			 */
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				String methodName = method.getName();
				//打印日誌
				System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));				
				//調用目標方法
				Object result = null;				
				try {
					//前置通知
					result = method.invoke(target, args);
					//返回通知, 可以訪問到方法的返回值
				} catch (NullPointerException e) {
					e.printStackTrace();
					//異常通知, 可以訪問到方法出現的異常
				}				
				//後置通知. 因爲方法可以能會出異常, 所以訪問不到方法的返回值			
				//打印日誌
				System.out.println("[after] The method ends with " + result);
				return result;
			}
		};
		
		/**
		 * loader: 代理對象使用的類加載器。 
		 * interfaces: 指定代理對象的類型. 即代理代理對象中可以有哪些方法. 
		 * h: 當具體調用代理對象的方法時, 應該如何進行響應, 實際上就是調用 InvocationHandler 的 invoke 方法
		 */
		proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);
		
		return proxy;
	}
}
在使用的時候需要將原始的類傳遞到此代理類,然後用代理類來實現對業務類的所有操作,在實際使用時是比較麻煩的。

二、Spring AOP實戰

1、建立一個maven工程,在pom.xml中加入以下依賴,其中,aspectjweaver、aopalliance是用來支持aop註解aspect和Before的。

<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-expression</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>4.3.0.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
		<dependency>
			<groupId>aopalliance</groupId>
			<artifactId>aopalliance</artifactId>
			<version>1.0</version>
		</dependency> 

		<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.9</version>
		</dependency>

	</dependencies>


2、實現一個接口move以及實現類,表示向某個方向移動:

package com.yefeng.spring.spring4;

public interface Move {
	public void up(int i);
	public void down(int i);
	public void left(int i);
	public void right(int i);
}

package com.yefeng.spring.spring4;

import org.springframework.stereotype.Component;

@Component
public class MyMove implements Move {

	@Override
	public void up(int i) {
		System.out.println("I'm moving up " + i + " steps!");
	}

	@Override
	public void down(int i) {
		System.out.println("I'm moving down " + i + " steps!");
	}

	@Override
	public void left(int i) {
		System.out.println("I'm moving left " + i + " steps!");
	}

	@Override
	public void right(int i) {
		System.out.println("I'm moving right " + i + " steps!");
	}

}


3、編寫打印日誌類,注意此處在before註解後,需要加入通知的方法,並且括號中的參數一定要和方法的參數一致。例如下中,括號中int不能省略。可以用*表示所有含有一個int參數的方法。

package com.yefeng.spring.spring4;

import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingMove {
	//下面的()中int 不能省略
	//@Before("execution(public void com.yefeng.spring.spring4.Move.up(int))")
	//此處中類名爲接口或者實現類都可以
//	@Before("execution(public void com.yefeng.spring.spring4.MyMove.up(int))")
	@Before("execution(public void com.yefeng.spring.spring4.MyMove.*(int))")
	public void beforeMove(JoinPoint joinPoint){
		String name = joinPoint.getSignature().getName();
		List<Object> args = Arrays.asList(joinPoint.getArgs());
		System.out.println("I'm ready to move " + name +" " + args);
	}
}


4、在spring bean配置文件中加入自動掃面bean以及aop動態代理支持,注意需要加入命名空間context,aop:

<?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="com.yefeng.spring.spring4"></context:component-scan>

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

5、測試app類:

package com.yefeng.spring.spring4;

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

/**
 * @author yefengzhichen
 * 2016年7月5日
 */
public class App 
{
    public static void main( String[] args )
    {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationConetsx.xml");
        Move move = (Move) ctx.getBean("myMove");
        move.up(3);      
        move.down(2);
    }
}


6、執行結果:

I'm ready to move up [3]
I'm moving up 3 steps!
I'm ready to move down [2]
I'm moving down 2 steps!











發佈了118 篇原創文章 · 獲贊 60 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章