xml 设值注入举例ref_切片式详解Spring IoC的依赖注入,我沸腾了

一、依赖注入

DI(Dependency Injection),Spring Ioc与其说是一种技术,我更愿意承认它是一种思想,这种思想对于设计出松耦合的程序具有非常强的指导意义。其作用主要凸显在两个方面,第一,如何将Bean装配至容器中和如何从容器中获取Bean。第二,如何处理Bean之间的依赖关系。

09e60e3579e243338c1b61d2547ecbe0

解决Spring中Bean之间的依赖的实现方式,在Spring的概念中就被称之为依赖注入。通常情况下,我们认为Spring的依赖注入的实现方式有三类,分别是构造方法注入、setter方法注入、注解注入。但是我个人在习惯性上喜欢将前两者归到基于XML注入,然后再进行细分。基于XML注入是最先学习的,所以熟悉度非常高,我们先从这个开始说起。

24b7ffe8d8254d7dbe8de26eb627a98b

1.通过构造方法注入

public class UserServiceImpl implements UserService {      private UserDao userDao;      public UserServiceImpl(UserDao userDao) {         this.userDao = userDao;     }      /**继承自UserService的方法**/ } 

我们首先定义一个服务层UserServiceImpl,然后在内部增加对dao层的引用userDao。

然后我们让spring通过一个构造好的方法public UserServiceImpl给userDao导入实例进行操作。

 

在所有的阶段结束后,将Bean的实例注入到Spring XML配置文件中。通过构造方法的注入,这意味着一定要注入类中具有对应的构造方法,如果没有,则会出现报错的问题。

2.通过setter方法注入

我们修改一下UserServiceImpl.java,将其变为

public class UserServiceImpl implements UserService {      private UserDao userDao;      public void setUserDao(UserDao userDao) {         this.userDao = userDao;     }      /**继承自UserService的方法**/ } 

然后再将XML文件的内容也修改掉

 

我们总结一下这两种的区别。首先,UserServiceImpl.java可以不必被限制在添加构造方法这个问题里,但是这也意味着一个无参构造方法必须存在。上文示例中没有的原因在于Java自动会提供无参构造方法以供Bean的生成。其次,XML文件里必须要通过这对标签来注入构造方法,而在setter方法注入时,使用 标签。

在XML注入过程中,可以使用ref=""引用和value=""设定数值,具体成果和用@Value注解差不多。

二、基于注解的依赖注入

三、Autowired

源码:

@Target({ElementType.CONSTRUCTOR,          ElementType.METHOD,          ElementType.PARAMETER,          ElementType.FIELD,          ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired {     boolean required() default true; } 

@Autowired是基于注解的依赖注入的关键点,我们知道,它的源码简单到只有一个参数request(),其作用在于对注入Bean是否有必要进行标注。什么意思呢?就是如果Spring并未找到相应的Bean并且值为true时,就会出现异常,但是如果其值为false就不会异常,如果在使用的过程中容器对Bean不注入,那么空指针异常就会出现。

还有另一个点,源代码之中的@Target里的参数就是基于注解的依赖注入的注入方式。@Target决定了@Autowired其标注的类型。

1.通过构造方法注入

@Service("userService") public class UserServiceImpl implements UserService {      private UserDao userDao;      @Autowired     public UserServiceImpl(UserDao userDao) {         this.userDao = userDao;     }     /**继承自UserService的方法**/ } 

如果以开发文档的情况来说,这种单一构造方法自Spring4.3后就不必一定再有@Autowired标注了,但需要注意的是如果构造方法有多个,则一定要对一个标注@Autowired,不然异常就会出现。

2.通过setter方法注入

@Service("userService") public class UserServiceImpl implements UserService {      private UserDao userDao;      @Autowired     public void setUserDao(UserDao userDao) {         this.userDao = userDao;     }     /**继承自UserService的方法**/ } 

3.字段方法注入

@Service("userService") public class UserServiceImpl implements UserService {      @Autowired     private UserDao userDao;      /**继承自UserService的方法**/ } 

4.方法入参注入

这里重点说明一下,其实方法入参注入感觉上和上文说的构造方法、setter方法注入形式的区别并不是很大,相当于将构造方法、setter方法上的注解@Autowired放到入参的位置。我们直接举个例子吧,通过例子看就很明显。

@Component public class UserDaoImpl implements UserDao {     //简单返回一个User,模拟数据库查找过程     @Override     public User getUser(Long id, String name){         User user = new User();         user.setId(id);         user.setName(name);         user.setAccount("12345678911");         user.setPassword("******");         user.setOtherInfo("this is a test account");         return user;     } } 
//UserService类 @Service("userService") public class UserServiceImpl implements UserService {      private UserDao userDao;     public UserServiceImpl(@Autowired UserDao userDao,                           @Autowired User user) {        System.out.println("UserServiceImpl: "+user);        this.userDao = userDao;    }      @Override     public User getUser(Long id, String name){         return userDao.getUser(id,name);     } } 
//简单的配置类 //作用就是为标有@Componet(@Service也算)注解的类 生成Bean //同时 为@Autowired标识下的Bean(对象) 注入实例 @Configuration @ComponentScan public class DIConfig {      //用于Service类中入参user的注入     @Bean     public User getUser(){         User u = new User();         u.setName("user inject into service");         return u;     } } 
//测试类 //注意:使用JUnit4测试时,如果需要使用@Autowired注入那么必须添加 //@RunWith    标注使用Spring方式启动(或者SpringBootRunner) //@ContextConfiguration  扫描配置类 @RunWith(SpringRunner.class) @ContextConfiguration(classes = DIConfig.class) public class DITest {      //如果不添加测试类上两个注解,会注入失败     @Autowired     private UserService userService;      @Test     public void testAutowired(){ System.out.println(userService.getUser(1L,"name"));     } } 

当这个测试方法正常运行后,结果是:

3b014c6705094114a48b96df4268b5e3
cac9e9abac35468fa81d487a960c07fe

注意这里public UserServiceImpl的入参,UserServiceImpl的字段是userDao,注意是userDao,并不是user。意思就是只要我们需要,就可以在构造中添加任意的参数,至于是否要求这个参数是类中的属性字段,并没有特别的要求。不仅如此,还需要注意,这块儿叙述的方法是构造或者setter方法,并不是任意方法都可以的,类似public void initService自定义的方法是完成不了注入的。

b8814fdcd24d453b9b452b548ee6bd44

四、@Primary 和 @Qualifier

在刚才的例子仅仅是容器中仅有一个Bean时的情况,如果容器中有多个出现,那么该如何处理这个问题呢?

修改配置类型代码

@Configuration @ComponentScan public class DIConfig {      @Bean     public User getUser(){         User u = new User();         u.setName("this is user");         return u;     }      @Bean     public User getUser2(){         User u = new User();         u.setName("this is user2");         return u;     } } 

修改测试类型代码:

@RunWith(SpringRunner.class) @ContextConfiguration(classes = DIConfig.class) public class DITest {      @Autowired     private User user;      @Test     public void testAutowiredPriamry(){         System.out.println(user);     } } 

如果没有做其余的处理,则

66b93867f5954993aa24f54a94b75737

造成这种问题的原因在于有两个User Bean,未注明@Bean时,默认方法名为Bean Name)的存在,所以Spring无法确定使用那个进行注入。

我们这样修改一下,首先在Bean中设置name,当名字能匹配上private User user;的时候即可注入。再者,我们可以改写private User user;比如改写成getUser或getUser2的一个,都可以完成注入,道理是互通的,Spring先按照type匹配,然后再通过名字匹配,如果匹配都失败则会出现异常。

但是在这个之外,Spring提供了两个注解来消除依赖注入时候会产生歧义的情况。

@Primary

@Target({ElementType.TYPE,    // 类、接口、枚举类型          ElementType.METHOD})// 方法 @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Primary { } 

@Primary这是一个同类型Bean优先级设定的一个注解,其根本含义在于如果某个类型上添加了@Priamry,如果在注入的过程中没有对Bean进行指定,就会优先注入被表示的Bean。

@Configuration @ComponentScan public class DIConfig {      @Primary     @Bean     public User getUser(){         User u = new User();         u.setName("this is user");         return u;     }      @Bean     public User getUser2(){         User u = new User();         u.setName("this is user2");         return u;     } } 

我们可以用这个为例子,在getUser()里对这个进行对应的注释,这样测试方法就可以正常运行。但是这种方法的并不完善,其问题在于@Priamry可以对很多类使用,假设@Primary标注过了同一个类型的好几个Bean,那么它的效果就没有了。

@Qualifier

真是因为有这样问题的出现,所以@Qualifier这个注解应运而生,直接标在通过@Autowired注入的Bean,然后明确指定注入某个Bean。

@Target({ElementType.FIELD,           ElementType.METHOD,           ElementType.PARAMETER,           ElementType.TYPE,           ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Qualifier {     String value() default ""; } 

非常方便的是@Qualifier可以出现在所有@Autowired可以出现的地方,举个例子:

@RunWith(SpringRunner.class) @ContextConfiguration(classes = DIConfig.class) public class DITest {       //直接指定使用getUser2进行注入     @Autowired     @Qualifier("getUser2")     private User user;      @Test     public void testAutowiredPriamry(){         System.out.println(user);     } } 

但凡属于这两类的注释,其实都可以对歧义进行消除,比较推荐@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来确定,不再由开发人员管理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值