一、依赖注入
DI(Dependency Injection),Spring Ioc与其说是一种技术,我更愿意承认它是一种思想,这种思想对于设计出松耦合的程序具有非常强的指导意义。其作用主要凸显在两个方面,第一,如何将Bean装配至容器中和如何从容器中获取Bean。第二,如何处理Bean之间的依赖关系。
解决Spring中Bean之间的依赖的实现方式,在Spring的概念中就被称之为依赖注入。通常情况下,我们认为Spring的依赖注入的实现方式有三类,分别是构造方法注入、setter方法注入、注解注入。但是我个人在习惯性上喜欢将前两者归到基于XML注入,然后再进行细分。基于XML注入是最先学习的,所以熟悉度非常高,我们先从这个开始说起。
1.通过构造方法注入
我们首先定义一个服务层UserServiceImpl,然后在内部增加对dao层的引用userDao。
然后我们让spring通过一个构造好的方法public UserServiceImpl给userDao导入实例进行操作。
在所有的阶段结束后,将Bean的实例注入到Spring XML配置文件中。通过构造方法的注入,这意味着一定要注入类中具有对应的构造方法,如果没有,则会出现报错的问题。
2.通过setter方法注入
我们修改一下UserServiceImpl.java,将其变为
然后再将XML文件的内容也修改掉
我们总结一下这两种的区别。首先,UserServiceImpl.java可以不必被限制在添加构造方法这个问题里,但是这也意味着一个无参构造方法必须存在。上文示例中没有的原因在于Java自动会提供无参构造方法以供Bean的生成。其次,XML文件里必须要通过这对标签来注入构造方法,而在setter方法注入时,使用 标签。
在XML注入过程中,可以使用ref=""引用和value=""设定数值,具体成果和用@Value注解差不多。
二、基于注解的依赖注入
三、Autowired
源码:
@Autowired是基于注解的依赖注入的关键点,我们知道,它的源码简单到只有一个参数request(),其作用在于对注入Bean是否有必要进行标注。什么意思呢?就是如果Spring并未找到相应的Bean并且值为true时,就会出现异常,但是如果其值为false就不会异常,如果在使用的过程中容器对Bean不注入,那么空指针异常就会出现。
还有另一个点,源代码之中的@Target里的参数就是基于注解的依赖注入的注入方式。@Target决定了@Autowired其标注的类型。
1.通过构造方法注入
如果以开发文档的情况来说,这种单一构造方法自Spring4.3后就不必一定再有@Autowired标注了,但需要注意的是如果构造方法有多个,则一定要对一个标注@Autowired,不然异常就会出现。
2.通过setter方法注入
3.字段方法注入
4.方法入参注入
这里重点说明一下,其实方法入参注入感觉上和上文说的构造方法、setter方法注入形式的区别并不是很大,相当于将构造方法、setter方法上的注解@Autowired放到入参的位置。我们直接举个例子吧,通过例子看就很明显。
当这个测试方法正常运行后,结果是:
注意这里public UserServiceImpl的入参,UserServiceImpl的字段是userDao,注意是userDao,并不是user。意思就是只要我们需要,就可以在构造中添加任意的参数,至于是否要求这个参数是类中的属性字段,并没有特别的要求。不仅如此,还需要注意,这块儿叙述的方法是构造或者setter方法,并不是任意方法都可以的,类似public void initService自定义的方法是完成不了注入的。
四、@Primary 和 @Qualifier
在刚才的例子仅仅是容器中仅有一个Bean时的情况,如果容器中有多个出现,那么该如何处理这个问题呢?
修改配置类型代码
修改测试类型代码:
如果没有做其余的处理,则
造成这种问题的原因在于有两个User Bean,未注明@Bean时,默认方法名为Bean Name)的存在,所以Spring无法确定使用那个进行注入。
我们这样修改一下,首先在Bean中设置name,当名字能匹配上private User user;的时候即可注入。再者,我们可以改写private User user;比如改写成getUser或getUser2的一个,都可以完成注入,道理是互通的,Spring先按照type匹配,然后再通过名字匹配,如果匹配都失败则会出现异常。
但是在这个之外,Spring提供了两个注解来消除依赖注入时候会产生歧义的情况。
@Primary
@Primary这是一个同类型Bean优先级设定的一个注解,其根本含义在于如果某个类型上添加了@Priamry,如果在注入的过程中没有对Bean进行指定,就会优先注入被表示的Bean。
我们可以用这个为例子,在getUser()里对这个进行对应的注释,这样测试方法就可以正常运行。但是这种方法的并不完善,其问题在于@Priamry可以对很多类使用,假设@Primary标注过了同一个类型的好几个Bean,那么它的效果就没有了。
@Qualifier
真是因为有这样问题的出现,所以@Qualifier这个注解应运而生,直接标在通过@Autowired注入的Bean,然后明确指定注入某个Bean。
非常方便的是@Qualifier可以出现在所有@Autowired可以出现的地方,举个例子:
但凡属于这两类的注释,其实都可以对歧义进行消除,比较推荐@Bean(name="xxx")和@Qualifier(value="xxx")组合使用的方式。假设开发环境中并不存在歧义,那就没有使用的必要了。
总结
我们总结一下,首先来说,Spring是如何实现IoC?Spring是有IoC容器的。之后,再提供了一套依赖注入的机制去帮助IoC容器去协调Bean之间的依赖关系,进而提高对IoC的思想。单一的Bean是不可能全部脱离其余Bean去单独存在,如果一个Bean需要其余Bean引入进而初始化的时候,就需要依赖注入这个机制。
我们举个例子来说,我们假设有一个A想去调用B接口的方法或者说需要B接口的一个实例。我们可以看传统的程序流程。用C类来实现一个接口B,然后再用A创建一个C的实例,然后再实现方法的调用。
在Spring的依赖注入过程中就变成了,A类只需要在自己的内部添加一个注入接口(广义上的接口,不是interface这个接口),这个接口可以是构造方法,也可以是setter方法或者说其他形式;同时添加一个对B接口的引用(private B b;)。
当真正需要生成A类的实例时,Spring IoC容器根据A类提供的接口,为其注入相应的Bean,而这个Bean可以是C类(class C implements B{}),也可以D类(class D implements B{})等等;具体是谁,根据Bean的装配策略和IoC容器中的Bean来确定,不再由开发人员管理。