1. 程式人生 > >spring---Aop切面程式設計

spring---Aop切面程式設計

一、學習aop之前先了解一些Aop的相關術語:

1、通知(Advice):定義切面是什麼以及何時使用。描述了切面要完成的工作以及何時需要執行這個工作。

2、連線點(JoinPoint):程式能夠應用通知的一個時機,這些時機就是連線點,如方法被呼叫時、異常被丟擲時等

3、切入點(Pointcut):就是帶有通知的連線點,在程式中主要體現為書寫切入點表示式

4、切面(Aspect):通常是一個類,裡面可以定義切入點和通知

5、引入(Introduction)

6、目標(Target)

7、代理(Proxy)

8、織入(Weaving)

二、spring提供了4種實現AOP的方式

1、幾點的基於代理的AOP

2、@AspectJ註解驅動的切面

3、純Pojo切面

4、注入式AspectJ切面

三、spring支援5種類型的通知

1、@Before:前置通知,org.springframework.aop.MethodBeforeAdvice

前置通知在目標方法被呼叫之前做增強處理,@Before只需要指定切入點表示式即可。

2、@After:後置通知,

在目標方法完成之後做增強處理,@After可以指定切入點表示式即可

3、@After-returning:返回通知,org.springframework.aop.AfterReturningAdvice

在目標方法正常完成後做增強處理,如果出現異常則不做增強處理。

@After-returning除了指定切入點表示式後,還可以指定一個返回值行參名returning,它代表目標方法的返回值

4、@After-throwing:異常通知,org.springframework.aop.ThrowsAdvice

主要用於處理程式中未處理的異常,@After-throwing處理指定切入點表達後,還可以指定一個返回值形參名throwing,可以通過該形參名來訪問目標方法中所丟擲的異常物件。

5、@Arround:環繞通知,org.aopaliance.intercept.MethodInterceptor

在目標方法完成前後做增強處理,環繞通知是最重要的通知型別,像事務、日誌等都是環繞通知。

注意,該通知型別的核心是一個ProceedingJoinPoint

四、aop的實現步驟

1、建立通知:實現這幾個介面,把其中的方法實現了

2、定義切點和通知者:在spring配置檔案中配置這些資訊

3、使用ProxyFactoryBean來生成代理

五、aop示例

步驟1:新建maven工程springDemo,在pom.xml中匯入需要的jar包如下:

<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.cn.springDemo</groupId>
	<artifactId>springDemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<build />
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>4.3.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>4.3.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
		</dependency>

		<!-- 面向切面必備的兩個jar包 -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.8.9</version>
		</dependency>
		<dependency>
			<groupId>aopalliance</groupId>
			<artifactId>aopalliance</artifactId>
			<version>1.0</version>
		</dependency>

		<!-- 資料來源 -->
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.41</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>4.3.3.RELEASE</version>
		</dependency>



	</dependencies>
</project>

步驟2:在src/main/java下,新建包com.cn.service,然後在該包下新建介面UserService,程式碼如下:

package com.cn.service;

public interface UserService {
	public void addUser(); 
	public void selectUserByCon(String username,String password);
	public int div(int i,int j);
}

步驟3:在src/main/java下,新建包com.cn.service.impl,然後在該包下新建介面實現類UserServiceImpl,程式碼如下:

package com.cn.service.impl;

import org.springframework.stereotype.Service;

import com.cn.entity.User;
import com.cn.service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService {

	@Override
	public void addUser() {
		System.out.println("新增使用者");
		
	}

	public void selectUserByCon(String username,String password){
		System.out.println("使用者名稱"+username+"-"+"密碼"+password);
		
	}
	
	public int div(int i,int j){
		int result=i/j;
		return result;
	}

}

步驟4:在src/main/resources下,新建spring-aop.xml配置檔案,程式碼如下:

<?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:aop="http://www.springframework.org/schema/aop"  
       xmlns:context="http://www.springframework.org/schema/context"  
       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.xsd  
       http://www.springframework.org/schema/aop  
       http://www.springframework.org/schema/aop/spring-aop.xsd">  
	<!-- 配置自動掃描包 -->
	<context:component-scan base-package="com.cn" />
	<!-- 使aspectJ註解起作用,自動為匹配的類生成代理物件 -->
	<aop:aspectj-autoproxy />
</beans>

步驟5:在包com.cn.service.impl包下新建日誌切面LoginAspect和驗證切面ValidataAspect,程式碼如下:

LoginAspect類程式碼如下:

package com.cn.service.impl;

import java.util.Arrays;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
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.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 日誌切面
 * 宣告切面需要兩個步驟:1、將該類放入到IOC容器中,2、通過註解@Aspect將該類宣告為一個切面
 * */
@Order(2)
@Aspect
@Component
public class LoggingAspect {
	/***
	 * 1、前置通知
	 * 在目標方法開始前執行
	 * */
	@Before("execution(public void com.cn.service.impl.UserServiceImpl.selectUserByCon(String,String))")
	public void beforeMethod(JoinPoint joinPoint){
		String methodName=joinPoint.getSignature().getName();
		//Object[] args=joinPoint.getArgs();
		List<Object> args =Arrays.asList(joinPoint.getArgs());
		System.out.println("the method"+methodName+" begins with"+args);
	}
	
	/**
	 * 後置通知
	 * 在目標方法執行後(無論是否異常),執行的通知
	 * 在後置通知中還不能訪問
	 * */
	@After("execution(* com.cn.service.impl.*.*(String,String) )")
	public void afterMethod(JoinPoint joinPoint){
		String methodName=joinPoint.getSignature().getName();
		System.out.println("the method "+ methodName+" ends");
	}
	
	/**
	 * 返回通知
	 * 在方法正常結束後才執行的程式碼
	 * 返回通知是可以訪問到方法的返回值的( result是返回的結果)
	 * */
	@AfterReturning(value="execution (* com.cn.service.impl.*.*(int,int))",returning="result")
	public void afterReturnMethod(JoinPoint joinPoint,Object result){
		String methodName=joinPoint.getSignature().getName();
		System.out.println("the method "+methodName+" ends,after return is "+result);
	}
	
	/**
	 * 異常通知
	 * 在目標方法出現異常時會執行的程式碼
	 * 可以訪問到異常物件,且可以指定在出現異常時執行通知程式碼
	 * */
	@AfterThrowing(value="execution (public int com.cn.service.impl.UserServiceImpl.*(..))",throwing="ex")
	public void afterThrowingMethod(JoinPoint joinPoint,Exception ex){
		String methodName=joinPoint.getSignature().getName();
		System.out.println("the method "+methodName+" occurs exction: "+ex);
	}
	/**
	 * 環繞通知
	 * 環繞通知需要攜帶ProceedingJoinPoint型別的引數
	 * 環繞通知類似於動態代理的全過程:proceedingJoinPoint型別的引數可以決定是否執行目標方法,
	 * 且環繞通知必須有返回值,返回值為目標方法的返回值
	 * */
	@Around(value="execution (* com.cn.service.impl.*.*(..))")
	public void aroundMethod(ProceedingJoinPoint pjd){
		Object result=null;
		String methodName=pjd.getSignature().getName();
		try {
			//1、前置通知
			System.out.println("the method "+methodName+" begins with "+Arrays.asList(pjd.getArgs()));
			//執行目標方法
			result=pjd.proceed();
			//2、返回通知
			System.out.println("the method "+methodName+" ends with " +result);
			
		} catch (Throwable e) {
			e.printStackTrace();
			//3、異常通知
			System.out.println("the method occurs exception:"+e);
		}
			//4、後置通知
		System.out.println("the method "+methodName+" ends");
	}
}

ValidataAspect切面程式碼如下:

package com.cn.service.impl;

import java.util.Arrays;

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

/**
 * 驗證切面
 * @order 用於設定切面的優先順序
 * 
 * */
@Order(0)
@Aspect
@Component  //將該類加入到IOC容器中
public class ValidateAspect {
	/**
	 * 前置通知,在方法執行前呼叫
	 * */
	@Before(value="execution (public int com.cn.service.impl.UserServiceImpl.*(..))")
	public void validate(JoinPoint joinPoint){
		System.out.println("validate:"+Arrays.asList(joinPoint.getArgs()));
	}
}

步驟6:在src/main/java包下,新建包com.cn.test,然後在該包下新建測試類Test_aop

package com.cn.test;

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

import com.cn.service.UserService;

public class Test_aop {
	public static void main(String[] args) {
		ApplicationContext ctx=new ClassPathXmlApplicationContext("spring-aop.xml");
		UserService userService=(UserService) ctx.getBean("userService");
		int result=userService.div(100, 5);  
	}
}