一、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!