6. my自动装配看到这里又自然会产生疑问:不会吧,上面可都是自动装配啊,我在配置文件或者使用注解都配置了变量的值,然后加个@autowired注解就ok了,spring也是帮我自动去装配。
再高端一点话,我就把xml文件写成javaconfig配置类,然后使用@configuration注解,这样也能自动装配,这不是很nice了吗?
6.1 自动装配之再思考我的理解,上面的自动装配,我们至少要写一个配置文件,无论是什么形式,我们都至少需要一个文件把它全部写下来,就算这个文件的内容是固定的,但是为了装配这个对象,我们不得不写。
我们甚至都可以做成模板了,比如我在学习spring框架整合时,把经常写的都搞成了模板:
有了这些模板,我们只需要点点点,再进行修改,就能用了。
这样做确实很好,可是对于越来越成型的项目体系,我们每次都搞一些重复动作,是会厌烦的。而且面对这么多xml配置文件,我太难了。
于是我有了一个想说但不敢说的问题:
我一个配置文件都不想写,程序还能照样跑,我只关心有我需要的组件就可以了,我只需要关注我的目标就可以了, 我想打开一个工程之后可以1秒进入开发状态,而不是花3小时写完配置文件(2.5小时找bug) 希望有个东西帮我把开始之前的准备工作全做了,即那些套路化的配置,这样在我接完水之后回来就可以直接进行开发。
说到这里,想必大家都懂了:springboot
6.2 一个例子让我们在偷懒的道路上继续前进。
来看下面这个例子:
仍然是a类和b类,其中a类仍然引用了b类,我们给a类组件起id=“a”,b类组件起id=“b”
@component(a)public class a { @value(我是aaa) private string name; @autowired private b b;}@component(b)public class b { @value(我是bbb) private string name;}可以看到我们使用了@autowired注解来自动注入b,测试类如下:
@testpublic void test(){annotationconfigapplicationcontext ac = new annotationconfigapplicationcontext(myautoconfig.class);a aaa = ac.getbean(a, a.class);system.out.println(aaa);}细心的同学已经发现了:我们这里使用了annotationconfigapplicationcontext这个javaconfig配置类会使用到的加载类,于是我们顺利成章地点开它所加载的myautoconfig类文件
文件内容如下:
@configuration@myenableautoconfigpublic class myautoconfig { // bean 都去哪了 ?}what? 我要声明的bean对象都去哪了(注意:这里的applicationcontext.xml是空的)?
让我们运行test:
a(name=我是aaa, b=b(name=我是bbb))竟然运行成功了,这究竟是为什么?(元芳,你怎么看?)
细心的同学已经发现了:@myenableautoconfig是什么注解?我怎么没有这个注解
让我们点进@myenableautoconfig一探究竟:
@target(elementtype.type)@retention(retentionpolicy.runtime)@documented@import(myimportselector.class) // 导入bean定义public @interface myenableautoconfig {}原来如此!你是用了@import注解导入了bean定义对吧,注释都写着呢!
可是客官,@import导入bean定义是没错,但是它导入的是myimportselector这个bean,不是a也不是b啊…
6.3 @import注解@import的功能就是获取某个类的bean对象,他的使用形式大致如下:
@import(a.class)@import(myimportbeandefinitionregister.class)@import(myimportselector.class)6.3.1 @import(a.class)第一种形式@import(a.class),是最简单易懂的形式
我们需要哪个bean定义,直接import他的class即可
6.3.2 @import(myimportbeandefinitionregister.class)第二种形式@import(myimportbeandefinitionregister.class)
传递了一个bean定义注册器,这个注册器的具体内容如下:
public class myimportbeandefinitionregister implements importbeandefinitionregistrar { @override public void registerbeandefinitions(annotationmetadata importingclassmetadata, beandefinitionregistry registry) { rootbeandefinition adef = new rootbeandefinition(a.class); registry.registerbeandefinition(a, adef); }}这个注册器实现了importbeandefinitionregistrar接口,并且重写了里面的registerbeandefinitions方法
看他做了什么事:创建了一个新的bean定义,他的类型就是a,然后把这个bean定义注册到beandefinitionmap(还记得吧!)里面,key值我们可以人为设置,这里就设置成a
这样在传递一个注册器的时候,我们就可以把注册器中新增的bean定义注册进来使用
6.3.3 @import(myimportselector.class)可以看到,这种使用方式就是我们刚才的注解中使用的方式
他传递了一个叫myimportselector的类,这个类依然是我们自己定义的,具体内容如下:
public class myimportselector implements importselector { @override public string[] selectimports(annotationmetadata importingclassmetadata) { // 导入配置类 return new string[]{config.myconfig}; }}这个类实现了importselector接口,并且重写了selectimports方法,返回一个字符串数组
我们可以看到,返回的字符串数组中是我们要导入类的全类名
这个importer返回的类如果是组件bean对象,就会被加载进来使用;如果是一个配置类,就会加载这个配置类
第三种和第二种的区别是第三种可以一次性写很多类,而且比较简洁,只需要清楚类的全包名即可。而第二种方式需要自己清楚包类名,手动创建bean定义,然后手动加入beandefinitionmap。
6.4 例子的研究我们打开myimportselector,发现里面赫然写着几个大字:
return new string[]{config.myconfig};然后我们找到config.myconfig类,发现这个类竟然就是我们刚才写的javaconfig版本的配置文件:
@configurationpublic class myconfig { @bean public a a(){ return new a(); } @bean public b b(){ return new b(); }}加载这个myconfig配置类,就相当于加载了a和b两个bean定义
喂!你是不是搞我!绕了一大圈,怎么还是加载这个配置文件啊!这个配置文件明明就是我自己写的。
总结一下,我们这个例子大概绕了这些过程:
6.5 将偷懒进行到底没有会偷懒的人解决不掉的问题“ —— 鲁迅
上面的例子也没有多大优化啊,我怎么觉得更加麻烦了?不但绕了一大圈,定义了许多新东西,到最后还是加载了我写好的javaconfig类,说到底我不是还在写javaconfig类吗…
但是你注意到没有:有了上面的机制,我只需要把javaconfig类写一次,然后放在某个地方,在myimportselector中加入这个地方的全包名路径,下次用的时候直接导入最顶层的myautoconfig类,所有有关这个部件我需要的东西,就全部自动整理好了,甚至比鼠标点点点添加代码模板还要快!
我突然有了个很棒的想法,不知道你有了没有 。
如果你开始有点感觉了,就会自然提出另一个问题:我这样做确实可以提高效率,但是一段代码里写入我自己定制的内容,每次更改起来不是太费劲了吗?
想到这里,我就不禁回想起使用jdbc的时候,在代码里改sql语句的痛苦了,那真是生不如死…这种情况就构成了硬编码的行为,是不好的。
我们自然会想到:要是我创建一个配置文件properties来专门保存我这个需求所使用的bean对象,然后使用的时候在myimportselector中读取配置文件并且返回全包名,不就更加nice了吗?
于是myimportselector中的代码又改成了下面这样:
public class myimportselector implements importselector { @override public string[] selectimports(annotationmetadata importingclassmetadata) { properties properties = mypropertyreader.readpropertyforme(/myproperty.properties); string strings = (string) properties.get(myenableautoconfig.class.getname()); return new string[]{strings}; }}其中mypropertyreader是我们自己新创建的用于读取properties文件的工具类
之所以要自己再定义这样一个工具类,是为了以后在其中可以做一些其他操作(比如:去重、预检查)
public class mypropertyreader { public static properties readpropertyforme(string path){ properties properties = new properties(); try(inputstream sin = mypropertyreader.class.getresourceasstream(path)){ properties.load(sin); }catch (ioexception e){ e.printstacktrace(); system.out.println(读取异常...); } return properties; }}我们的配置文件里面这么写:
anno.myenableautoconfig=config.myconfig可以看到,key是注解@myenableautoconfig的类名,也就是根据这个注解,就会导入后面的myconfig这个bean,这个bean就是我们的配置文件
如此一来我们读取这个配置文件,然后加载跟这个注解名称相符的value(即javaconfig配置文件),就相当于我们在代码里手写的config.myconfig,只不过现在的形式已经发生了巨大的变化:我们添加或者删除一个配件,完全只需要修改myproperty.properties这个配置文件就行了!
至此,无论是添加或者删除组件,无非是在配置文件中加上或者删除一行的问题了。
让我们在更新之后运行程序,可以看到成功拿到了配置文件的全类名
程序的运行当然也是没问题的:
a(name=我是aaa, b=b(name=我是bbb))到此,我仿佛又领悟了一些东西。。。
我的配置文件好像活了,在我需要的时候他会出现,在我不需要的时候只需要在配置文件里面给他”打个叉“,他自己就跑开了
电子称量设计电路
ZigBee 3.0推动智能家居领域进步 带来智能家居的大一统体验
浅谈ARM+DSP的嵌入式四轴运动控制器设计
数字技术与能源技术融合将助力电网企业打赢疫情防控阻击战
赵治勋与日本围棋AI DeepZenGo对决,赵治勋险胜!
初学者必看的SpringBoo自动装配原理3
机上WiFi逐渐成为潮流,但价格高问题一时难以解决
2019年三星与华为的全球市场份额差距缩小至3.6%
华为5G随行WiFi系列将于12月10日正式开售
高精密电压源
基于总线协议标准实现DeviceNet适配器的应用设计
Mate10和P20等七款华为老旗舰迎来EMUI10正式版,还不快快升级
腾讯在联合国提议AI应对地球危机 马化腾称“意义非凡”
联通iphone上网设置手册
哪些情况下不能使用万用表或不建议使用万用表
Coway在华启动二次尝试计划瞄准中国空净与净水市场
星恒轻型车锂电池全球累计销量突破1800万组
基于GaN的电机控制器提高电动汽车的效率
OLED有机电视,离我们还有多远?OLED电视价格压缩难 或步等离子后尘
基于区块链技术的社会征信体系借贷链介绍