IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring(十三):创建Bean——使用构造方法创建Bean -> 正文阅读

[Java知识库]Spring(十三):创建Bean——使用构造方法创建Bean

作者:recommend-item-box-tow

回顾

前面已经看了整个Bean的加载过程了,并且在上一章已经看到了,Spring如何对Bean的构造方法进行筛选,Spring将筛选构造方法和使用构造方法创建Bean都交由了ConstructResolver负责

下面就看下,Spring如何利用构造方法来创建Bean的

使用构造方法构建Bean

先看一下ConstrutResolver找到了构造方法是如何实例Bean的

在这里插入图片描述
可以看到,代码为

bw.setBeanInstance(instantiate(beanName,mbd,constructorToUser,argsToUse))

逻辑也很简单,使用instantiate方法实例Bean,然后装配进beanWrapper中

instantiate方法

在这里插入图片描述
源码如下

	private Object instantiate(
			String beanName, RootBeanDefinition mbd, Constructor<?> constructorToUse, Object[] argsToUse) {

		try {
            //从beanFactory获取实例化策略
			InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();
            //判断有没有开安全管理配置器
			if (System.getSecurityManager() != null) {
				return AccessController.doPrivileged((PrivilegedAction<Object>) () ->
						strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),
						this.beanFactory.getAccessControlContext());
			}
			else {
                //使用实例化策略去创建bean
				return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean instantiation via constructor failed", ex);
		}
	}

逻辑如下

  • 根据beanFactory去获取实例化策略
  • 使用实例化策略去进行创建bean

实例化策略

实例化过程需要使用实例化策略,那么实例化策略是用来做什么的呢?

经过前面一系列的步骤,我们已经得到了足以实例化的所有信息,完全可以使用最简单的反射方法来直接反射来构造实例对象,但是Spring并不是直接这么做,因为这还涉及一个动态代理问题,而是让策略去做,也就是使用策略模式去进行实例化Bean

在这里插入图片描述
可以看到,这个InstantiationStrategy是一个接口,并且有两个实现类

  • SimpleInstantiationStrategy:简单的实例化策略
  • CglibSubclassingInstantiationStrategy:扩展了SimpleInstantiationStrategy,默认的策略就是这个

而调用的instantiate只有SimpleInstantiationStrategy实现了

在这里插入图片描述
源码如下

	@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
			final Constructor<?> ctor, Object... args) {

        //判断RootBeanDefinition是否有覆盖方法
		if (!bd.hasMethodOverrides()) {
			if (System.getSecurityManager() != null) {
				// use own privileged to change accessibility (when security is on)
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					ReflectionUtils.makeAccessible(ctor);
					return null;
				});
			}
            //没有动态代理,使用beanUtils去实例化
			return BeanUtils.instantiateClass(ctor, args);
		}
		else {
            //如果有覆盖方法,证明出现了代理
            //
			return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
		}
	}

可以看到,这里实例化又分为两种情况

  • 如果有覆盖方法,也就是有replace属性,那就代表要进行代理,交给子类CglibSubclassingInstantiationStrategy策略进行创建,因为Cglib是默认的策略
  • 如果没有覆盖方法,也就是没有代理,交给BeanUtils来进行创建

没有覆盖方法的实例化

没有覆盖方法,就不需要Clb进行代理了,交由BeanUtils来进行实例化

源码如下

	public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
		Assert.notNull(ctor, "Constructor must not be null");
		try {
            //如果传进来的构造器为private类型
            //则设置构造器的的accessible属性为true,让其可以被访问
			ReflectionUtils.makeAccessible(ctor);
            //利用反射来进行创建Bean
			if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
				return KotlinDelegate.instantiateClass(ctor, args);
			}
			else {
                //获取构造器的参数的class类型
				Class<?>[] parameterTypes = ctor.getParameterTypes();
                //断言构造器使用参数数目与传进来的参数数目是否一样
				Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");
				//建立object数组来存放处理后的参数
                Object[] argsWithDefaultValues = new Object[args.length];
                //遍历参数进行整理
				for (int i = 0 ; i < args.length; i++) {
                    //如果有参数为null
					if (args[i] == null) {
                        //获取这个参数的类型
						Class<?> parameterType = parameterTypes[i];
                        //判断这个参数是不是原始类型,即基本数据类型
                        //如果是基本数据类型,从DEFAULT_TYPE_VALUES中取默认值
                        //如果不是基本数据类型,那就为Null
                        //不过一般都不会为null,因为前面对参数就进行处理了
						argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
					}
					else {
                        //如果参数不为null,存进数组中
						argsWithDefaultValues[i] = args[i];
					}
				}
                //使用反射的方法进行创建!!!
				return ctor.newInstance(argsWithDefaultValues);
			}
		}
		catch (InstantiationException ex) {
			throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
		}
		catch (IllegalAccessException ex) {
			throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
		}
		catch (IllegalArgumentException ex) {
			throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
		}
		catch (InvocationTargetException ex) {
			throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
		}
	}

默认值的容器如下
在这里插入图片描述
可以看到里面仅仅只有5种,缺少了浮点型和字符型,那么对于浮点型和字符型为空的情况呢???

可见Spring对于浮点型和字符型是没有提供默认值的,如果来到这里发生浮点型和字符型为null,反射后会报IllegalArgumentException异常

拥有覆盖方法的实例化

从上面可以看到,没有覆盖方法的实例化,其实就是使用反射来创建而已

下面看看如果有覆盖方法的实例化

对应的方法为instantiateWithMethodInjection,寓意为进行切入,也就是代理

在这里插入图片描述
可以看到,代理是交由CglibSubClassCreator去负责的,并且通过构造方法已经将RootBeanDefinition和BeanFactory注入进去了

源码如下

		public Object instantiate(@Nullable Constructor<?> ctor, Object... args) {
            //根据RootBeanDefinition一个增强class
           //所谓的增强Class其实就是代理Class,因为被代理了,所以就是增强嘛
			Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
			Object instance;
            //判断构造器是否为空
			if (ctor == null) {
                //如果构造器为空,直接利用增强class来进行实例化
                //BeanUtils其实就是使用反射去创建嘛,就不赘述了
				instance = BeanUtils.instantiateClass(subclass);
			}
			else {
               	//如果构造器不为空
				try {
                    //利用增强classs去获取构造器
					Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
					//利用构造器的增强class来实例化bean
                    instance = enhancedSubclassConstructor.newInstance(args);
				}
				catch (Exception ex) {
					throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
							"Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
				}
			}
			// SPR-10785: set callbacks directly on the instance instead of in the
			// enhanced class (via the Enhancer) in order to avoid memory leaks.
            //注意这里,这里注释说,将回调直接装在实例上,而不是增强class上,为了避免内存泄漏
			Factory factory = (Factory) instance;
           //给instace装了两个拦截器,
			factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
					new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
					new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
            //返回增强的class,包含了装上的拦截器
			return instance;
		}

总结一下

对于使用构造方法去创建Bean,需要注意以下几点

  • Spring虽然前期已经解析和加载大量数据,并且已经取得了构造器和参数,但并不是直接利用反射去做的,而是要进行判断,该bean需不需要进行增强,也就是是否要进行代理
    • 如果不需要进行代理,直接交由BeanUtils利用反射来实例化出对象出来
    • 如果需要进行代理
      • 首先去根据RootBeanDefinition获取增强的class,即一个Enhancer.class
      • 然后使用反射去创建该Enhancer
      • 给该Enhancer装配两个拦截器
        • LookupOverrideMethodInterceptor:对于Look-up属性的拦截器
        • ReplaceOverrideMethodInterceptor:对于replace属性的拦截器
      • 返回增强的实例Enhancer
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-17 11:52:07  更:2021-10-17 11:54:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 21:48:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码