Spring动态代理的生成-如何判断是使用JDK动态代理还是CGlib代理
前言
在上一篇文章中讲到了Spring是如何获取对应的Bean的增强,然后本次主要讲解一下Spring如何在获取到增强后创建Spring代理的。
在步入正题之前先给大家看一下Spring创建代理的大致流程图
接下来我们就回到AbstractAutoProxyCreator.class
类中的wrapIfNecessary
方法。
- 看源码(
AbstractAutoProxyCreator.class
)
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 如果已经处理过
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 无需增强
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 给定的bean类是否是一个基础设施类,基础设施类不应该被代理,或者配置了指定的bean不需要代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 获取能够应用到当前 Bean 的所有 Advisor(已根据 @Order 排序)
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果有 Advisor,则进行下面的动态代理创建过程
if (specificInterceptors != DO_NOT_PROXY) {
// 如果获取到了增强则需要针对增强进行代理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理 JDK 动态代理或者 CGLIB 动态代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// 将代理对象的 Class 对象(目标类的子类)保存
this.proxyTypes.put(cacheKey, proxy.getClass());
// 返回这个 Bean 对象
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
在上一篇Spring源码之创建AOP代理之增强器的获取文章中,主要是围绕着getAdvicesAndAdvisorsForBean
方法展开的,主要是获取到了所有对应Bean的增强器,并获取到了此目标Bean所匹配的Advisor,
接下来我们着手对接下来的方法createProxy
进行分析,
- 看源码(
AbstractAutoProxyCreator.class
)
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 创建一个代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 复制当前 ProxyConfig 的一些属性(例如 proxyTargetClass、exposeProxy)
proxyFactory.copyFrom(this);
// 判断是否是代理类(也就是是否开启了CGLIB代理) 默认是false
if (proxyFactory.isProxyTargetClass()) {
// Explicit handling of JDK proxy targets (for introduction advice scenarios)
if (Proxy.isProxyClass(beanClass)) {
// Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
for (Class<?> ifc : beanClass.getInterfaces()) {
proxyFactory.addInterface(ifc);
}
}
}
else {
// 如果这个 Bean 配置了进行类代理,则设置为 `proxyTargetClass` 为 `true`
// No proxyTargetClass flag enforced, let's apply our default checks...
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 检测当前Bean 实现的接口是否包含可代理的接口 ,如果没有,则将proxyTargetClass 设置为true 表示需要进行CGLIB 提升
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 对入参的 advisors 进一步处理,因为其中还可能存在Advice类型 需要将他们包装成 DefaultPointcutAdvisor
// 如果配置了 `interceptorNames` 拦截器,也会添加进来
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 代理工厂添加 Advisor 数组
proxyFactory.addAdvisors(advisors);
// 代理工厂设置 TargetSource 对象
proxyFactory.setTargetSource(targetSource);
// 对 ProxyFactory 进行加工处理,抽象方法,目前没有子类实现
customizeProxyFactory(proxyFactory);
//用来控制代理工厂被配置之后,是否还允许修改通知。(默认为 false) (即在代理被配置之后,不允许修改代理的配置)。
proxyFactory.setFrozen(this.freezeProxy);
// 这个 AdvisedSupport 配置管理器是否已经过滤过目标类(默认为 false)
if (advisorsPreFiltered()) {
// 设置 `preFiltered` 为 `true`
// 这样 Advisor 们就不会根据 ClassFilter 进行过滤了,而直接通过 MethodMatcher 判断是否处理被拦截方法
proxyFactory.setPreFiltered(true);
}
// 如果 bean 类未在覆盖类加载器中本地加载,则使用原始 ClassLoader
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
// 通过 ProxyFactory 代理工厂创建代理对象
return proxyFactory.getProxy(classLoader);
}
源码分析
上述代码中的
ProxyFactory proxyFactory = new ProxyFactory();
新建了一个工厂类,并且往后看,明显的看出对于代理类的创建Spring是委托给了ProxyFactory
处理的。接下来继续跟踪源码
proxyFactory.getProxy(classLoader);
该方法创建了代理对象。看源码(
Proxyfactory.java
)
public Object getProxy(@Nullable ClassLoader classLoader) {
// 先创建一个 AOP 代理类(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy) 其实现是在DefaultAopProxyFactory中
// 根据 AOP 代理为目标 Bean 创建一个代理对象,并返回
return createAopProxy().getProxy(classLoader);
}
源码分析
通过上述注释可以感觉到终于要来到了主题,到底是如何决定使用哪种代理方式的。首先我们看到
getProxy
方法中的createAopProxy
方法,它的默认实现其实是在DefaultAopProxyFactory
类中。这中间它经过了ProxyCreatorSupport
类的createAopProxy
方法。看源码(
ProxyCreatorSupport.java
)
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
// 先获取 AOP 代理工厂,默认为 DefaultAopProxyFactory,只有这个实现
// 然后通过它根据创建当前 AdvisedSupport 配置管理器创建一个 AOP 代理(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy)
return getAopProxyFactory().createAopProxy(this);
}
紧接着我们直接来到具体实现createAopProxy方法
的实现类DefaultAopProxyFactory
类中。
- 看源码(
DefaultAopProxyFactory.java
)
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 判断是否满足下面条件的
/*
* config.isOptimize() 需要优化,默认为 `false`详细来说就是:用来控制通过CGLIB创建的代理是否使用激进的优化策略
* 除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置,目前这个属性仅用于CGLIB 代理,对于JDK动态代理(缺省代理)无效
* config.isProxyTargetClass() 使用类代理,也就是使用 CGLIB 动态代理 默认为 `false`
* 设置方式:<aop:aspectj-autoproxy proxy-target-class="true"/>
* hasNoUserSuppliedProxyInterfaces(config) // 是否存在代理接口
*/
if (!NativeDetector.inNativeImage() &&
(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.");
}
// 如果目标类是一个接口或者是 java.lang.reflect.Proxy 的子类 则还是使用 JDK 动态代理,创建一个 JdkDynamicAopProxy 对象,
// 传入 AdvisedSupport 配置管理器,并返回
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 使用 CGLIB 动态代理,创建一个 ObjenesisCglibAopProxy 对象,传入 AdvisedSupport 配置管理器,并返回
return new ObjenesisCglibAopProxy(config);
}
else {
// 使用 JDK 动态代理,创建一个 JdkDynamicAopProxy 对象,传入 AdvisedSupport 配置管理器,并返回
return new JdkDynamicAopProxy(config);
}
}
源码分析
在这个
DefaultAopProxyFactory
类中可以明显的看到,这里根据Optimize
、ProxyTargetClass
、hasNoUserSuppliedProxyInterfaces
三个属性进行的决断,看究竟使用哪种动态代理。
optimize 需要优化,默认为
false
详细来说就是:用来控制通过CGLIB创建的代理是否使用激进的优化策略;除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置,目前这个属性仅用于CGLIB 代理,对于JDK动态代理(缺省代理)无效ProxyTargetClass使用类代理,也就是使用 CGLIB 动态代理 默认为
false
设置方式:<aop:aspectj-autoproxy proxy-target-class="true"/>
hasNoUserSuppliedProxyInterfaces(config) // 是否存在代理接口
JDK与Cglib的说明
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP。
- 如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理 和CGLIB之间转换
如何强制使用CGLIB实现AOP?
- 添加 CGLIB 库,Spring_HOME/cglib/*.jar
- Spring 配置文件中加人<aop:aspectj-autoproxy proxy-target-class="true"/>。
JDK动态代理和CGLIB字节码生成的区别?
- JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
- GLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final。
好了到这里就讲完了Spring是如何决定使用哪种动态代理的方式的。
想要获取更多精彩内容请微信搜索【码上遇见你】
Spring动态代理的生成-如何判断是使用JDK动态代理还是CGlib代理的更多相关文章
- Spring声明式事务的实现方式选择(JDK动态代理与cglib)
1.简介 Spring声明式事务的具体实现方式是动态决定的,与具体配置.以及事务代理对象是否实现接口等有关. 2.使用JDK动态代理的情况 在满足下面两个条件时,Spring会选择JDK动态代理作为声 ...
- jdk动态代理和cglib动态代理底层实现原理超详细解析(jdk动态代理篇)
代理模式是一种很常见的模式,本文主要分析jdk动态代理的过程 1.举例 public class ProxyFactory implements InvocationHandler { private ...
- Spring AOP详解 、 JDK动态代理、CGLib动态代理
AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...
- 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
Spring AOP详解 . JDK动态代理.CGLib动态代理 原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...
- 浅谈Spring中JDK动态代理与CGLIB动态代理
前言Spring是Java程序员基本不可能绕开的一个框架,它的核心思想是IOC(控制反转)和AOP(面向切面编程).在Spring中这两个核心思想都是基于设计模式实现的,IOC思想的实现基于工厂模式, ...
- Spring强制使用CGLIB代理事务
Spring强制使用CGLIB代理事务 springaopjdkreferenceclasspath Spring1.2: 将事务代理工厂[TransactionProxyFactoryBean] ...
- JDK动态代理浅析
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2018-06-29/17.html 作者:夜月归途 出处:http://www.guitu ...
- spring 如何决定使用jdk动态代理和cglib(转)
Spring1.2: 将事务代理工厂[TransactionProxyFactoryBean] 或 自动代理拦截器[BeanNameAutoProxyCreator] 的 proxyTargetCla ...
- spring5 源码深度解析----- AOP代理的生成
在获取了所有对应bean的增强后,便可以进行代理的创建了.回到AbstractAutoProxyCreator的wrapIfNecessary方法中,如下所示: protected static fi ...
随机推荐
- Java finally语句到底是在return之前还是之后执行
看过网上关于Java中异常捕获机制try-catch-finally块中的finally语句是不是一定会被执行的讨论也有很多. 首先明确一点,下面两种情况finally肯定是不执行的: 1). ret ...
- Spring之属性注入
时间:2017-1-31 23:38 --Bean的属性注入方式有三种注入方式: 1)接口注入: 定义一个接口,定义setName(String name)方法,定义一个类,实现该 ...
- java js转码解码
摘自网友:https://blog.csdn.net/sgear/article/details/1509400?utm_medium=distribute.pc_relevant.none-task ...
- 真实机中安装CentOS
前言 最近在b站上看了兄弟连老师的Linux教程,非常适合入门:https://www.bilibili.com/video/BV1mW411i7Qf 看完后就自己来试着玩下,正好手上有台空闲的电脑就 ...
- cmd(命令行)超好用的技巧,很不错的打开方式
超快速打开管理cmd widows + x 按a 直接打开文件位置,在地址栏输入cmd 地址----直接cmd打开到所在文件位置 ex:cmd D:\work cd ../../../ 返回上几层的方 ...
- 磁盘“Seagate”没有被推出,因为一个或多个程序可能正在使用它。
推出移动硬盘失败,解决方案: 执行 lsof /Volumes/Seagate/ 可以看到哪些进程在占用磁盘 $ lsof /Volumes/Seagate/ COMMAND PID USER FD ...
- GoLang设计模式05 - 原型模式
原型模式也是一种创建型模式,它可以帮助我们优雅地创建对象的拷贝.在这种设计模式里面,将克隆某个对象的职责交给了要被克隆的这个对象.被克隆的对象需要提供一个clone()方法.通过这个方法可以返回该对象 ...
- jq的选择器中带有特殊符号无法获取元素
因项目需要,将元素id命名为数组(array[i].string) 使用jq去获取该id的元素时,返回的是个undefined.即jq获取不到该元素,因为该元素中的id含有特殊字符"[&qu ...
- Servlet3.0注解配置访问路径和urlParttern配置
一.Servlet用注解配置访问路径 二.IDEA的tomcat相关配置 其中,第一点的配置文件,直接在IDEA的可视化操作界面修改就可以改掉配置文件中内容: 三.urlParttern配置 其中,* ...
- GC-优化-案例
优化案例 Service S 优化 现在看一下执行jstat -gcutil的结果 S0 S1 E O P YGC YGCT FGC FGCT GCT 12.16 0.00 5.18 63.78 20 ...