一. AOP是什么
1.AOP简介
AOP的出现确实解决外围业务代码与核心业务代码分离的问题,但它并不会替代OOP,如果说OOP的出现是把编码问题进行模块化,那么AOP就是把涉及到众多模块的某一类问题进行统一管理 AspectJ是一个java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能
2.定义语法
切点语法:
pointcut recordLog():call(* HelloWord.sayHello(..));
pointcut :切点定义
recordLog() :AOP方法名
call :调用 * HelloWord.sayHello(..)方法时调用切点方法
* :返回任意类型
.. :参数类型任意
通知语法:
before 目标方法执行前执行,前置通知
after 目标方法执行后执行,后置通知
after returning 目标方法返回时执行,后置返回通知
after throwing 目标方法抛出异常时执行异常通知
around 在目标函数执行中执行,可控制目标函数是否执行,环绕通知
Object around():aroundAdvice(){
System.out.println("sayAround 执行前执行");
Object result=proceed();//执行目标函数
System.out.println("sayAround 执行后执行");
return result;
}
3.AspectJ的植入方式及其原理概要
3.1织入
- 静态织入:
ApectJ采用的就是静态织入的方式。ApectJ主要采用的是编译期织入,在这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标类 - 动态织入: 方式是在运行时动态将要增强的代码织入到目标类中,这样往往是通过动态代理技术完成的,如Java
JDK的动态代理(Proxy,底层通过反射实现)或者CGLIB的动态代理(底层通过继承实现),Spring
AOP采用的就是基于运行时增强的代理技术.
静态织入
二. 基于Aspect Spring AOP开发
Spring没有采用ajc编译器,而是采用JDk动态代理技术来实现AOP功能
三. 基于注解的SprigAOP开发
1. 定义切入点函数
/**
* 使用Pointcut定义切点
*/
@Pointcut("execution(*com.zejian.spring.springAop.dao.UserDao.addUser(..))")
private void myPointcut(){}
/**
* 应用切入点函数
*/
@After(value="myPointcut()")
public void afterDemo(){
System.out.println("最终通知....");
}
2. 切入点指示符
2.1 通配符
.. :匹配方法定义中的任意数量的参数,此外还匹配类定义中的任意数量包
//任意返回值,任意名称,任意参数的公共方法
execution(public * *(..)) //匹配com.zejian.dao包及其子包中所有类中的所有方法
within(com.zejian.dao..*)
+ :匹配给定类的任意子类
//匹配实现了DaoUser接口的所有子类的方法
within(com.zejian.dao.DaoUser+)
* :匹配任意数量的字符
//匹配com.zejian.service包及其子包中所有类的所有方法
within(com.zejian.service..*)
//匹配以set开头,参数为int类型,任意返回值的方法
execution(* set*(int))
3. Aspect优先级
- 同一个切面:顺序执行
- 不同切面: 进入时则优先级高的切面类中的通知函数优先执行,退出时则最后执行 切面通过实现Ordered接口
@Override public int getOrder() {
return 0;//返回的值越小,那么优先级越大
}
四. Spring AOP的实现原理概要
1. JDK动态代理
- 实际上动态代理的底层是通过反射技术来实现,只要拿到A类的class文件和 A类的实现接口,很自然就可以生成相同接口的代理类并调用a对象的方法了.
- java的动态代理是有先决条件的,该条件是目标对象必须带接口(Proxy类需要 通过接口来构建一个新类),如A类的接口是ExInterface,通过ExInterface接口动态 代理技术便可以创建与A类类型相同的代理对象
- 代理类(Demo中的JDKProxy)还需要实现InvocationHandler接口,也是由 JDK提供,代理类必须实现的并重写invoke方法,完全可以把InvocationHandler看成 一个回调函数(Callback),Proxy方法创建代理对象proxy后,当调用execute方法 (代 理对象也实现ExInterface)时,将会回调InvocationHandler#invoke方法,因此我们可 以在invoke方法中来控制被代理对象(目标对象)的方法执行,从而在该方法前后动态增 加其他需要执行的业务
/**
* Created by zejian on 2017/2/11.
* Blog : [原文地址,请尊重原创]
*/
//自定义的接口类,JDK动态代理的实现必须有对应的接口类
public interface ExInterface {
void execute();
}
//A类,实现了ExInterface接口类
public class A implements ExInterface{
public void execute(){
System.out.println("执行A的execute方法...");
}
}
//代理类的实现
public class JDKProxy implements InvocationHandler{
/**
* 要被代理的目标对象
*/
private A target;
public JDKProxy(A target){
this.target=target;
}
/**
* 创建代理类
* @return
*/
public ExInterface createProxy(){
return (ExInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
/**
* 调用被代理类(目标对象)的任意方法都会触发invoke方法
* @param proxy 代理类
* @param method 被代理类的方法
* @param args 被代理类的方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//过滤不需要该业务的方法
if("execute".equals(method.getName())) {
//调用前验证权限
AuthCheck.authCheck();
//调用目标对象的方法
Object result = method.invoke(target, args);
//记录日志数据
Report.recordLog();
return result;
}eles if("delete".equals(method.getName())){
//.....
}
//如果不需要增强直接执行原方法
return method.invoke(target,args);
}
}
//测试验证
public static void main(String args[]){
A a=new A();
//创建JDK代理
JDKProxy jdkProxy=new JDKProxy(a);
//创建代理对象
ExInterface proxy=jdkProxy.createProxy();
//执行代理对象方法
proxy.execute();
}
2. CGLIB动态代理(接口换成继承)