- 开始学习spring就被重点的关注ioc和aop这两个关键的部分,今天着重了解下关于aop的一些实现和原理。
- aop 面向切面编程 即可以在业务逻辑前后加上需要的无关业务的处理如日志,权限控制等操作,与实际的业务相分离。
- 首先需要介绍的技术就是AspectJ 了解了这个之后会对spring的aop实现有对比显示出两者的差异。aspectJ是一个由java实现的aop框架,通过对编译期的代码进行编译 让代码具有aop的功能。
- 切面就是切点与通知的一个组合,在切面内部定义了两个部分
- 切点 pointcut 即需要应用切面的方法,
- pointcut 函数名 call(){匹配表达式}
pointcut recordLog():call(* HelloWord.sayHello(..));
- 通知 advice 需要在目标方法前后执行的函数
- before 前置通知 目标方法执行前
- after 后置通知 目标方法执行后
- after returning 方法返回通知 目标方法返回时执行
- after throwing 异常通知 目标方法抛出异常时执行
- around 目标函数中 执行 可以控制目标函数是否执行 环绕通知
- 目标函数 称为连接点 joinPoint
- 织入 指代码切入到目标函数的过程 例如aspectJ到java程序的过程称为织人
- 静态织入 aspectJ 编译为class文件后在目标类中织入
- idea进行aspectJ
- 动态织入 jdk动态代理 cglib
- @Aspect
- 静态织入 aspectJ 编译为class文件后在目标类中织入
- 切面就是切点与通知的一个组合,在切面内部定义了两个部分
@Aspect
public class AspectJ {
/**
* 前置通知
*/
@Before("execution(* aop.UserDao.addUser(..))")
public void before(){
System.out.println("前置通知");
}
/**
* 后置通知
* returnVal,切点方法执行后的返回值
*/
@AfterReturning(value="execution(* aop.UserDao.addUser(..))",returning = "returnVal")
public void AfterReturning(Object returnVal){
System.out.println("后置通知...."+returnVal);
}
/**
* 环绕通知
* @param joinPoint 可用于执行切点的类
* @return
* @throws Throwable
*/
@Around(value="execution(* aop.UserDao.addUser(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前.....");
Object result = joinPoint.proceed();
System.out.println("环绕后.....");
return result;
}
/**
* 抛出通知
* @param e
*/
@AfterThrowing(value = "execution(* aop.UserDao.addUser(..))",throwing = "e")
public void afterThrowable(Throwable e){
System.out.println("出现异常:msg="+e.getMessage());
}
/**
* 无论什么时候都会执行
*/
@After(value = "execution(* aop.UserDao.addUser(..))")
public void after(){
System.out.println("最终通知....");
}
}
或者
@Pointcut(value = "execution( * aop.UserDao.addUser(..))")
public void p1(){}
/**
* 前置通知
*/
@Before(value = "p1()")
public void before(){
System.out.println("前置通知");
}
- 切点指示符
.. 匹配方法中任意参数数量的方法
within(com.zejian.dao..*) 匹配该类中的所有方法
within(com.zejian.dao.DaoUser+) 匹配该类的所有子类/实现该接口的类的所有方法
within(com.zejian.service..*) 匹配 该包及子包下的所有方法
类型签名表达式
within(<type name>)
方法签名表达式
//匹配UserDaoImpl类中的所有方法
@Pointcut("execution(* com.zejian.dao.UserDaoImpl.*(..))")
//匹配UserDaoImpl类中的所有公共的方法
@Pointcut("execution(public * com.zejian.dao.UserDaoImpl.*(..))")
//匹配UserDaoImpl类中的所有公共方法并且返回值为int类型
@Pointcut("execution(public int com.zejian.dao.UserDaoImpl.*(..))")
//匹配UserDaoImpl类中第一个参数为int类型的所有公共的方法
@Pointcut("execution(public * com.zejian.dao.UserDaoImpl.*(int , ..))")
bean
@Pointcut("bean(*Service)") 用于匹配后缀为service的bean
private void myPointcut1(){}
this
用于匹配当前AOP代理对象类型的执行方法
and or not ! && ||
Aspect的优先级
不同切面
- 实现 ordered接口 实现getOrder() 越小的优先级越高
基于注解的 @Order(0,1,2)
AOP的应用场景- 模拟系统性能监控
异常统计
实现原理cglib 继承
- interface methodInterceptor
- Enhancer 产生代理对象(被代理对象的子类)
- jdk动态代理 基于反射
附上代码
public class CglibProxy implements MethodInterceptor {
//被代理对象
private A target;
public CglibProxy(A target) {
super();
this.target = target;
}
public A createProxy(){
// 1.声明增强类实例,用于生产代理类
Enhancer enhancer = new Enhancer();
// 2.设置被代理类字节码,CGLIB根据字节码生成被代理类的子类
enhancer.setSuperclass(target.getClass());
// 3.设置回调函数
enhancer.setCallback(this);
// 创建代理
return (A) enhancer.create();
}
/**
* 回调函数
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if("execute".equals(method.getName())) {
//调用前验证权限(动态添加其他要执行业务)
// AuthCheck.authCheck();
//调用目标对象的方法(执行A对象即被代理对象的execute方法)
Object result = methodProxy.invokeSuper(proxy, args);
//记录日志数据(动态添加其他要执行业务)
// Report.recordLog();
return result;
}
return methodProxy.invokeSuper(proxy,args);
}
}
参考博客 神一样的aspectj-aop的领跑者