Spring IoC&DI

本文详细介绍了IoC(控制反转)和DI(依赖注入)在Spring框架中的应用,包括它们的概念、Spring中如何实现、以及在实际项目中的使用示例,展示了如何通过注解如@Component和@Autowired来管理和注入对象,以达到解耦和简化代码的目的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

IoC(Inversion of Control)

IoC即为“控制反转”,是Spring的一种核心思想,我们可以在类上添加对应的注解,将控制权转交给Spring,Spring就会在框架启动时加载该类,把对象交给Spring管理,就是一种IoC思想。

控制反转是一种思想,在我们生活中也处处体现。列如自动驾驶汽车,将汽车的控制权转交给了自动驾驶系统来控制。

IoC优点

程序的设计应该符合”高内聚低耦合“的设计原则,IoC的思想帮助我们很好的实现了各个不同模块之间的解耦合功能。

DI(Depedency Injection)

DI即”依赖注入“,指在容器运行期间,动态的为应用程序提供运行时所依赖的资源。是Spring实现IoC的一种具体实现方式。

IoC&DI

IoC是一种思想,DI是Spring对IoC的实现方式。这句话该如何理解呢?这就相当于公式与代入实际题目解题之间的关系。

IoC&DI的使用

Spring作为一个IoC容器,具备最基本的两个功能”存“和”取“。

Spring 容器管理的主要是对象, 这些对象, 我们称之为"Bean". 我们把这些对象交由Spring管理, 由 Spring来负责对象的创建和销毁. 我们程序只需要告诉Spring, 哪些需要存, 以及如何从Spring中取出对象。接下来以图书管理系统为例,介绍IoC&DI的使用。

1. Service层及Dao层的实现类,交给Spring管理,使用注解 @Component

@Component
public class BookDao {
    public List<BookInfo> mockData() {
        List<BookInfo> bookInfos = new ArrayList<>(15);
        for (int i = 0; i < 15; i++) {
            BookInfo bookInfo = new BookInfo();
            bookInfo.setId(1);
            bookInfo.setBookName("图书" + i);
            bookInfo.setAuthor("作者" + i);
            bookInfo.setCount(new Random().nextInt(200));
            bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));
            bookInfo.setPublish("出版社" + i);
            bookInfo.setStatus(i % 5 == 0 ? 2 : 1);
            bookInfos.add(bookInfo);
        }
        return bookInfos;
    }
}
@Component
public class BookService {
    @Autowired
    private BookDao bookDao;
    public List<BookInfo> getBookList(){
        List<BookInfo> bookInfos = bookDao.mockData();
        for(BookInfo bookInfo:bookInfos){
            if(bookInfo.getStatus() == 1){
                bookInfo.setStatusCN("可借阅");
            }else{
                bookInfo.setStatusCN("不可借阅");
            }
        }
        return bookInfos;
    }
}

2. 在Controller层 和Service层 注⼊运⾏时依赖的对象: 使⽤注解 @Autowired

@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/login")
    public boolean login(String userName, String password, HttpSession session) {
        if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)){
            return false;
        }
        if("admin".equals(userName) && "admin".equals(password)){
            session.setAttribute("userName",userName);
            return true;
        }
        return false;
    }
}
@RestController
@RequestMapping("/book")
public class BookController {
    @Autowired
    private BookService bookService;
    @RequestMapping("/getBookList")
    public List<BookInfo> getBookList(){
        List<BookInfo> bookInfos = bookService.getBookList();
        return bookInfos;
    }

}
@Component
public class BookService {
    @Autowired
    private BookDao bookDao;
    public List<BookInfo> getBookList(){
        List<BookInfo> bookInfos = bookDao.mockData();
        for(BookInfo bookInfo:bookInfos){
            if(bookInfo.getStatus() == 1){
                bookInfo.setStatusCN("可借阅");
            }else{
                bookInfo.setStatusCN("不可借阅");
            }
        }
        return bookInfos;
    }
}

通过上⾯的案例, 我们已经知道了Spring IoC 和DI的基本操作, 接下来我们来系统的学习Spring IoC和DI 的操作。

IoC详解

在之前的案例中,我们通过@Component和@Autowired将多个需要用到的类对象交给了Spring管理,实现了控制反转。而Spring为了提供更好的web服务,提供了更加丰富的注解。一共可以分为以下两类。

类注解:@Controller(控制器存储)、@Service(服务存储)、@Repository(仓库存储)、@Component(组件存储)、@Configuration(配置存储)

方法注解:@Bean

由于使用方式类似,以Controller为例介绍。  

@Controller(控制器存储)

@Controller
public class UController {
    public void sayHi(){
        System.out.println("hello controller");
    }

}

此时我们已经将对象存储到了Spring当中,那么我们该如何调用呢?

@SpringBootApplication
public class SpringBootDemoApplication {

    public static void main(String[] args) {

        ApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
        UController ucontroller = applicationContext.getBean(UController.class);
        ucontroller.sayHi();

    }

}

因为对象都交给 Spring 管理了,所以获取对象要从 Spring 中获取,那么就得先得到 Spring 的上下⽂,通过ApplicationContext的getBean()方法获取UController对象。

以下是Spring源码中提供的获取对象的几种方式。常用的是1,2,5几种方式。

public interface BeanFactory {
    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

}
@SpringBootApplication
public class SpringBootDemoApplication {

    public static void main(String[] args) {

        ApplicationContext applicationContext = SpringApplication.run(SpringBootDemoApplication.class, args);
        UController ucontroller = (UController) applicationContext.getBean("UController");
        UController ucontroller1 = applicationContext.getBean("UController", UController.class);
        UController ucontroller2 = applicationContext.getBean(UController.class);
        System.out.println(ucontroller);
        System.out.println(ucontroller1);
        System.out.println(ucontroller2);

    }

}

根据几种不同的方式获取对象,发现地址是一样的。

类注解及其关系

为了与应用分层呼应,能够让程序员看到类注解后直接明白该类的用途,所以有了各种名称不同,功能类似的注解。

@Controller:控制层, 接收请求, 对请求进⾏处理, 并进⾏响应.

@Servie:业务逻辑层, 处理具体的业务逻辑.

@Repository:数据访问层,也称为持久层. 负责数据访问操作 

@Configuration:配置层. 处理项⽬中的⼀些配置信息.

Spring三层架构

 方法注解@Bean

类注解是添加到某个类上的, 但是存在两个问题:

1. 使⽤外部包⾥的类, 没办法添加类注解

2. ⼀个类, 需要多个对象, ⽐如多个数据源

这时候我们就需要使用方法注解@Bean

方法注解的的使用需要搭配类注解共同使用

@Component
public class BeanController {
    @Bean
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(2);
        userInfo.setName("lisi");
        userInfo.setAge(13);
        return userInfo;
    }
}

可以看到我们成功获取了UserInfo中存储的信息

通过Bean获取多个对象

@Component
public class BeanController {
    @Bean
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(2);
        userInfo.setName("lisi");
        userInfo.setAge(13);
        return userInfo;
    }
    @Bean
    public UserInfo userInfo1() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(23);
        userInfo.setName("zhangsan");
        userInfo.setAge(14);
        return userInfo;
    }
}

我们可以通过给方法起不同的名称,通过名称获取对应的对象.

观察结果,对象已经被成功取出 

通过设置Bean的name属性获取对象

@Component
public class BeanController {
    @Bean
    public UserInfo userInfo() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(2);
        userInfo.setName("lisi");
        userInfo.setAge(13);
        return userInfo;
    }
    @Bean(name = {"uf1","userInfo1"})
    public UserInfo userInfo1() {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(23);
        userInfo.setName("zhangsan");
        userInfo.setAge(14);
        return userInfo;
    }
}

这里给userInfo1设置了别名uf1

设置别名的uf1也被成功取出

DI详解

依赖注⼊是⼀个过程,是指IoC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象. 在上⾯程序案例中,我们使⽤了@Autowired这个注解,完成了依赖注⼊的操作.

简单来说, 就是把对象取出来放到某个类的属性中.

依赖注入可以分为以下三种:1.属性注入2.构造方法注入3.setter注入

属性注入

属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中。

@Service
public class UserService {
    public void sayHi(){
        System.out.println("hi,userService!");
    }
}
@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public void sayHi(){
        System.out.println("hi,userController!");
        userService.sayHi();
    }

}

结果显示我们已经成功将userService类注入到userController类中

构造方法注入

构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所⽰:

@Controller
public class UserController {
    private UserService userService;
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void sayHi(){
        System.out.println("hi,userController!");
        userService.sayHi();
    }

}

同样成功获取到了userController类对象,需要注意的是,如果类只有⼀个构造⽅法,那么 @Autowired 注解可以省略;如果类中有多个构造⽅法, 那么需要添加上 @Autowired 来明确指定到底使⽤哪个构造⽅法.

Setter注入 

Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法的时候需要加上 @Autowired 注 解 ,如下代码所⽰:

@Controller
public class UserController {
    private UserService userService;

    public void sayHi(){
        System.out.println("hi,userController!");
        userService.sayHi();
    }
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

三种注入优缺点分析

属性注⼊

优点: 简洁,使⽤⽅便

缺点:

1. 只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常

2.不能注⼊⼀个Final修饰的属

构造函数注⼊

优点:

1. 可以注⼊final修饰的属性

 2. 注⼊的对象不会被修改

3. 依赖对象在使⽤前⼀定会被完全初始化,因为依赖是在类的构造⽅法中执⾏的,⽽构造⽅ 法是在类加载阶段就会执⾏的⽅法。

4. 通⽤性好, 构造⽅法是JDK⽀持的, 所以更换任何框架,他都是适⽤的

缺点:

注⼊多个对象时, 代码会⽐较繁琐

Setter注⼊

优点:

⽅便在类实例之后, 重新对该对象进⾏配置或者注⼊

缺点:

1.不能注⼊⼀个Final修饰的属性

2,注⼊对象可能会被改变, 因为setter⽅法可能会被多次调⽤, 就有被修改的⻛险.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值