DI(依赖注入)详解

目录

一.DI的定义

二.Spring提供的依赖注入的方式 

a.属性注入

去掉@Autowired

b.构造方法注入 

注意事项 

结论 

c.Setter注入

去掉@Autowired 

d.三种注入方法的优缺点 

三. @Autowired存在的问题

解决方案如下 

@Primary

@Qualifier

@Resource

四.@Autowired和@Resource的区别 


一.DI的定义

DI(依赖注入)是一个过程,是指IoC容器在创建Bean时,去提供运行时所依赖的资源,而资源指的就是对象。简单来说,就是把对象取出来放到某个类的属性中。

注意,在有一些文章中,依赖注入也被称为“对象注入”,“属性装配"

二.Spring提供的依赖注入的方式 

关于依赖注入,Spring 提供的三种方式:

  1. 属性注入(Field Injection)
  2. 构造方法注入(Constructor Injection)
  3. Setter 注入(Setter Injection)

a.属性注入

属性注入是使用@Autowired实现的。

演示代码如下

代码结构如下

UserService代码如下

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void say(){
        System.out.println("this is UserService");
    }

}

UserController代码如下

package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    //方法1 属性注入
    @Autowired
    private UserService userService;
    public void say(){
        System.out.println("this is UserController");
        userService.say();
    }
}

DemoApplication代码如下

package com.example.demo;

import com.example.demo.controller.UserController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//获取Spring上下文
		ApplicationContext context=SpringApplication.run(DemoApplication.class, args);
		UserController userController= (UserController) context.getBean("userController");
		userController.say();
	}

}

运行结果如下

去掉@Autowired

会报NullpointerException,因为userService是空的 

b.构造方法注入 

构造⽅法注⼊是在类的构造⽅法中实现注⼊
代码结构如下

UserController2代码如下

package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController2 {
    // 注入方法2 构造方法
    private UserService userService;

    @Autowired
    public UserController2(UserService userService){
        this.userService=userService;
    }
    public void say(){
        System.out.println("this is UserController2");
        userService.say();
    }
}

DemoApplication代码如下

package com.example.demo;

import com.example.demo.controller.UserController;
import com.example.demo.controller.UserController2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//获取Spring上下文
		ApplicationContext context=SpringApplication.run(DemoApplication.class, args);
//		UserController userController= (UserController) context.getBean("userController");
		UserController2 userController2= (UserController2) context.getBean("userController2");
		userController2.say();
	}

}

运行结果如下

注意事项 

注意事项:如果类只有一个构造方法,那么@Autowired注解可以省略;如果类中有多个构造方法,那么需要添加上@Autowired来明确指定到底使用哪个构造方法。

如果有多个构造方法,不添加@Autowired

稍微修改一下代码结构

新添加一个StudentService类

StudentService类代码如下

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class StudentService {
    public void say(){
        System.out.println("this is StudentService");
    }


}

 并在UserController2类中增加一个构造方法

运行结果如下

 

java.lang.NoSuchMethodException 异常在这个上下文中通常表示 Spring 找不到默认的无参构造方法

那我们如果加上无参构造方法会怎么样

运行结果

 

通过这个异常,我们就可以推出结论,当有多个构造方法时,会选择默认的无参构造方法.

我们也可以通过@Autowired来指定默认的构造方法

 

运行结果

结论 

  • 如果只有一个构造方法,@Autowired 可以省略
  • 如果存在多个构造方法,默认的构造方法是 无参的构造方法,也可以通过 @Autowired 来指定默认的构造方法

c.Setter注入

Setter注入和属性的Setter方法实现类似,只不过在设置set方法的时候需要加上@Autowired注解

代码结构如下 

UserController3代码如下

package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController3 {
    //注入方法3 setter方法注入
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public void say(){
        System.out.println("this is UserController3");
        userService.say();
    }

}

DemoApplication代码如下

package com.example.demo;

import com.example.demo.controller.UserController;
import com.example.demo.controller.UserController2;
import com.example.demo.controller.UserController3;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//获取Spring上下文
		ApplicationContext context=SpringApplication.run(DemoApplication.class, args);
//		UserController userController= (UserController) context.getBean("userController");
//		UserController2 userController2= (UserController2) context.getBean("userController2");
		UserController3 userController3= (UserController3) context.getBean("userController3");
		userController3.say();
	}

}

结果如下

去掉@Autowired 

结果如下

不能调用,因为userService是空的。 

d.三种注入方法的优缺点 

  • 属性注入
    • 优点:简洁,使用方便;
    • 缺点:
      • 只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)
      • 不能注入一个 Final 修饰的属性
  • 构造函数注入 (Spring 4.X 推荐)
    • 优点:
      • 可以注入 final 修饰的属性
      • 注入的对象不会被修改
      • 依赖对象在使用前一定会被完全初始化,因为依赖是在类的构造方法中执行的,而构造方法是在类加载阶段就会执行的方法.
      • 通用性好,构造方法是 JDK 支持的,所以更换任何框架,他都是适用的
    • 缺点:
      • 注入多个对象时,代码会比较繁琐
  • Setter 注入 (Spring 3.X 推荐)
    • 优点:方便在类实例之后,重新对该对象进行配置或者注入
    • 缺点:
      • 不能注入一个 Final 修饰的属性
      • 注入对象可能会被改变,因为 setter 方法可能会被多次调用,就有被修改的风险.

三. @Autowired存在的问题

当同一类型存在多个bean是,使用@Autowired会存在问题

代码结构如下

User类代码

package com.example.demo.model;

import lombok.Data;

@Data
public class User {
    private String name;
    private int age;

}

UserService类代码


import com.example.demo.model.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

@Service
public class UserService {
//    public void say(){
//        System.out.println("this is UserService");
//    }

    @Bean("u1")
    public User user1(){
        User user=new User();
        user.setName("Maybe");
        user.setAge(15);
        return user;
    }
    @Bean
    public User user2(){
        User user=new User();
        user.setName("lisi");
        user.setAge(18);
        return user;
    }

}

UserController类代码

package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
//    方法1 属性注入
    @Autowired
    private UserService userService;
    @Autowired
    private User user;
    public void say(){
        System.out.println("this is UserController");
        System.out.println(user);
    }


}

DemoApplication类代码

package com.example.demo;

import com.example.demo.controller.UserController;
import com.example.demo.controller.UserController2;
import com.example.demo.controller.UserController3;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		//获取Spring上下文
		ApplicationContext context=SpringApplication.run(DemoApplication.class, args);
		UserController userController= (UserController) context.getBean("userController");
//		UserController2 userController2= (UserController2) context.getBean("userController2");
//		UserController3 userController3= (UserController3) context.getBean("userController3");
//		userController3.say();
		userController.say();


	}

}

运行结果 

UserContro只需要一个Bean对象,但是出现了2个。

解决方案如下 

 Spring提供了以下几种解决方案:

@Primary

使⽤@Primary注解:当存在多个相同类型的Bean注⼊时,加上@Primary注解,来确定默认的实现

 

@Qualifier

使用@Qualifier注解:指定当前要注入的 bean 对象。在@Qualifier的 value 属性中,指定注入的 bean 的名称。 

  • @Qualifier注解不能单独使用,必须配合@Autowired使用 

没有和@Autowired搭配使用

 则结果为null

@Resource

使用@Resource注解:是按照 bean 的名称进行注入。通过name属性指定要注入的 bean 的名称。

四.@Autowired和@Resource的区别 

  • @Autowired 是 Spring 框架提供的注解,而 @Resource 是 JDK 提供的注解
  • @Autowired 默认是按照类型注入,而 @Resource 是按照名称注入。相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值