spring的注入依赖set方法吗(spring如何注入类)

技术Spring中@Value注入复杂类型怎么用这篇文章主要为大家展示了“Spring中@Value注入复杂类型怎么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Sprin

这篇文章主要为大家展示了"春天中@值注入复杂类型怎么用",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"春天中@值注入复杂类型怎么用"这篇文章吧。

为什么用,分割的字符串可以注入数组?于是我就去一步一步的断点去走了一遍@值注入属性的过程,才发现了根本原因。

@值不支持复杂类型封装(数组、地图、对象等)这个说法确实是有问题的,不够严谨,因为在特殊情况下,是可以注入复杂类型的。

先来梳理一下@Value对属性的注入流程

先交代一下我们的代码:

一个yml文件a.yml

测试:a、b、c、d一个憨豆A.java

@组件

@属性源(值={ ' class path : a . yml ' },ignoreResourceNotFound=true,编码='utf-8 ')

publicclassA{

@Value('${test} ')

privateString[]测试;

公共void测试(){ 0

系统。出去。println(' test : '数组。tostring(test));

System.out.println('长度:'测试。长度);

}

}main方法:

@配置

@ComponentScan('com.kinyang ')

publicclassHelloApp{

publicationstativitmain(String[]args){ 0

AnnotationConfigApplicationContextac=new annocationconfigapplicationcontext(hello app。类);

一个bean=AC。GetBean(一个. class);

比恩。test();

}

}好的!下面开始分析

1、从AutowiredAnnotationBeanPostProcessor后置处理说起

过多的春天初始化豆的流程就不说了,我们直接定位到豆的属性注入的后置处理器autowiredannotationbeanstreator。

此类中的处理部分()方法中完成了豆中@自动连线、@注入、@值注解的解析并注入的功能。

此方法中完成了豆中@自动连线、@注入、@值注解的解析并注入的功能

public void processing inject(ObjectBean)引发了一个creationexception {

上课?clazz=bean。GetClass();

///找到类上所有的需要自动注入的元素

//(把@自动连线、@注入、@值注解的字段和方法包装成注射数据类的对象返回)

injectionmetadata元数据=finautowingmetadata(clazz。getname(),clazz,null);

尝试{

metadata.inject(bean,null,null);

}

catch(bean creation exceptionex){ 0

throwex

}

catch(Throwableex){ 0

thrownewBeanCreationException(

injectionfautowiredependencies failed or class [' nbs

p;+ clazz + "]", ex);
}
}

2、接着进入InjectionMetadata的inject()方法

inject()方法就是一个循环上面一步解析出来的注解信息,注解的方法或者字段包装后的对象是InjectedElement类型的类,InjectedElement是一个抽象类,他的实现主要有两个:对注解字段生成的是AutowiredFieldElement类,对注解方法生成的是AutowiredMethodElement类。

我们这里只分析@Value注解字段的注入流程,所以下一步会进到AutowiredFieldElement类的inject()方法.

此方法就两大步骤:

  • 获取要注入的value

  • 通过反射,把值去set字段上

其中获取要注入的value过程比较复杂,第二步set值就两行代码搞定

具体逻辑看下面代码上我写的注释

		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			Field field = (Field) this.member;
			Object value;
			if (this.cached) {
			/// 优先从缓存中获取
				value = resolvedCachedArgument(beanName, this.cachedFieldValue);
			}
			else {
			///缓存中没有的话,走下面的逻辑处理
				DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
				desc.setContainingClass(bean.getClass());
				Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
				Assert.state(beanFactory != null, "No BeanFactory available");
				  这个对我们今天讨论的问题很关键
				  获取一个 类型转换器
				TypeConverter typeConverter = beanFactory.getTypeConverter();
				try {
					///   获取值(重点,这里把一个TypeConverter传进去了)
					value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
					///  经过上面的方法返回来的 value 就是要注入的值了
					///  通过断点调试,我们可以发现我们在配置文件yml中配置的 “a,b,c,d”字符串已经变成了一个String[]数组
				}
				catch (BeansException ex) {
					throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
				}
				synchronized (this) {
					..... 
					这里不是我们本次讨论的重点所以就去掉了
				}
			}
			if (value != null) {
			 这里就是第二步,赋值
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}
	}

从上面代码来看,所有重点就都落到了这行代码

value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

推断下来resolveDependency方法里应该是读取配置文件字符串,然后将字符串用,分割转换了数组。

那么具体怎么转换的呢?我们继续跟进!

进入resolveDependency()方法,里面逻辑很简单做了一些判断,真正实现其实是doResolveDependency()方法,进行跟进。

根据@Value注解,从配置文件a.yml中解析出配置的内容:“a,b,c,d”

Spring中@Value注入复杂类型怎么用

Spring中@Value注入复杂类型怎么用

到这里我们得到值还是配置文件配置的字符串,并没有变成我们想要的String[]字符串数组类型。

我们继续往下走,下面是获取一个TypeConverter类型转换器,这里的类型转换器是上面传进来的,具体类型SimpleTypeConverter类。

然后通过这个类型转换器的convertIfNecessary方法把,我们的字符串"a,b,c,d"转换成了String[]数组。

Spring中@Value注入复杂类型怎么用

所以我们现在知道了,我们从配置文件获取到的值,通过了Spring转换器,调用了convertIfNecessary方法后,进行了类型自动转换。那么这转换器到底是怎么进行工作的呢?

继续研究~~

那接下来要研究的就是Spring的TypeConverter的工作原理问题了

首先我们这里知道了外面传进来的那个转换器是一个叫SimpleTypeConverter 的转换器。

这转换器是org.springframework.beans.factory.support.AbstractBeanFactory#getTypeConverter方法得到的

	@Override
	public TypeConverter getTypeConverter() {
		TypeConverter customConverter = getCustomTypeConverter();
		if (customConverter != null) {
			return customConverter;
		}
		else {
		/// 如果没有 用户自定的TypeConverter 那就用 默认的SimpleTypeConverter吧
			// Build default TypeConverter, registering custom editors.
			SimpleTypeConverter typeConverter = new SimpleTypeConverter();
			 注册一些默认的ConversionService
			typeConverter.setConversionService(getConversionService());
			  再注册一些默认的CustomEditors
			registerCustomEditors(typeConverter);
			return typeConverter;
		}
	}

默认的SimpleTypeConverter里面注册了一些转换器,从debug过程我们可以看到默认是注入了12个PropertyEditor

Spring中@Value注入复杂类型怎么用

这12个PropertyEditor是在哪注入的呢?大家可以看registerCustomEditors(typeConverter)方法,这里就不展开了,我直接说了,是通过ResourceEditorRegistrar类注入进去的。

@Override
	public void registerCustomEditors(PropertyEditorRegistry registry) {
		ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
		doRegisterEditor(registry, Resource.class, baseEditor);
		doRegisterEditor(registry, ContextResource.class, baseEditor);
		doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
		doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
		doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
		doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
		doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
		doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));

		ClassLoader classLoader = this.resourceLoader.getClassLoader();
		doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
		doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
		doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));

		if (this.resourceLoader instanceof ResourcePatternResolver) {
			doRegisterEditor(registry, Resource[].class,
					new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
		}
	}

现在我们回到 SimpleTypeConverter 的convertIfNecessary方法里去,这个方法其实是SimpleTypeConverter的父类TypeConverterSupport的方法,而这个父类方法里调用的又是TypeConverterDelegate类的convertIfNecessary方法(一个比一个懒,哈哈哈就是自己不干活)

最后我们重点来分析TypeConverterDelegate的convertIfNecessary方法。

这个方法内容比较多,但是整体思路就是 根据最后想转换的类型,选择出对应的PropertyEditor或者ConversionService,然后进行类型转换。

从上面的看的注入的12个PropertyEditor中,我们就可以看出来了,我们匹配到的是

这行代码doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));注入的ClassArrayEditor。

所以我ClassArrayEditor这个类就可以了,这个类就很简单了,主要看setAsText方法

public void setAsText(String text) throws IllegalArgumentException {
		if (StringUtils.hasText(text)) {
			///  这里通过StringUtils 把字符串,转换成 String数组
			String[] classNames = StringUtils.commaDelimitedListToStringArray(text);
			Class<?>[] classes = new Class<?>[classNames.length];
			for (int i = 0; i < classNames.length; i++) {
				String className = classNames[i].trim();
				classes[i] = ClassUtils.resolveClassName(className, this.classLoader);
			}
			setValue(classes);
		}
		else {
			setValue(null);
		}
	}

这个方法里通过

Spring的字符串工具类StringUtils的commaDelimitedListToStringArray(text)方法把字符串转换成了数组,方法里就是通过 “,” 进行分割的。

到此为止,我们知道了@Value为什么可以把“,”分割的字符串注册到数组中了吧。

其实@Value可以注入URI、Class、File、Resource等等类型,@Value可以注入什么类型完全取决于能不能找到处理 String 到 注入类型的转换器。

上面列出来的12个其实不是全部默认的,系统还有47个其他的转换器,只不过是上面的12个优先级比较高而已,其实还有下面的40多个转换器,所以你看@Value可以注入的类型还会很多的。

private void createDefaultEditors() {
		this.defaultEditors = new HashMap<>(64);

		// Simple editors, without parameterization capabilities.
		// The JDK does not contain a default editor for any of these target types.
		this.defaultEditors.put(Charset.class, new CharsetEditor());
		this.defaultEditors.put(Class.class, new ClassEditor());
		this.defaultEditors.put(Class[].class, new ClassArrayEditor());
		this.defaultEditors.put(Currency.class, new CurrencyEditor());
		this.defaultEditors.put(File.class, new FileEditor());
		this.defaultEditors.put(InputStream.class, new InputStreamEditor());
		this.defaultEditors.put(InputSource.class, new InputSourceEditor());
		this.defaultEditors.put(Locale.class, new LocaleEditor());
		this.defaultEditors.put(Path.class, new PathEditor());
		this.defaultEditors.put(Pattern.class, new PatternEditor());
		this.defaultEditors.put(Properties.class, new PropertiesEditor());
		this.defaultEditors.put(Reader.class, new ReaderEditor());
		this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
		this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
		this.defaultEditors.put(URI.class, new URIEditor());
		this.defaultEditors.put(URL.class, new URLEditor());
		this.defaultEditors.put(UUID.class, new UUIDEditor());
		this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());

		// Default instances of collection editors.
		// Can be overridden by registering custom instances of those as custom editors.
		this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
		this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
		this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
		this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
		this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

		// Default editors for primitive arrays.
		this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
		this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

		// The JDK does not contain a default editor for char!
		this.defaultEditors.put(char.class, new CharacterEditor(false));
		this.defaultEditors.put(Character.class, new CharacterEditor(true));

		// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
		this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
		this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

		// The JDK does not contain default editors for number wrapper types!
		// Override JDK primitive number editors with our own CustomNumberEditor.
		this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
		this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
		this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
		this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
		this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
		this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
		this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
		this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
		this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
		this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
		this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
		this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
		this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
		this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));

		// Only register config value editors if explicitly requested.
		if (this.configValueEditorsActive) {
			StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
			this.defaultEditors.put(String[].class, sae);
			this.defaultEditors.put(short[].class, sae);
			this.defaultEditors.put(int[].class, sae);
			this.defaultEditors.put(long[].class, sae);
		}
	}

重点来了,分析了这么久了,那么,如果我们想注册一个我们自定义的类该如何操作呢???

好了,既然知道了@Value的注入的原理和中间类型转换的过程,那我们就知道该从哪里下手了,那就是写一个我们自己的PropertyEditor,然后注册到Spring的类型转换器中。

先明确一下我们的需求,就是在yml配置文件中,配置字符串,然后通过@Value注入为一个自定义的对象。

我们的自定义对象 Car.java

public class Car {
	private String color;
	private String name;
	// 省略 get set方法
}

yml配置文件,配置car: 红色|法拉利,我们这里用|分割

test: a,b,c,d
car: 红色|法拉利

用于测试的Bean A.java

@Component
@PropertySource(value = {"classpath:a.yml"},ignoreResourceNotFound = true, encoding = "utf-8")
public class A {
	@Value("${test}")
	private String[] test;
	@Value("${car}")
	private Car car;
	public void test(){
		System.out.println("test:"+Arrays.toString(test));
		System.out.println("长度:"+test.length);

		System.out.println("自定的Car 居然通过@Value注册成功了");
		System.out.println(car.toString());
	}
}

下面就是写我们的PropertyEditor然后注册到Spring的Spring的类型转换器中。

  • 自定义 一个 propertyEditor类:CarPropertyEditor,

  • 这里不要直接去实现PropertyEditor接口,那样太麻烦了,因为有很多接口要实现

  • 我们这里通过继承PropertyEditorSupport类,通过覆盖关键方法来做

  • 主要是两个方法 setAsText 和 getAsText 方法

/**
 * @author KinYang.Lau
 * @date 2020/12/18 11:00 上午
 *
 * 自定义 一个 propertyEditor,
 * 这里不要直接去实现PropertyEditor接口,那样太麻烦了,因为有很多接口要实现
 * 我们这里通过继承PropertyEditorSupport类,通过覆盖关键方法来做
 * 主要是两个方法 setAsText 和 getAsText 方法
 */
public class CarPropertyEditor extends PropertyEditorSupport {
	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		///  这实现我们的 字符串 转 自定义对象的 逻辑
		if (StringUtils.hasText(text)) {
			String[] split = text.split("\\|");

			Car car = new Car();
			car.setColor(split[0]);
			car.setName(split[1]);
			setValue(car);
		}
		else {
			setValue(null);
		}
	}

	@Override
	public String getAsText() {
		Car value = (Car) getValue();
		return (value != null ? value.toString() : "");
	}
}

那么如何注册到Spring的Spring的类型转换器中呢?

这个也简单,ConfigurableBeanFactory 接口有一个

void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass);方法就是用于注册CustomEditor的。

所以我们写一个BeanFactory的后置处理器就可以了。

/**
 * @author KinYang.Lau
 * @date 2020/12/18 10:54 上午
 */
@Component
public class MyCustomEditorConfigurer implements BeanFactoryPostProcessor, Ordered {
	private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	///  把我们自定义的 转换器器注册进去
		beanFactory.registerCustomEditor(Car.class, CarPropertyEditor.class);
	}

	@Override
	public int getOrder() {
		return this.order;
	}
}

下面我运行一下程序,看看结果吧:

@Configuration
@ComponentScan("com.kinyang")
public class HelloApp {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(HelloApp.class);
		A bean = ac.getBean(A.class);
		bean.test();
	}
}

Spring中@Value注入复杂类型怎么用

搞定!!!

以上是“Spring中@Value注入复杂类型怎么用”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/148252.html

(0)

相关推荐

  • mysql中自增id的示例分析

    技术mysql中自增id的示例分析这篇文章主要介绍了mysql中自增id的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。对于auto_increm

    攻略 2021年11月6日
  • 精益求精是什么意思,精益求精精益求精的精是什么意思

    技术精益求精是什么意思,精益求精精益求精的精是什么意思一、意思是:(学术、技术、作品、产品等)好了还求更好精益求精是什么意思。二、引证:毛泽东《纪念白求恩》:白求恩同志是个医生,他以医疗为职业,对技术~;在整个八路军医务

    生活 2021年10月21日
  • 形容颜色的四字词语,表示颜色的四字成语有哪些

    技术形容颜色的四字词语,表示颜色的四字成语有哪些花花绿绿形容颜色的四字词语、万紫千红、五光十色、五彩缤纷、五颜六色一、花花绿绿白话释义:状态词。形容颜色鲜艳多彩
    朝代:金
    作者:元好问
    出处:《又解嘲二首》:“凭君细数东

    生活 2021年10月26日
  • 数据库分库分表之后该如何解决事务问题

    技术数据库分库分表之后该如何解决事务问题今天就跟大家聊聊有关数据库分库分表之后该如何解决事务问题,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、概述随着时间和业

    攻略 2021年12月1日
  • 如何理解Elasticsearch倒排索引与分词

    技术如何理解Elasticsearch倒排索引与分词本篇内容主要讲解“如何理解Elasticsearch倒排索引与分词”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解E

    攻略 2021年10月23日
  • spark运行模式(spark的主要功能是什么)

    技术Spark2.4.0有什么功能这篇文章给大家分享的是有关Spark2.4.0有什么功能的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。SparkCore 和 SQL 增加了Barrier E

    攻略 2021年12月16日