前言最近在review别人代码的时候,看到@Autowired的一些不同用法,觉得很有意思。我花了一些时间研究它,收获了很多东西,现在分享给大家。也许@Autowired比你想象的更强大。1、@Autowired的默认组装我们都知道spring中的@Autowired注解是用来自动组装对象的。通常,我们在项目中使用这个:packagecom.sue.cache.service;importorg.springframework.stereotype.Service;@ServicepublicclassTestService1{publicvoidtest1(){}}packagecom.sue.cache.service;importorg.springframework.stereotype。Service;@ServicepublicclassTestService2{@AutowiredprivateTestService1testService1;publicvoidtest2(){}}是的,这样就可以拼装成功了,因为spring默认是按照type来拼装的,也就是我们所说的byType。另外@Autowired注解的required参数默认为true,表示开启自动组装。有时我们不想使用自动组装功能,可以将这个参数设置为false。2.当同类型的对象不止一个时,上面的byType方法主要是针对同类型的对象只有一个的情况。此时对象类型是唯一的,可以找到正确的对象。但是如果有多个相同类型的对象会怎样呢?在项目的test目录下,新建一个同名类TestService1:packagecom.sue.cache.service.test;importorg.springframework.stereotype.Service;@ServicepublicclassTestService1{publicvoidtest1(){}}重启项目时:Causedby:org.springframework.context.annotation.ConflictingBeanDefinitionException:Annotation-specifiedbeanname'testService1'forbeanclass[com.sue.cache.service.test.TestService1]conflictswithexisting,non-compatiblebeandefinitionofsamenameandclass[com.sue.cache.service.TestService1]结果是报错,报的类名有冲突,直接导致项目无法启动。注意这种情况不是两个同类型的对象被Autowired导致的,非常容易造成混淆。这是因为spring的@Service方法不允许类名相同,因为spring会将类名的首字母转为小写作为bean名,比如:testService1,默认情况下bean名必须是only。让我们看看如何生成两个相同类型的beans:}@Bean("test2")publicTestService1test2(){returnnewTestService1();}}在TestConfig类中手动创建一个TestService1实例,并去掉TestService1类上原有的@Service注解。重启项目:果然报错,说明testService1是单例,但是找到了两个对象。其实还有一种情况会生成两个相同类型的bean:;}项目重新启动时:报错,提示同上,testService1是单例,但是找到了两个对象。第二种情况在实际项目中出现的比较多。在下面的例子中,我们主要关注第二种情况。3、@Qualifier和@Primary明显在spring中,按照Autowired默认的组装方式:byType,解决不了上面的问题,那么可以改用按名称组装:byName。只需在代码中添加@Qualifier注解:@ServicepublicclassUserService{@Autowired@Qualifier("user1")privateIUseruser;}调整后项目即可正常启动。合格者是指有资格的人。一般与Autowired结合使用。需要指定一个bean名,通过bean名可以找到需要组装的bean。除了上面的@Qualifier注解,@Primary注解也可以用来解决上面的问题。在User1上添加@Primary注解:@Primary@ServicepublicclassUser1implementsIUser{@Overridepublicvoidsay(){}}去掉UserService上的@Qualifier注解:@ServicepublicclassUserService{@AutowiredprivateIUseruser;}重启项目即可正常使用。当我们使用自动配置组装一个bean时,如果这个bean有多个候选,如果其中一个候选有@Primary注解修饰,那么这个候选就会被选为自动配置的值。4、@Autowired的使用范围上面例子中的@Autowired注解是用在成员变量上的,但是@Autowired的威力还远不及。先看@Autowired注解的定义:从图中可以看出,这个注解可以用在5种目标类型上。下面用一张图总结一下:4.1成员变量在成员变量上使用Autowired注解:@ServicepublicclassUserService{@AutowiredprivateIUseruser;}这个方法可能是最常用的。4.2构造函数在构造函数上使用Autowired注解:@ServicepublicclassUserService{privateIUseruser;@AutowiredpublicUserService(IUseruser){this.user=user;System.out.println("user:"+user);}}注意在构造函数上添加Autowired注解实际上使用的是Autowired装配方法,而不是构造函数装配。4.3方法为普通方法添加Autowired注解:@ServicepublicclassUserService{@Autowiredpublicvoidtest(IUseruser){user.say();}}spring会在项目启动过程中自动调用一次带有@Autowired注解的方法,我们可以通过该方法做一些初始化工作。也可以在setter方法中添加Autowired注解:@ServicepublicclassUserService{privateIUseruser;@AutowiredpublicvoidsetUser(IUseruser){this.user=user;}}4.4参数可以在带有Autowired注解的构造函数的参数中添加:@ServicepublicclassUserService{privateIUseruser;publicUserService(@AutowiredIUseruser){this.user=user;System.out.println("user:"+user);}}也可以给非静态方法的入参加上Autowired注解:@ServicepublicclassUserService{publicvoidtest(@AutowiredIUseruser){user.say();}}4.5这种注解的方式其实不太实用,就不过多介绍了。5、@Autowired的高端玩法其实上面的例子都是使用@Autowired来自动组装单个实例,这里我告诉大家它也可以自动组装多个实例。这是怎么回事?调整UserService方法,使用List集合接收IUser类型的参数:@ServicepublicclassUserService{@AutowiredprivateListuserList;@AutowiredprivateSetuserSet;@AutowiredprivateMapuserMap;publicvoidtest(){System.out.println("userList:"+userList);System.out.println("用户集:"+userSet);System.out.println("userMap:"+userMap);}}添加控制器:@RequestMapping("/u")@RestControllerpublicclassUController{@AutowiredprivateUserServiceuserService;@RequestMapping("/test")publicStringtest(){userService.test();return"success";}}调用该接口后:从上图可以看出:userList、userSet、userMap都打印出两个元素,说明@Autowired会自动将同类型的IUser对象收集到一个集合中.你是惊讶还是意外?6、@Autowired一定能组装成功吗?我已经介绍了@Autowired注释的许多很棒的功能。其实在某些情况下,即使用@Autowired组装的对象还是null,这是什么原因呢?什么?6.1如果忘记在没有@Service注解的类上添加@Controller、@Service、@Component、@Repository等注解,spring将无法完成自动组装功能,例如:publicclassUserService{@AutowiredprivateIUseruser;publicvoidtest(){用户。say();}}这种情况应该是最常见的错误,不会因为长得帅就犯这种低级错误。6.2InjectingFilterorListenerWeb应用启动的顺序是:listener->filter->servlet。接下来来看这个例子:publicclassUserFilterimplementsFilter{@AutowiredprivateIUseruser;@Overridepublicvoidinit(FilterConfigfilterConfig)throwsServletException{user.say();}@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{}@Overridepublicvoiddestroy(){}}@ConfigurationpublicclassFilterConfig{@BeanpublicFilterRegistrationBeanfilterRegistrationBean(){FilterRegistrationBeanbean=newFilterRegistrationBean();bean.setFilter(newUserFilter"r"*);/beanPattern().add;returnbean;}}程序启动会报错:tomcat无法正常启动。是什么原因?众所周知,springmvc的启动是在DisptachServlet中完成的,在listener和filter之后执行。如果我们要在listener和filter中@Autowire某个bean,肯定不行,因为在初始化filter的时候,此时bean还没有初始化,无法自动组装。如果我们在工作中真的需要这样做,我们该如何解决这个问题呢?publicclassUserFilterimplementsFilter{privateIUseruser;@Overridepublicvoidinit(FilterConfigfilterConfig)throwsServletException{ApplicationContextapplicationContext=WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());this.user=(applicationContext.getBean("user1"));user.say();}@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{}@Overridepublicvoiddestroy(){}}答案是使用当前的WebApplicationContextUtils.getWebApplication,然后通过它获取bean实例。6.3@ComponentScan未扫描的注解一般情况下,@Controller、@Service、@Component、@Repository、@Configuration等注解需要通过@ComponentScan注解扫描来收集元数据。但是如果不加@ComponentScan注解,或者@ComponentScan注解扫描的路径不正确,或者路径范围太小,有些注解无法采集,后面也无法使用@Autowired完成自动组装功能.好消息是,在springboot项目中,如果使用了@SpringBootApplication注解,它内置了@ComponentScan注解功能。6.4循环依赖问题如果A依赖B,B依赖C,C依赖A,就形成了死循环。spring的bean默认是单例的。如果使用@Autowired组装单例bean,在大多数情况下,循环依赖问题是可以解决的。但是如果bean有多个实例,就会出现循环依赖问题,导致bean无法自动组装。在某些情况下,如果创建了代理对象,即使这个bean是单例的,仍然会存在循环依赖的问题。如果你对循环依赖问题比较感兴趣,也可以看看我的另一篇专题《spring:我是如何解决循环依赖的?》,写的很详细。7、@Autowired和@Resouce的区别虽然@Autowired的功能很强大,但是它也有一些缺点。举个例子:比如和spring强耦合。如果换成JFinal等其他框架,功能就会失效。而@Resource是由JSR-250提供的,它是一个Java标准,大多数框架都支持。另外,有些场景使用@Autowired不能满足需求,改成@Resource可以解决问题。接下来重点说一下@Autowired和@Resource的区别。@Autowired默认为按类型自动装配,而@Resource默认为按名称自动装配。@Autowired只包含一个参数:required,表示是否开启自动接纳,默认为true。而@Resource包含七个参数,其中最重要的两个参数是:名称和类型。@Autowired如果要使用byName,需要配合使用@Qualifier。而@Resource如果指定了name,会自动用byName拼装,如果指定了type,会自动用byType拼装。@Autowired可用于:构造函数、方法、参数、成员变量和注解,而@Resource可用于:类、成员变量和方法。@Autowired是spring定义的注解,而@Resource是JSR-250定义的注解。此外,它们的组装顺序不同。@Autowired的组装顺序如下:@Resource的组装顺序如下:如果name和type都指定了:如果指定了name:如果指定了type:如果既没有指定name也没有指定type:Postscript我本来计划的接下来写@Autowired原理分析和源码解读,但是因为篇幅太长,不适合放在一起,打算以后开个专题。有兴趣的可以继续关注我的后续文章,相信看完后你会有所收获。本文转载自微信公众号“苏三硕科技”,可通过以下二维码关注。转载本文请联系苏三硕科技公众号。