一、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的。
junit
junit
3.8.1
test
org.springframework
spring-context
4.3.0.RELEASE
org.springframework
spring-core
4.3.0.RELEASE
org.springframework
spring-beans
4.3.0.RELEASE
org.springframework
spring-expression
4.3.0.RELEASE
org.springframework
spring-aop
4.3.0.RELEASE
aopalliance
aopalliance
1.0
org.aspectj
aspectjweaver
1.8.9
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
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!