1、切面(Aspect)
首先要理解‘切’字,需要把对象想象成一个立方体,传统的面向对象变成思维,类定义完成之后(封装)。每次实例化一个对象,对类定义中的成员变量赋值,就相当于对这个立方体进行了一个定义,定义完成之后,那个对象就在那里,不卑不亢,不悲不喜,等着被使用,等着被回收。
面向切面编程则是指,对于一个我们已经封装好的类,我们可以在编译期间或在运行期间,对其进行切割,把立方体切开,在原有的方法里面添加(织入)一些新的代码,对原有的方法代码进行一次增强处理。而那些增强部分的代码,就被称之为切面,一般用于通用日志处理代码、事务处理、权限认证等等。
2、切入点(PointCut)
要对哪些类中的哪些方法进行增强,进行切割,指的是被增强的方法。即要切哪些东西。
3、连接点(JoinPoint)
我们知道了要切哪些方法后,剩下的就是什么时候切,在原方法的哪一个执行阶段加入增加代码,这个就是连接点。如方法调用前,方法调用后,发生异常时等等。
4、通知(Advice)
通知被织入方法,改如何被增强。定义切面的具体实现。那么这里面就涉及到一个问题,空间(切哪里)和时间(什么时候切,在何时加入增加代码),空间我们已经知道了就是切入点中定义的方法,而什么时候切,则是连接点的概念,如下面实例中,通用日志处理(切面),@Pointcut规则中指明的方法即为切入点,@Before、@After是连接点,而下面的代码就是对应通知。
5、目标对象(Target Object)
被一个或多个切面所通知的对象,即为目标对象。
6、AOP代理对象(AOP Proxy Object)
AOP代理是AOP框架所生成的对象,该对象是目标对象的代理对象。代理对象能够在目标对象的基础上,在相应的连接点上调用通知。
7、织入(Weaving)
将切面切入到目标方法之中,使目标方法得到增强的过程被称之为织入。
一、引入依赖
org.springframework
spring-aspects
二、定义一个切面类
@Aspect
@Component
public class MyAspect {
/**
* 声明切入点
* 该方法 point 的内容并不重要,方法名也不重要,实际上它只是作为一个标识,供通知使用
*/
@Pointcut("execution(* com.example.demo.web.Demo.hello(..))")
void point(){}
/**
* 目标方法调用之前调用
*/
@Before("point()")//也可直接使用execution(* com.example.demo.web.Demo.hello(..)),声明切入点,只是
public void before() {
System.out.println("before ...");
}
/**
* 目标方法返回或异常之后调用
*/
@After("point()")
public void after() {
System.out.println("After ...");
}
/**
* 目标方法返回之后调用
*/
@AfterReturning("point()")
public void afterReturning() {
System.out.println("AfterReturning ...");
}
/**
* 目标方法抛出异常之后调用
*/
@AfterThrowing("point()")
public void afterThrowing() {
System.out.println("AfterThrowing ...");
}
/**
* 目标方法调用之后调用
*/
@Around("point()")
public void around(ProceedingJoinPoint pj) {
try {
System.out.println("Around aaa ...");
pj.proceed();//如果不调用该对象的 proceed() 方法,表示不调用原目标方法及上面的切面方法
System.out.println("Around bbb ...");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
三、增加配置@EnableAspectJAutoProxy注解(可以不做),因为springboot-autoconfiguration中已经有此注解
@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true)//proxyTargetClass为true说明走CGLIB代理,否则走JDK代理
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
springboot-autoconfiguration代码如下:
如果要修改proxy-target-class还可以通过配置文件配置
spring.aop.auto=true
spring.aop.proxy-target-class=true
1、EnableAspectJAutoProxy注解引入了AspectJAutoProxyRegistrar处理类
2、AspectJAutoProxyRegistrar类注册bean定义
3、在 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying方法里标记上此类需要代理
4、在spring注册bean时增加代理
下图是在AbstractAutoProxyCreator类的wrapIfNecessary方法中
下图是spring创建bean时的栈帧
在CglibAopProxy类中创建代理
真正被增加的类会执行DynamicAdvisedInterceptor类的intercept方法
之后的每次被拦截的方法都会执行intercept方法
是spring对两种动态代理Proxy和cglib进行的封装