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