【Spring】AOP的代理默认是Jdk还是Cglib?
菜瓜:你觉得AOP是啥
水稻:我觉得吧,AOP是对OOP的补充。通常情况下,OOP代码专注功能的实现,所谓面向切面编程,大多数时候是对某一类对象的方法或者功能进行增强或者抽象
菜瓜:我看你这个理解就挺抽象的
水稻:举个栗子!我要在满足开闭原则的基础下对已有功能进行扩展
- 我现在想对很多个功能增加日志功能,但是代码已经打好包了,不想改。又或者有时候方法调用很慢,想定位问题
- low一点的方法就是每个方法调用之前记录调用开始,之后记录调用结束
菜瓜:你说的这个low一点的方法怎么好像是在说我???
水稻:建议看一下动态代理设计模式【DP-动态代理】JDK&Cglib,我当然知道你不会看,所以我还准备了自定义注解的栗子
package com.hb.merchant.config.aop; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy; /**
* @author QuCheng on 2020/6/23.
*/
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
} package com.hb.merchant.config.aop; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* @author QuCheng on 2020/6/23.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OperatorLog {
} package com.hb.merchant.config.aop; import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component; /**
*
* @author QuCheng on 2020/6/23.
*/
@Aspect
@Component
@Slf4j
public class OperatorAspect { @Around("@annotation(OperatorLog)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//获取要执行的方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//记录方法执行前日志
log.info("startLog: {} 开始了。。。" , methodSignature.getName());
//获取方法信息
String[] argNames = methodSignature.getParameterNames();
// 参数值:
final Object[] argValues = joinPoint.getArgs();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < argNames.length; i++) {
String value = argValues[i] == null ? "null" : argValues[i].toString();
sb.append(argNames[i]).append("=").append(value).append(",");
}
String paramStr = sb.length() > 0 ? sb.toString().substring(0, sb.length() - 1) + "]" : "";
log.info("参数信息为:[{}", paramStr); //执行方法
Object result;
try {
result = joinPoint.proceed();
} catch (Exception e) {
log.error("errorLog", e);
return null;
} //记录方法执行后日志
log.info("endLog: {} 结束了。。。" , methodSignature.getName());
return result;
} } package com.hb.merchant.controller.icbc.item.oc; import com.hb.merchant.config.aop.OperatorLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* @author QuCheng on 2020-06-23.
*/
@RestController
@RequestMapping("/item")
@Slf4j
public class ItemOcController { @OperatorLog
@GetMapping("/delete")
public String delete(Long itemId) {
Assert.notNull(itemId,"itemId不能为空");
return "delete finished ...";
}
} // 后台打印
startLog: delete 开始了。。。
参数信息为:[itemId=1]
endLog: delete 结束了。。。
菜瓜:这个自定义注解又是怎么实现的呢?
水稻:不愧是你,没有源码看来是满足不了你的好奇心了!!不知道你是否还记得我们之前有聊到过bean创建完毕后会调用一些PostProcessor对其进一步操作
菜瓜:有印象,@PostConstruct注解就是InitDestroyAnnotationBeanPostProcessor在这里调用的,还自定义过BeanPostProcessorT对象打印输出过bean信息
水稻:你猜Spring是怎么操作的
菜瓜:let me try try。结合刚刚的栗子和提示,大胆猜测应该是用PostProcessor在bean创建完成之后生成代理对象。实际调用代理的invoke方法实现对被代理bean的增强
水稻:思路正确。看脉络
- 入口在AbstractAdvisorAutoProxyCreator#initializeBean
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
。。。
// BeanNameAware BeanFactoryAware ...
invokeAwareMethods(beanName, bean);
。。。
// BeanPostProcessorBefore @PostConstruct
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
。。。
// initMethod InitializingBean接口
invokeInitMethods(beanName, wrappedBean, mbd);
。。。
if (mbd == null || !mbd.isSynthetic()) {
// aop
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}- 从aop入口跟下去
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
。。。
// 收集切面信息匹配被代理对象
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 如果符合切面 创建代理,被代理对象被代理引用
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}- 跟createProxy方法 -> DefaultAopProxyFactory#createAopProxy
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// jdk动态代理类
return new JdkDynamicAopProxy(config);
}
// cglib
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}- 此处省略了切面类搜集和匹配的过程。可以简单理解成搜集到所有的切面类信息获取pointcut的目录或者注解信息,匹配当前bean是否属于pointcut目标范围
- 另外我们可以看到最后返回的bean已经不是原始bean了,而是代理对象。也就是说getBean("xxx")返回的对象实际是代理对象,被代理对象被其成员变量直接引用
菜瓜:然后代理类中都有invoke方法,那些advice(@Around,@Before...)在invoke中找到适当时机调用对吧
水稻:是的,这里我想结合@Transactional注解会更容易理解,你肯定用过这个注解吧,它其实。。。
菜瓜:停。。。今天获取的知识量已经够了,我下去自己断点走一趟再熟悉熟悉。下次请结合Transactional注解再敲打我吧
水稻:也好,我下去再给你准备几个栗子
总结:
- AOP提供了在不侵入代码的前提下动态增强目标对象的途径,让OOP更加专注于实现自己的逻辑
- 而Spring的实现还是老套路,利用PostProcessor在类初始化完成之后替需要的bean创建代理对象
- 这里还有一些细节没有照顾到,譬如说AOP解析类是什么时候注册到IOC容器的(偷偷告诉你从@EnableAspectJAutoProxy注解下手)
【Spring】AOP的代理默认是Jdk还是Cglib?的更多相关文章
- 死磕Spring之AOP篇 - Spring AOP自动代理(二)筛选合适的通知器
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...
- 死磕Spring之AOP篇 - Spring AOP自动代理(三)创建代理对象
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...
- 死磕Spring之AOP篇 - Spring AOP自动代理(一)入口
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...
- Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式
Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...
- Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题
Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只 ...
- 什么是 Spring AOP 和代理
https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9403056301388627935% ...
- 求求你,下次面试别再问我什么是 Spring AOP 和代理了!
https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_9403056301388627935% ...
- Spring AOP 动态代理 缓存
Spring AOP应用:xml配置及注解实现. 动态代理:jdk.cglib.javassist 缓存应用:高速缓存提供程序ehcache,页面缓存,session缓存 项目地址:https://g ...
- spring aop 动态代理批量调用方法实例
今天项目经理发下任务,需要测试 20 个接口,看看推送和接收数据是否正常.因为对接传输的数据是 xml 格式的字符串,所以我拿现成的数据,先生成推送过去的数据并存储到文本,以便验证数据是否正确,这时候 ...
随机推荐
- 用Python做词云可视化带你分析海贼王、火影和死神三大经典动漫
对于动漫爱好者来说,海贼王.火影.死神三大动漫神作你肯定肯定不陌生了.小编身边很多的同事仍然深爱着这些经典神作,可见"中毒"至深.今天小编利用Python大法带大家分析一下这些神作 ...
- 基于nodejs+express+mysql+webstorm+html的 增删改查
一.工具准备 Nodejs框架,WebStorm.Mysql服务.Navicat.此篇文章只讲项目的搭建过程,至于Nodejs,WebStorm.Mysql的下载.安装与配置网上资源很多,请自行查阅, ...
- Android Studio常见对话框(普通对话框、单选对话框、多选对话框、进度条对话框、消息对话框、自定义对话框)
Android Studio常见对话框(普通对话框.单选对话框.多选对话框.进度条对话框.消息对话框.自定义对话框) 1.普通对话框 2.单选对话框 3.多选对话框 4.进度条对话框 5.消息对话框 ...
- vnc server,vnc server去哪下载,下载后如何连接使用(vnc viewer)
vnc server是vnc服务端,通过需要下载的服务器连接之后在服务器端下载. 1.使用到的工具:iis7服务器管理 2.首先去服务器端下载vnc 3.根据要求安装结束,得到登录密码. 4.用IIS ...
- Rocket - debug - TLDebugModuleInnerAsync
https://mp.weixin.qq.com/s/Xf4VFTHIDFh1NHmfwlTX3w 简单介绍TLDebugModuleInnerAsync的实现. 1. dmInner dmInner ...
- Java 第十一届 蓝桥杯 省模拟赛 无向连通图最少包含多少条边
无向连通图最少包含多少条边 题目 问题描述 一个包含有2019个结点的无向连通图,最少包含多少条边? 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可.本题的结果为一个整数,在提交答案时只填 ...
- Java实现Labeling Balls(拓扑排序的应用)
1 问题描述 给出一些球,从1N编号,他们的重量都不相同,也用1N标记加以区分(这里真心恶毒啊,估计很多WA都是因为这里),然后给出一些约束条件,< a , b >要求编号为 a 的球必须 ...
- Python UI自动化测试实操
本UI 自动化框架主要的实验的目的是:完成了登录页面的自动化登录与打开会员中心的页面这一自动化的过程. 废话不多说,直接上代码截图: 我们首先来看看整个工程的目录结构,这样以便于了解项目的调用关系: ...
- Java并发编程实战总结 (一)
前提 首先该场景是一个酒店开房的业务.为了朋友们阅读简单,我把业务都简化了. 业务:开房后会添加一条账单,添加一条房间排期记录,房间排期主要是为了房间使用的时间不冲突.如:账单A,使用房间1,使用时间 ...
- jdbc+mysql常见报错总结
1.The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone. You ...