AOP简介
AOP:全称是 Aspect Oriented Programming 即:面向切面编程。
AOP,面向切面编程就是当你已经完成,或者更加注意一些核心功能的时候,需要使用的编程过程。这个使用,你会发现,一个核心的业务,是由一个核心功能,和额外服务类型的功能,统一组成的,比如数据验证,再比如日志的记录。
其实在场景中,我们的核心功能是针对数据库的操作,可能是增删过程,而服务类功能,比如记录日志,或者输入内容的验证过程。其实不是非有不可,没有这个功能也能正常执行。
这个时候,我们就要考虑一个问题,这些必要或者非必要的额外功能,是不是要写在你的核心代码过程当中啊?
我们现在,将核心业务,做一个简单的切分:核心功能,和辅助功能。
场景的举例
突然一天,你刚将一个功能代码完成,这个时候,产品经理过来告诉你,客户那边有变化,需要一些小变动,在执行你这个功能前需要去验证一下,参数的传递是否正确。
AOP名词术语
- Aspect:切面,一个关注点的模块化,这个关注点可能会横切多个对象。
- 切点: pointcut 在某些个核心方法之前,之后等位置 切面 : 很多个切点组成的过程叫做切面
- Joinpoint:连接点,程序执行过程中的某一行为,即业务层中的所有方法。
- 增强:(Advice)(也叫通知)
主要是用来告诉AOP,我们在切点(目标方法上)应该在什么时机执行什么功能。- 织入 :将辅助功能安装在切点的过程
- 目标(Target):被通知的对象
- 代理(Proxy): 向目标对象应用通知之后创建的对象
基于配置文件的AOP
切点表达式
切点:execution(返回值 全包名.类名.方法名(参数集合))
两个点表示任意多个参数
execution(* com.lanou..(…))
增强时机的选择有以下几种:AspectJ(来源是AspectJ)
前置增强:before
后置增强:after
环绕增强:around
异常增强:throw
返回增强:after returning
三种实现增强方式
**1.继承Spring给出的对应的增强的接口(Spring优化后的)**
**2.在Aop配置的时候,通过aop:aspect来配置它的增强类型**
**(在方法里可以传入两个参数:**
**JoinPoint joinPoint(除around以外的其他几个增强类型)**
**ProceedingJoinPoint pjp(around类型的参数))**
**3.通过注解来实现**
**在配置文件中加入 <context:component-scan> 和 <aop:aspectj-autoproxy>;**
**在增强类上加两个注解:@Aspect 和**
**@Configuration
**
1.继承Spring给出的对应的增强的接口(Spring优化后的)
Spring只能找到两个增强点
-
前置增强继承MethodBeforeAdvice接口
-
前置通知拿到的内容
//参数method——将要执行的方法
//参数objects——将要执行的方法的参数集合
//参数Object o —— 是执行方法的那个类的对象,是内部真正执行方法的对象,
前置通知会做的一些事情:
合法性判断,合理性判断,参数集合的验证(是否为空等等),开启事务
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class MyBeforAdvice implements MethodBeforeAdvice {
//参数method——将要执行的方法
//参数objects——将要执行的方法的参数集合
//参数Object o —— 是执行方法的那个类的对象目标对象,是内部真正执行方法的对象,
//而在main方法里的target是它的代理,这个代理所有的使用过程和目标对象的使用过程一样,如果打印这个代理,这个代理将目标对象的toString方法拿过来使用的
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("我是前置");
//改变参数集合
objects[0] = "nnnnnn";
}
}
- 后置增强实现AfterReturningAdvice接口,
public class MyAfterAdvice implements AfterReturningAdvice , AfterAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
}
}
Object o 执行方法的返回值//
Method method 执行的方法//
Object[] objects 执行方法的参数列表//
Object o1 执行方法的对象
- 在配置文件中开启AOP
<bean id="myBeforAdvice" class="test.advice.MyBeforAdvice"></bean>
<aop:config>
<!-- aspect就是我们说的织入 advisor就是我们说的增强 pointcut就是我们说的切点-->
<!-- 一般配完advisor织入就默认生效了-->
<!-- 切入到里面所有的方法-->
<aop:pointcut id="methodsBefore" expression="execution(* test.service.*.*(..))"/>
<!-- 如果想要切到某些方法就配置如下过程
下方就是切到所有以l开头的方法 是用通配符匹配的-->
<!-- <aop:pointcut id="methodsBefore"expression="execution(* com.lanou.test.service.*.l(..))"/>-->
<aop:advisor advice-ref="myBeforAdvice" pointcut-ref="methodsBefore"></aop:advisor>
</aop:config>
2、使用aspectj 和Spring联合实现AOP ( 通用增强-)
- 配置文件配置:
<aop:config>
<aop:aspect ref="myAdvisor">
<!-- 不关心method的名字,只关心这是不是before-->
<aop:before method="before" pointcut-ref="methodsBefore"></aop:before>
<aop:after method="after" pointcut-ref="methodsBefore"></aop:after>
<aop:after-returning method="afterReturning" pointcut-ref="methodsBefore"> </aop:after-returning>
<aop:after-throwing method="afterThrowing" pointcut-ref="methodsBefore"> </aop:after-throwing>
<aop:around method="around" pointcut-ref="methodsBefore"></aop:around>
</aop:aspect>
</aop:config>
从理论过程来讲after先afterReturning后,after执行完了有可能改结果;afterReturning不能改结果,可以改结果里面的内容,在五种增强中,Around环绕增强是最强的。
around
在所有的增强中around是最强的,参数是ProceedingJoinPoint pjp
around可以针对方法内部的执行过程产生影响的一种通知,甚至可以拒绝方法的执行-
around既可以改变参数,也能拒绝方法正常执行
around可以改变方法的执行过程,可以改变返回值的内容
可以在之前截获参数改变参数,能在之后截获返回值改变返回值
public Object around(ProceedingJoinPoint pjp) {
System.out.println("around");
Object obj = null;
try {
obj = pjp.proceed(pjp.getArgs());
} catch (Throwable throwable) {
throwable.printStackTrace();
}
obj = null;
return obj;
}
注:在执行AOP的过程中,其实是Spring将service封装了一个代理类,真正执行的是Proxy,它去帮你执行service里的方法,你执行的是代理类的方法。
3、异常中断和正常结束执行对afterReturning和afterThrowing所谓影响
一个方法的正常结束和抛异常的异常结束是不一样的:
异常抛出是异常中断;异常捕获是正常结束。
就是说异常我捕获以后,try块里面的内容不执行了,但是执行catch块,是正常执行完了。此时afterThrowing就不会执行。
我抛出异常要不要捕获异常,抛出异常同时捕获异常,就等于没抛出这个异常,等于直接捕获异常了,继续执行了。
after不管是正常结束还是异常中断,他一定会执行。
afterReturning和afterThrowing执行条件:
如果在目标方法执行过程中碰见了异常并且没有捕获而抛出了异常,就会执行afterThrowing,不会执行afterReturning;
如果是正常结束没有抛出任何的异常,就会执行afterReturning,不会执行afterThrowing。
4、基于注解式开启AOP
在配置文件中开启AOP的注解:
<!--开启AOP的注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
在自定义增强类MyAdvisor类上加上:
@Aspect
@Configuration或加@Component
public class MyAdvisor {}
在方法上加上:
@Before("execution(* com.lanou.test.service.*.*(..))")
@After("execution(* com.lanou.test.service.*.*(..))")
@AfterReturning("execution(* com.lanou.test.service.*.*(..))")
@AfterThrowing("execution(* com.lanou.test.service.*.*(..))")
@Around("execution(* com.lanou.test.service.*.*(..))")
或者写一个空的方法,无返回值无方法体,然后在上面加上*@Pointcut(value = “execution(* com.lanou.test.service…(…))”)*,然后在后面所有的增强方法上将这个方法名传进来即可。
如:
@Pointcut(value = "execution(* com.lanou.test.service.*.*(..))")
public void point() {
}
@After("point()")
public void after(JoinPoint joinPoint) {
System.out.println("after");
}
Spring的动态代理方案
Aop底层原理使用的是动态代理。
Spring的动态代理方案有两种: 原生的jdk动态代理 、 CGLIB动态代理
CGLIB(Code Generator Library) 代码生成器库 如果拿的是个类,就用CGLIB创建这个类的子类,然后调用这个类的子类去实现动态代理
原生的jdk动态代理:JdkDynamicAopProxy(Spring提供的)
默认使用AOP的过程中(不加proxy-target-class),使用的是原生jdk动态代理实现
加入proxy-target-class="true"就从jdk的动态代理实现方案就变为了CGLIB动态代理实现方案 ;"false"就变为jdk动态代理实现方案。
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
当把实现类和接口分离后,写"false"使用的是CGLIB的动态代理方案
java的jdk的原生动态代理生成模式只支持接口的代理生成,只要这个类不是final类。
CGLIB将整个代理类切分的非常细,所以启动的慢 。
CGLIB动态代理和jdk动态代理的区别:
(1)在生成代理类的速度上jdk比CGLIB快
(2) 运行速度上CGLIB速度快
(3)jdk动态代理只能对接口创建
(4)CGLIB既可以对接口创建,也可以对类创建
使用场景:
如果这个类是单例的类,只创建一次,多次调用,使用CGLIB合适; 如果创建的特别的多,调用的比较少,使用jdk合适— 常规情况下,单例用的多,所以CGLIB用的多
★只要是Spring里的代理生成都是通过ClassLoader重新创建的