版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a656678879/article/details/83503747
AOP的基础知识总结(jdk动态代理/cglib动态代理)
知识总结
aop切面编程
切面:
切面包含了通知和切点,通知和切点共同定义了切面是什么,在何时,何处执行切面逻辑。
切点:
如果说通知定义了在何时执行通知,那么切点就定义了在何处执行通知。所以切点的作用就是
通过匹配规则查找合适的连接点(Joinpoint),AOP 会在这些连接点上织入通知。
通知:
Spring 中对应了 5 种不同类型的通知:
· 前置通知(Before):在目标方法执行前,执行通知
· 后置通知(After):在目标方法执行后,执行通知,此时不关系目标方法返回的结果是什么
· 返回通知(After-returning):在目标方法执行后,执行通知
· 异常通知(After-throwing):在目标方法抛出异常后执行通知
· 环绕通知(Around): 目标方法被通知包裹,通知在目标方法执行前和执行后都被会调用
动态代理:
Spring在选择用JDK还是CGLiB的依据:
(1)当Bean实现接口时,Spring就会用JDK的动态代理(就比如userServiceImpl实现了IUserService)
(2)当Bean没有实现接口时,Spring使用CGlib是实现(就比如userServiceImpl没有实现IUserService)
(3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
CGlib比JDK快?
(1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
(2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
Spring的aop的基本的使用:
增加依赖包:
jdk:不需要额外添加依赖
cglib:
org.springframework.spring-aop.5.0.8.RELEASE
org.aspectj.aspectjweaver.1.8.13
org.springframework.spring-aspects.5.0.8.RELEASE
aopalliance.aopalliance.1.0
增加切面类类:
① 在类上使用 @Component 注解 把切面类加入到IOC容器中
② 在类上使用 @Aspect 注解 使之成为切面类
案例(可以只使用环绕通知/其他的前置后置通知之一):
@Component
@Aspect
public class LoggingAspect {
/**
* 前置通知:目标方法执行之前执行以下方法体的内容
* @param jp
*/
@Before("execution(* com.qcc.beans.aop.*.*(..))")
public void beforeMethod(JoinPoint jp){
String methodName = jp.getSignature().getName();
System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
}
/**
* 返回通知:目标方法正常执行完毕时执行以下代码
* @param jp
* @param result
*/
@AfterReturning(value="execution(* com.qcc.beans.aop.*.*(..))",returning="result")
public void afterReturningMethod(JoinPoint jp, Object result){
String methodName = jp.getSignature().getName();
System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】");
}
/**
* 后置通知:目标方法执行之后执行以下方法体的内容,不管是否发生异常。
* @param jp
*/
@After("execution(* com.qcc.beans.aop.*.*(..))")
public void afterMethod(JoinPoint jp){
System.out.println("【后置通知】this is a afterMethod advice...");
}
/**
* 异常通知:目标方法发生异常的时候执行以下代码
*/
@AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e")
public void afterThorwingMethod(JoinPoint jp, NullPointerException e){
String methodName = jp.getSignature().getName();
System.out.println("【异常通知】the method 【" + methodName + "】 occurs exception: " + e);
}
// /**
// * 环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码
// * @return
// */
// @Around(value="execution(* com.qcc.beans.aop.*.*(..))")
// public Object aroundMethod(ProceedingJoinPoint jp){
// String methodName = jp.getSignature().getName();
// Object result = null;
// try {
// System.out.println("【环绕通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
// //执行目标方法
// result = jp.proceed();
// System.out.println("【环绕通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result);
// } catch (Throwable e) {
// System.out.println("【环绕通知中的--->异常通知】:the method 【" + methodName + "】 occurs exception " + e);
// }
//
// System.out.println("【环绕通知中的--->后置通知】:-----------------end.----------------------");
// return result;
// }
}