Idea和Spring都不推荐使用Autowired注解,你
前言
最近看到了很多关于Autowired不一样的用法代码,现在将收获到的知识分享给大家。
Autowired比你想象中更强大,它主要作用范围包含构造器、方法、参数、成员变量、注解。
1。Autowired的默认装配
熟悉Spring的都知道Autowired注解,是用来自动装配对象的。
示例如下:packagecom。sue。cache。service;importorg。springframework。stereotype。Service;ServicepublicclassProductService{publicvoidproduct(){无参构造}}packagecom。sue。cache。service;importorg。springframework。stereotype。Service;ServicepublicclassProductService2{Autowired注入对象privateProductServiceproductService;publicvoidproduct2(){午餐构造}}
正常情况下是可以装配成功的,都知道spring默认按照类型注入的(即byType方式)。
看过底层源码的应该知道,Autowired注解有一个required参数,其值默认为true(默认开启自动装配),如果不想使用自动装配可将该参数设置成false。2。多个相同类型对象如何处理的?
可以设想一下,如果多个相同类型的对象,再用Autowired来注入这同类型的对象会发生什么?
项目新建一个目录,创建一个同名的类ProductService。示例如下:packagecom。sue。cache。service。test;importorg。springframework。stereotype。Service;ServicepublicclassProductService{publicvoidproduct(){}}
再启动项目时,抛出如下的异常:Causedby:org。springframework。context。annotation。ConflictingBeanDefinitionException:AnnotationspecifiedbeannametestService1forbeanclass〔com。sue。cache。service。test。TestService1〕conflictswithexisting,noncompatiblebeandefinitionofsamenameandclass〔com。sue。cache。service。ProductService〕
这个异常时告诉我们类名称有冲突,然后项目嗝屁了,起不来了。注意:
其实这并不是因为Autowired注入了两个同类型的对象导致的。其实是spring中Service注解不允许有同名的类,spring自动把类首字母大写转为小写作为它管理的bean名称,默认情况下bean名称必须是唯一的。
如何产生两个相同的类型bean?:publicclassProductService1{publicvoidproduct1(){}}ServicepublicclassProductService2{AutowiredprivateProductService1productService1;publicvoidproduct2(){}}ConfigurationpublicclassProductConfig{Bean(productService1)publicProductService1productService1(){returnnewProductService1();}Bean(productService2)publicProductService1productService2(){returnnewProductService1();}}
在ProductConfig类中手动创建ProductService1实例。
重启项目抛出异常:
抛出异常,提示productService1是单例的,却找到两个实例。
两个类实现同一个接口后,另一个类Autowired这个接口,也会产生两个相同的类型bean异常,实例如下:publicinterfaceIUser{voidsay();}ServicepublicclassUser1implementsIUser{Overridepublicvoidsay(){}}ServicepublicclassUser2implementsIUser{Overridepublicvoidsay(){}}ServicepublicclassUserService{AutowiredprivateIUseruser;}
项目重启抛出异常:
和上面一样的配出同样的异常信息。
不难看出,实际项目中第二种情况在我们实际项目中遇到的更多!3。Qualifier、Primary
当然在项目中用Autowired装配对象时,是解决不了上面的问题。这是就是spring的强大之处了,spring还给我们提供了改用按名称装配实例。
此时用AutowiredQualifier(beanName)可以完美解决上述问题:ServicepublicclassUserService{AutowiredQualifier(user1)privateIUseruser;}
调整之后,再重启项目就ok了。
Qualifier一般都是和Autowired结合使用,通过Qualifier的参数指定一个bean的名称,实现多个同对象的装配。
Spring还给我们提供了Primary注解解决上面的问题。在一个对象上加上Primary注解:PrimaryServicepublicclassUser1implementsIUser{Overridepublicvoidsay(){}}
此时另一个对象只使用Autowired注解即可:ServicepublicclassUserService{AutowiredprivateIUseruser;}
重启项目,一样解决了问题。当用Autowired自动装配对象时,有多个同名对象,其中一个使用Primary注解修饰,就会把该对象作为候选者被选中,来作为自动配置的值。
注:其实项目中这种方式不是很常用。4。Autowired作用范围
下面我们聊一聊Autowired注解除了作用在成员变量上,还有哪些作用范围?
看看源码中Autowired注解的定义:
不难看出Autowired作用在5种目标类型上,图例总结一下:
接下来,我们重点看看在其他地方该怎么用?4。1成员变量
Autowired作用在成员变量上:ServicepublicclassUserService{AutowiredprivateIUseruser;}4。2构造器
Autowired作用在构造器上:ServicepublicclassUserService{privateIUseruser;AutowiredpublicUserService(IUseruser){this。useruser;System。out。println(user:user);}}
注意:Autowired作用在构造器上,实际仍然用Autowired装配方式,并非构造器装配。4。3方法
Autowired作用在普通方法上:ServicepublicclassUserService{Autowiredpublicvoidtest(IUseruser){user。say();}}
项目启动中,Spring自动调用一次加了Autowired的方法此时可以在该方法内做一些初始化工作。
Autowired也可以作用在setter方法上:ServicepublicclassUserService{privateIUseruser;AutowiredpublicvoidsetUser(IUseruser){this。useruser;}}4。4参数
Autowired作用在构造器的入参上:ServicepublicclassUserService{privateIUseruser;publicUserService(AutowiredIUseruser){this。useruser;System。out。println(user:user);}}
Autowired作用在非静态方法的入参上:ServicepublicclassUserService{publicvoidtest(AutowiredIUseruser){user。say();}}4。5注解
基本没人用,偷个懒不介绍了5。Autowired的高级玩法
上面几种Autowired都是自动装配单实例,其实它也能自动装配多个实例,惊喜补,怎么回事呢?
把UserService成员变量变通一下,用List集合接收IUser类型的参数:ServicepublicclassUserService{AutowiredprivateListIUseruserList;AutowiredprivateSetIUseruserSet;AutowiredprivateMapString,IUseruserMap;publicvoidtest(){System。out。println(userList:userList);System。out。println(userSet:userSet);System。out。println(userMap:userMap);}}
创建一个UController:RequestMapping(u)RestControllerpublicclassUController{AutowiredprivateUserServiceuserService;RequestMapping(test)publicStringtest(){userService。test();returnsuccess;}}
启动项目并调用接口:
控制台中:userList、userSet和userMap都分别打印出来了各自的元素,并没有抛出异常,说明Autowired会自动把相同类型的IUser对象收集到集合中。6。Autowired一定能装配成功?
有时候就算用了Autowired装配的对象结果还是null,这又是怎么回事呢?6。1不打Service注解
在类上面不加Controller、Service、Component、Repository等注解,这种情况下Spring就不能完成对象的装配,示例如下:publicclassUserService{AutowiredprivateIUseruser;publicvoidtest(){user。say();}}6。2注入Filter或Listener(装配过滤器或监听器)
web项目启动的顺序:listenerfilterservlet。
示例如下:publicclassUserFilterimplementsFilter{AutowiredprivateIUseruser;Overridepublicvoidinit(FilterConfigfilterConfig)throwsServletException{user。say();}OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{}Overridepublicvoiddestroy(){}}ConfigurationpublicclassFilterConfig{BeanpublicFilterRegistrationBeanfilterRegistrationBean(){FilterRegistrationBeanbeannewFilterRegistrationBean();bean。setFilter(newUserFilter());bean。addUrlPatterns();returnbean;}}
此时我们启动项目一样会抛出异常,并告诉我们tomcat不能正常启动:
这又是什么原因呢?
Springmvc的启动主要依靠DisptachServlet(常说的核心处理器),而核心处理器是在listener和filter之后执行的。那么此时我们在监听器或者过滤器里面用Autowired装配某个对象时,因此此时核心处理器还没执行,bean还没有初始化,自然无法完成自动注入的。
下面我们讲一讲项目中遇到这种情况该怎么处理呢?publicclassUserFilterimplementsFilter{privateIUseruser;Overridepublicvoidinit(FilterConfigfilterConfig)throwsServletException{ApplicationContextapplicationContextWebApplicationContextUtils。getWebApplicationContext(filterConfig。getServletContext());this。user((IUser)(applicationContext。getBean(user1)));user。say();}OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{}Overridepublicvoiddestroy(){}}
这里用到了WebApplicationContextUtils。getWebApplicationContext获取当前的ApplicationContext,再通过它获取到bean实例(其实这里实际用到的是Java里面的反射来完成的,小伙伴们可以了解一下)。6。3注解未被ComponentScan扫描
Spring中Component、Repository、Controller、Service、Configuration等注解,是需要通过ComponentScan注解扫描,收集元数据的。
试想一下,不加ComponentScan,或配置的ComponentScan扫描的包路径错误,或包路径范围太小,此时也会导致有些注解的元数据无法收集,最后导致Autowired不能自动注入bean。
当然Springboot项目中就不存在此类问题,Springboot的SpringBootApplication注解,已经内置了ComponentScan注解。6。4循环依赖问题
循环依赖,相信伙伴们都遇到过吧,这里简单说一下,如果A依赖于B,B依赖于C,C又依赖于A,就形成了死循环依赖。
Spring的bean默认是单例的,单例对象用Autowired自动注入,大多数情况能解决循环依赖问题。
那么当bean是多例的情况下,就会出现循环依赖,导致bean无法自动注入。7。Autowired和Resouce的区别
有时候Autowiredw无法解决,改成Resource却能解决问题。接下来,我们重点看看Autowired和Resource的区别。Autowired默认按类型自动注入,而Resource默认按名称自动注入。Autowired只有一个参数:required,意思是是否开启自动注入,默认是true。而Resource有七个参数,最重要的两个参数:name和type。AutowiredQualifier结合使用也可以实现按名称注入。而Resource如果指定了名称,则按名称自动注入,如果指定了类型,则按照自动注入。Autowired可用在:构造器、方法、参数、成员变量和注解上,而Resource能用在:类、成员变量和方法上。Autowired是spring定义的注解,而Resource是JSR250定义的注解。Autowired和Resource的装配顺序不同。
Autowired的装配顺序如下:
Resource的装配顺序如下:
1。如果同时指定了name和type
2。如果指定了name
3。如果指定了type
4。如果既没有指定name,也没有指定type:
关注公众号:编程怪咖,学习更多知识。