AOP 称为面向切面编程,在程序开发中,AOP 技术可以在不改变原有方法代码的情况下,把逻辑直接插入到方法上。Spring AOP 的大致原理主要依靠的是动态代理,你以为操作的这个类,实际上操作的是它的一个代理类。动态代理用到两种机制,一直是基于 JDK 的动态代理,另一种是基于 CGLib 的动态代理。网上有很多介绍这种原理的文章,深入了解可以参考这些文章。
AOP 中有很多术语,比如连接点,切入点,有必要解释一下:
术语解释
Joinpoint(连接点): 类里面可以被增强的方法,这些方法称为连接点。
Pointcut(切入点): 类中有很多连接点,但是我们真正增强的那个连接点,即那个方法,称之为所谓切入点。
Advice(通知/增强): 通知/增强,指的是增强某个方法而实现的逻辑。通知/增强 有几个类型。分为:
捋一下 AOP 的相关术语,白话的说如下:
一个类有好多个连接点(方法),当去增强某个连接点时,这个连接点就称之为切入点。有好几种方式增强某个切入点(就是扩展一些逻辑功能),分别是:
这个扩展功能的过程又称之为切面。
代码演示
使用 AOP 的时候,需要配置 AOP,分好几步。
1,配置切入点,就是要对哪个方法增强。用到 execution 表达式写法。
2,实现需要增强的逻辑,这个逻辑通常是写在某个方法中,这个方法可以用来增强切入点。
3,配置切面,即配置一下,把增强和切入点关联起来。确定了哪个方法需要哪方面的增强,增强方式是前置增强,或者后置增强,或者其他类型的增强。
配置可以是用 XML 配置,也可以基于注解配置。这里仅演示基于注解配置,本质上都是一样的。
配置文件:
bean1.xml
<?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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.learn" />
<!-- 开启aop注解方式,此步骤s不能少,这样java类中的aop注解才会生效 -->
<aop:aspectj-autoproxy/>
</beans>
需要被增强的方法,即所谓的切入点:
package com.learn.demo;
import org.springframework.stereotype.Component;
@Component
public class HelloWorld {
public void sayHello(){
System.out.println("Hello!");
}
}
配置 AOP 的类,AOP 的增强功能在这里实现 。
package com.learn.demo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AopUtils {
//前置通知
@Before("execution(* com.learn.demo.HelloWorld.*(..))")
public void before() {
System.out.println("before doing.....");
}
// 后置通知
@After("execution(* com.learn.demo.HelloWorld.*(..))")
public void after() {
System.out.println("after doing.....");
}
// 环绕通知。注意要有ProceedingJoinPoint参数传入。
@Around("execution(* com.learn.demo.HelloWorld.*(..))")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around..begin");
pjp.proceed();// 执行方法
System.out.println("around..end");
}
}
上面的代码中,有形如 execution 这样的表达式,这称之为切入点表达式。其语法格式:
execution(< 访问修饰符>?< 返回类型>< 方法名>(< 参数>)< 异常>)
这个表达式和定义方法的写法很类似,举个例子:
上述代码中,每个注解上都有 execution 表达式。其实可以定义一个切入点函数,用该函数代替每个 execution 表达式。
package com.learn.demo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AopUtils {
//定义切点
@Pointcut("execution(* com.learn.demo.HelloWorld.*(..))")
public int point(){return 0;}
@Before("point()")
public void before(){
System.out.println("before doing.....");
}
//后置通知
@After("point()")
public void after(){
System.out.println("after doing.....");
}
//环绕通知。注意要有ProceedingJoinPoint参数传入。
@Around("point()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around..begin");
pjp.proceed();//执行方法
System.out.println("around..end");
}
}
调用代码如下:
package com.learn;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.learn.demo.HelloWorld;
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean1.xml");
HelloWorld helloWorld = ( HelloWorld) context.getBean("helloWorld");
helloWorld.sayHello();
}
}
运行结果如下:
around..begin before doing….. Hello! around..end after doing…..