0
点赞
收藏
分享

微信扫一扫

Windows自动化1️⃣环境搭建WinAppDriver

左小米z 2024-08-15 阅读 19

目录


DI介绍

学习了IoC后, 什么是DI呢?

IoC是一种思想,DI是一种实现方式

DI: Dependency Injection(依赖注⼊)

容器在运⾏期间, 动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊

上述代码中, 是通过构造函数的⽅式, 把依赖对象注⼊到需要使⽤的对象中

在这里插入图片描述

IoC 是⼀种思想,也是"⽬标", ⽽思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽ DI 就属于具体的实现。所以也可以说, DI 是IoC的⼀种实现.

IoC & DI 使⽤

对IoC和DI有了初步的了解, 我们接下来具体学习Spring IoC和DI的代码实现.

依然是先使⽤, 再学习

⽬标: 把BookDao, BookService 交给Spring管理, 完成Controller层, Service层, Dao层的解耦

步骤:

  1. Service层及Dao层的实现类,交给Spring管理: 使⽤注解: @Component
  2. 在Controller层和Service层注⼊运⾏时依赖的对象: 使⽤注解 @Autowired

实现:

  1. 把 BookDao 交给Spring管理, 由Spring来管理对象
@Component
public class BookDao {
    //mock - 虚拟数据,假数据
    public List<BookInfo> mockData() {
        //对于已知的数据量或者大概知道这个集合的数量时,创建List时建议指定初始化容量
        List<BookInfo> bookInfos=new ArrayList<>(15);
        for(int i=0;i<15;i++){
            BookInfo bookInfo=new BookInfo();
            bookInfo.setId(i);
            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;
    }
}
  1. 把 BookService 交给Spring管理, 由Spring来管理对象
//通过注解告诉Spring帮我们把BookService存入容器中
@Component
public class BookService {
    private BookDao bookDao = new BookDao();
    public List<BookInfo> getBookList(){
        //1.获取图书数据
        List<BookInfo> bookInfos=bookDao.mockData();
        //2.对图书数据进行修改处理
        for(BookInfo bookInfo:bookInfos){
            if(bookInfo.getStatus()==1){
                bookInfo.setStatusCN("可借阅");
            }else{
                bookInfo.setStatusCN("不可借阅");
            }
        }
        return bookInfos;
    }
}
  1. 删除创建BookDao的代码, 从Spring中获取对象
//通过注解告诉Spring帮我们把BookService存入容器中
@Component
public class BookService {
    @Autowired
    private BookDao bookDao;
    public List<BookInfo> getBookList(){
        //1.获取图书数据
        List<BookInfo> bookInfos=bookDao.mockData();
        //2.对图书数据进行修改处理
        for(BookInfo bookInfo:bookInfos){
            if(bookInfo.getStatus()==1){
                bookInfo.setStatusCN("可借阅");
            }else{
                bookInfo.setStatusCN("不可借阅");
            }
        }
        return bookInfos;
    }
}
  1. 删除创建BookService的代码, 从Spring中获取对象
@RestController
@RequestMapping("/book")
public class BookController {
    @Autowired//告诉Spring,从容器中取出这个对象,赋值给当前对象的属性
    private BookService bookService;
    //等价于
    //private BookService bookService
    //public BookController() {
    //    this.bookService = new BookService();
    //}

    @RequestMapping("/getBookList")
    public List<BookInfo> getBookList(){
        List<BookInfo> bookInfos = bookService.getBookList();
        //3.返回数据
        return bookInfos;
    }
}
  1. 重新运⾏程序, http://127.0.0.1:8080/book_list.html

结果是一样的

IoC详解

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

前⾯我们提到IoC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。

也就是bean的存储.

Spring是一个容器,存的是对象,对象这个词,在Spring的范围内,称之为bean

Bean的存储

在之前的⼊⻔案例中,要把某个对象交给IOC容器管理,需要在类上添加⼀个注解 @Component

⽽Spring框架为了更好的服务web应⽤程序, 提供了更丰富的注解.

共有两类注解类型可以实现:

  1. 类注解@Controller、@Service、@Repository、@Component、@Configuration

  2. ⽅法注解@Bean

接下来我们分别来看

@Controller(控制器存储)

控制层用这个

使⽤ @Controller 存储 bean 的代码如下所⽰:

@Controller//将对象存储到 Spring 中
public class UserController {
    public void doController(){
        System.out.println("doController...");
    }
}

如何观察这个对象已经存在Spring容器当中了呢?

接下来我们学习如何从Spring容器中获取对象

@SpringBootApplication
public class DemoApplication {

   public static void main(String[] args) {
      //Spring上下文,返回的就是Spring的运行环境
      ApplicationContext context=SpringApplication.run(DemoApplication.class, args);
      //从Spring上下⽂中获取对象
      UserController bean = context.getBean(UserController.class);
      //使用对象
      bean.doController();
   }

}

观察运⾏结果, 发现成功从Spring中获取到Controller对象, 并执⾏Controller的doController⽅法

在这里插入图片描述

如果把@Controller删掉, 再观察运⾏结果

在这里插入图片描述

报错信息显⽰: 找不到类型是: com.example.demo.controller.UserController 的 bean

获取bean对象的其他⽅式

上述代码是根据类型来查找对象, 如果Spring容器中, 同⼀个类型存在多个bean的话, 怎么来获取呢?

ApplicationContext 也提供了其他获取bean的⽅式, ApplicationContext 获取bean对象的功能, 是⽗类 BeanFactory 提供的功能.

public interface BeanFactory {

    //以上省略...

    // 1. 根据bean名称获取bean
    Object getBean(String var1) throws BeansException;

    // 2. 根据bean名称和类型获取bean
    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    // 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean
    Object getBean(String var1, Object... var2) throws BeansException;

    // 4. 根据类型获取bean
    <T> T getBean(Class<T> var1) throws BeansException;

    // 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的bean
    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    //以下省略...
}

常⽤的是上述1,2,4种, 这三种⽅式,获取到的bean是⼀样的

其中1,2种都涉及到根据名称来获取对象. bean的名称是什么呢?

Bean 命名约定

程序开发⼈员不需要为bean指定名称(BeanId), 如果没有显式的提供名称(BeanId),Spring容器将为该bean⽣成唯⼀的名称.

命名约定使⽤Java标准约定作为实例字段名. 也就是说,bean名称以⼩写字⺟开头,然后使⽤驼峰式⼤⼩写.

也有⼀些特殊情况, 当有多个字符并且第⼀个和第⼆个字符都是⼤写时, 将保留原始的⼤⼩写. 这些规则与java.beans.Introspector.decapitalize (Spring在这⾥使⽤的)定义的规则相同.

@Service(服务存储)

业务逻辑层用这个

使⽤ @Service 存储 bean 的代码如下所⽰:

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

读取 bean 的代码:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //Spring上下文,返回的就是Spring的运行环境
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        //从Spring上下⽂中获取对象
        UserController bean = context.getBean(UserController.class);
        //使用对象
        bean.doController();

        UserService userService = context.getBean(UserService.class);
        userService.doService();
    }

}

观察运⾏结果, 发现成功从Spring中获取到UserService对象, 并执⾏UserService的doService⽅法

在这里插入图片描述

同样的, 把注解@Service删掉, 再观察运⾏结果,也是会有一样的异常找不到bean

@Repository(仓库存储)

数据访问层用这个

使⽤ @Repository 存储 bean 的代码如下所⽰:

@Repository
public class UserRepository {
    public void doRepository(){
        System.out.println("doRepository...");
    }
}

读取 bean 的代码:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //Spring上下文,返回的就是Spring的运行环境
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        //从Spring上下⽂中获取对象
        UserController bean = context.getBean(UserController.class);
        //使用对象
        bean.doController();

        UserService userService = context.getBean(UserService.class);
        userService.doService();
        //根据名称获取bean
        UserService userService2 = (UserService)context.getBean("userService");
        userService2.doService();
        //根据名称和类型获取bean
        UserService userService3 = context.getBean("userService", UserService.class);
        userService3.doService();

        UserRepository userRepository = context.getBean(UserRepository.class);
        userRepository.doRepository();
    }

}

观察运⾏结果, 发现成功从Spring中获取到UserRepository 对象, 并执⾏UserRepository 的doRepository⽅法

在这里插入图片描述

同样的, 把注解@Repository删掉, 再观察运⾏结果,也是会有一样的异常找不到bean

@Component(组件存储)

用于除了控制层、业务逻辑层、数据访问层的代码

使⽤ @Component 存储 bean 的代码如下所⽰:

@Component
public class UserComponent {
    public void doComponent(){
        System.out.println("doComponent...");
    }
}

读取 bean 的代码:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //Spring上下文,返回的就是Spring的运行环境
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        //从Spring上下⽂中获取对象
        UserController bean = context.getBean(UserController.class);
        //使用对象
        bean.doController();

        UserService userService = context.getBean(UserService.class);
        userService.doService();
        //根据名称获取bean
        UserService userService2 = (UserService)context.getBean("userService");
        userService2.doService();
        //根据名称和类型获取bean
        UserService userService3 = context.getBean("userService", UserService.class);
        userService3.doService();

        UserRepository userRepository = context.getBean(UserRepository.class);
        userRepository.doRepository();

        UserComponent userComponent=context.getBean(UserComponent.class);
        userComponent.doComponent();
    }

}

观察运⾏结果, 发现成功从Spring中获取到UserComponent 对象, 并执⾏UserComponent 的doComponent⽅法

在这里插入图片描述

同样的, 把注解@Component删掉, 再观察运⾏结果,也是会有一样的异常找不到bean

@Configuration(配置存储)

用于大型项目一些Spring以外的配置

使⽤ @Configuration 存储 bean 的代码如下所⽰:

@Configuration
public class UserConfig {
    public void doConfig(){
        System.out.println("doConfig...");
    }
}

读取 bean 的代码:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //Spring上下文,返回的就是Spring的运行环境
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        //从Spring上下⽂中获取对象
        UserController bean = context.getBean(UserController.class);
        //使用对象
        bean.doController();

        UserService userService = context.getBean(UserService.class);
        userService.doService();
        //根据名称获取bean
        UserService userService2 = (UserService)context.getBean("userService");
        userService2.doService();
        //根据名称和类型获取bean
        UserService userService3 = context.getBean("userService", UserService.class);
        userService3.doService();

        UserRepository userRepository = context.getBean(UserRepository.class);
        userRepository.doRepository();

        UserComponent userComponent=context.getBean(UserComponent.class);
        userComponent.doComponent();

        UserConfig userConfig=context.getBean(UserConfig.class);
        userConfig.doConfig();
    }

}

观察运⾏结果, 发现成功从Spring中获取到UserConfiguration 对象, 并执⾏UserConfiguration 的doConfig⽅法

在这里插入图片描述

同样的, 把注解@Configuration删掉, 再观察运⾏结果,也是会有一样的异常找不到bean

为什么要这么多类注解?

这个也是和咱们前⾯讲的应⽤分层是呼应的. 让程序员看到类注解之后,就能直接了解当前类的⽤途.

  • @Controller控制层, 接收请求, 对请求进⾏处理, 并进⾏响应.
  • @ServiCE业务逻辑层, 处理具体的业务逻辑.
  • @Repository数据访问层,也称为持久层. 负责数据访问操作
  • @Configuration配置层. 处理项⽬中的⼀些配置信息.

程序的应⽤分层,调⽤流程如下:

在这里插入图片描述

类注解之间的关系

查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:

在这里插入图片描述

其实这些注解⾥⾯都有⼀个注解@Component,说明它们本⾝就是属于 @Component 的"⼦类".

@Component 是⼀个元注解,也就是说可以注解其他类注解,如 @Controller , @Service ,@Repository 等. 这些注解被称为 @Component 的衍⽣注解.

Controller是被赋予其他功能的,想被外界访问到,只能用这个Controller,想要接受请求就只能用这个Controller,也就是说这个Controller必须作为程序的第一关

基本大家程序入口都是Controller

@Controller , @Service@Repository ⽤于更具体的⽤例(分别在控制层, 业务逻辑层, 持久化层), 在开发过程中, 如果你要在业务逻辑层使⽤ @Component@Service,显然@Service是更好的选择

RequestMapping() 是SpringMVC的一个注解

⽅法注解 @Bean

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

  1. 使⽤外部包⾥的类, 没办法添加类注解
  2. ⼀个类, 需要多个对象, ⽐如多个数据源

这种场景, 我们就需要使⽤⽅法注解 @Bean

我们先来看看⽅法注解如何使⽤:

@Data
public class UserInfo {
    private Integer id;
    private String name;
    private Integer age;
}
public class BeanConfig {
    @Bean
    public UserInfo userInfo(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(12);
        return userInfo;
    }
}

然⽽,当我们写完以上代码,尝试获取 bean 对象中的 user 时却发现,根本获取不到:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //Spring上下文,返回的就是Spring的运行环境
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        //@Bean演示
        UserInfo userInfo=(UserInfo) context.getBean(UserInfo.class);
        System.out.println(userInfo);
    }

}

以上程序的执⾏结果如下:

在这里插入图片描述

这是为什么呢?

⽅法注解要配合类注解使⽤ & 定义多个对象

在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中

对于同⼀个类, 如何定义多个对象呢?

如下代码所⽰:

@Configuration
public class BeanConfig {
    @Bean
    public UserInfo userInfo(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(12);
        return userInfo;
    }
    @Bean
    public UserInfo userInfo2(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(2);
        userInfo.setName("lisi");
        userInfo.setAge(13);
        return userInfo;
    }
}

定义了多个对象的话, 我们根据类型获取对象, 获取的是哪个对象呢?

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //Spring上下文,返回的就是Spring的运行环境
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        //@Bean演示
        UserInfo userInfo=(UserInfo) context.getBean(UserInfo.class);
        System.out.println(userInfo);
    }

}

运⾏结果:

在这里插入图片描述

报错信息显⽰: 期望只有⼀个匹配, 结果发现了两个, userInfo,userInfo2

从报错信息中, 可以看出来, @Bean 注解的bean, bean的名称就是它的⽅法名

一个类型,存在多个bean时,我们就不能使用类型(.class)来获取对象了

接下来我们根据名称(也就是方法名)来获取bean对象

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //Spring上下文,返回的就是Spring的运行环境
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        //@Bean演示
        UserInfo userInfo=(UserInfo) context.getBean("userInfo");
        System.out.println(userInfo);
        UserInfo userInfo2=(UserInfo) context.getBean("userInfo2");
        System.out.println(userInfo2);
    }

}

运⾏结果:

在这里插入图片描述

可以看到, @Bean 可以针对同⼀个类, 定义多个对象。

重命名 Bean

可以通过设置 name 属性给 Bean 对象进⾏重命名操作,如下代码所⽰:这两个都是它的别名

@Bean(name = {"u1","user1"})
public User user1(){
 	User user = new User();
 	user.setName("zhangsan");
 	user.setAge(18);
 	return user;
}

此时我们使⽤ u1 就可以获取到 User 对象了,如下代码所⽰:

 @SpringBootApplication
 public class SpringIocDemoApplication {

 	public static void main(String[] args) {
 		//获取Spring上下⽂对象
 		ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio
 		//从Spring上下⽂中获取对象
 		User u1 = (User) context.getBean("u1");
 		//使⽤对象
 		System.out.println(u1);
 	}
 }
扫描路径

Q: 使⽤前⾯学习的四个注解声明的bean,⼀定会⽣效吗?

A: 不⼀定(原因:bean想要⽣效,还需要被Spring扫描)

下⾯我们通过修改项⽬⼯程的⽬录结构,来测试bean对象是否⽣效:

在这里插入图片描述

再运⾏代码:

 @SpringBootApplication
 public class SpringIocDemoApplication {

 	public static void main(String[] args) {
 		//获取Spring上下⽂对象
 		ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio
 		//从Spring上下⽂中获取对象
 		User u1 = (User) context.getBean("u1");
 		//使⽤对象
 		System.out.println(u1);
 	}
                                                           
 }

运⾏结果:

在这里插入图片描述

解释: 没有bean的名称为u1

为什么没有找到bean对象呢?

使⽤五⼤注解声明的bean,要想⽣效, 还需要配置扫描路径, 让Spring扫描到这些注解

也就是通过 @ComponentScan 来配置扫描路径.

 @ComponentScan({"com.example.demo"})
 @SpringBootApplication
 public class SpringIocDemoApplication {

     public static void main(String[] args) {
 		//获取Spring上下⽂对象
 		ApplicationContext context = SpringApplication.run(SpringIocDemoApplicatio
 		//从Spring上下⽂中获取对象
 		User u1 = (User) context.getBean("u1");
 		//使⽤对象
		System.out.println(u1);
 	}
 }

那为什么前⾯没有配置 @ComponentScan 注解也可以呢?

@ComponentScan 注解虽然没有显式配置,但是实际上已经包含在了启动类声明注解@SpringBootApplication 中了

SpringBoot特点:约定大于配置

其中之一体现:扫描路径

默认的扫描路径:启动类所在的目录及其子孙目录(这句话和下面这句是一样的)

默认扫描的范围是SpringBoot启动类所在包及其⼦包

推荐做法:

把启动类放在我们希望扫描的包的路径下, 这样我们定义的bean就都可以被扫描到

在这里插入图片描述

DI详解

上⾯讲了控制反转IoC的细节,接下来我们学习依赖注⼊DI的细节。

依赖注⼊是⼀个过程,是指IoC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象.

在上⾯程序案例中,我们使⽤了 @Autowired 这个注解,完成了依赖注⼊的操作.

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

关于依赖注⼊, Spring也给我们提供了三种⽅式:

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

接下来,我们分别来看。

下⾯我们按照实际开发中的模式,将 Service 类注⼊到 Controller 类中。

1. 属性注⼊

属性注入是以类型进行匹配,与注入的属性名称无关

但是一个类型如果存在多个对象时,优先名称匹配,如果名称都匹配不上,就报错了

而且@Autowired无法注入一个final修饰的属性

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

Service 类的实现代码如下:

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

Controller 类的实现代码如下:

@Controller//将对象存储到 Spring 中
public class UserController {

    @Autowired
    private UserService userService;
    public void doController(){
        userService.doService();
        System.out.println("doController...");
    }
}

获取 Controller 中的 doController⽅法:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        //Spring上下文,返回的就是Spring的运行环境
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
        //从Spring上下⽂中获取对象
        UserController bean = context.getBean(UserController.class);
        //使用对象
        bean.doController();
    }

}

最终结果如下:

在这里插入图片描述

去掉@Autowired , 再运⾏⼀下程序看看结果

在这里插入图片描述

2. 构造⽅法注⼊

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

@Controller//将对象存储到 Spring 中
public class UserController {

    //属性注入
    //@Autowired
    //private UserService userService;

    //构造方法注入
    private UserService userService;
    private UserInfo userInfo;

    //Spring会默认优先使用无参构造函数,这样就没有userService对象,下面doController()方法就空指针异常了
    //public UserController() {
    //}

    //注释了无参构造,下面两个构造函数Spring就不知道用哪个了,因为Spring是默认优先使用无参的
    //因此我们要显式告诉Spring用哪个,这里就用到@Autowired
    @Autowired
    public UserController(UserService userService) {
        this.userService=userService;
    }
    public UserController(UserService userService, UserInfo userInfo) {
        this.userService = userService;
        this.userInfo = userInfo;
    }

    public void doController(){
        userService.doService();
        System.out.println("doController...");
    }
}

Spring会默认优先使用无参构造函数,这样就没有userService对象,下面doController()方法中的userService.doService()就空指针异常了

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

3. Setter 注⼊

Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 Setter ⽅法的时候需要加上 @Autowired 注解 ,它不像是构造方法只有一个的话会默认帮你加上,如下代码所⽰:

@Controller//将对象存储到 Spring 中
public class UserController {

    //属性注入
    //@Autowired
    //private UserService userService;

    //构造方法注入
    //private UserService userService;
    //private UserInfo userInfo;

    //Spring会默认优先使用无参构造函数,这样就没有userService对象,下面doController()方法就空指针异常了
    //public UserController() {
    //}

    //注释了无参构造,下面两个构造函数Spring就不知道用哪个了,因为Spring是默认优先使用无参的
    //因此我们要显式告诉Spring用哪个,这里就用到@Autowired
    //@Autowired
    //public UserController(UserService userService) {
    //    this.userService=userService;
    //}
    //public UserController(UserService userService, UserInfo userInfo) {
    //    this.userService = userService;
    //    this.userInfo = userInfo;
    //}

    //Setter方法注入
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void doController(){
        userService.doService();
        System.out.println("doController...");
    }
}

尝试⼀下 Setter ⽅法如果不加 @Autowired 注解能注⼊成功吗?

肯定不行

三种注⼊优缺点分析
  • 属性注⼊

    • 优点: 简洁,使⽤⽅便;

    • 缺点:

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

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

  • 构造函数注⼊(Spring 4.X推荐)

    • 优点:

      • 可以注⼊final修饰的属性

      • 注⼊的对象不会被修改

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

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

    • 缺点:

      • 注⼊多个对象时, 代码会⽐较繁琐
  • Setter注⼊(Spring 3.X推荐)

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

  • 缺点:

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

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

更多DI相关内容参考:https://docs.spring.io/spring-framework/reference/core/beans/dependencies.html

@Autowired存在问题

当同⼀类型存在多个bean时, 使⽤@Autowired会存在问题

@Configuration
public class BeanConfig {
    @Bean
    public String name(){
       return "zhangsan";
    }
    @Bean
    public UserInfo userInfo1(String name){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(1);
        userInfo.setName(name);
        userInfo.setAge(12);
        return userInfo;
    }
    @Bean   
    public UserInfo userInfo2(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(2);
        userInfo.setName("lisi");
        userInfo.setAge(13);
        return userInfo;
    }
}
@Controller//将对象存储到 Spring 中
public class UserController {

    //属性注入
    @Autowired
    private UserService userService;
    @Autowired
    private UserInfo userInfo;

    public void doController(){
        userService.doService();
        System.out.println(userInfo);
        System.out.println("doController...");
    }
}

在这里插入图片描述

报错的原因是,⾮唯⼀的 Bean 对象。

如何解决上述问题呢?Spring提供了以下⼏种解决⽅案:(解决方法思想:给bean重命名或者指定名称)

  • 属性名和你需要使用的对象名保持一致(不这样做就以下三种方法)

  • @Primary

  • @Qualifier

  • @Resource

**(不推荐)**使⽤@Primary注解:当存在多个相同类型的Bean注⼊时,加上@Primary注解,来确定默认的实现,标识默认的对象

@Configuration
public class BeanConfig {
    @Bean
    public String name(){
       return "zhangsan";
    }
    @Primary
    @Bean
    public UserInfo userInfo1(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(1);
        userInfo.setName("zhangsan");
        userInfo.setAge(12);
        return userInfo;
    }
    @Bean
    public UserInfo userInfo2(){
        UserInfo userInfo=new UserInfo();
        userInfo.setId(2);
        userInfo.setName("lisi");
        userInfo.setAge(13);
        return userInfo;
    }
}

**(较常用)**使⽤@Qualifier注解:指定当前要注⼊的bean对象。@Qualifier的value属性中,指定注⼊的bean的名称。

  • @Qualifier注解不能单独使⽤,必须配合@Autowired使⽤
@Controller//将对象存储到 Spring 中
public class UserController {

    //属性注入
    @Autowired
    private UserService userService;
    @Qualifier("userInfo2")
    @Autowired
    private UserInfo userInfo;

    public void doController(){
        userService.doService();
        System.out.println(userInfo);
        System.out.println("doController...");
    }
}

(较常用)使⽤@Resource注解:是按照bean的名称进⾏注⼊。通过name属性指定要注⼊的bean的名称。

@Controller//将对象存储到 Spring 中
public class UserController {

    //属性注入
    @Autowired
    private UserService userService;
    @Resource(name="userInfo2")
    private UserInfo userInfo;

    public void doController(){
        userService.doService();
        System.out.println(userInfo);
        System.out.println("doController...");
    }
}
练习

通过上⾯的学习, 我们把前⾯的图书管理系统代码进⾏调整

Service层的注解, 改成 @Service

Dao层的注解, 改成 @Repository

重新运⾏代码, 验证程序访问正常

总结
Spring, Spring Boot 和 Spring MVC 的关系以及区别

Spring: 简单来说, Spring 是⼀个开发应⽤框架,什么样的框架呢,有这么⼏个标签:轻量级、⼀站式、模块化,其⽬的是⽤于简化企业级应⽤程序开发

Spring MVC: Spring MVC 是 Spring 的⼀个⼦框架, Spring诞⽣之后, ⼤家觉得很好⽤, 于是按照MVC模式设计了⼀个 MVC框架(⼀些⽤Spring 解耦的组件), 主要⽤于开发WEB应⽤和⽹络接⼝,所以,Spring MVC 是⼀个Web框架

Spring Boot: Spring Boot 是对 Spring 的⼀个封装, 为了简化Spring应⽤的开发⽽出现的,中⼩型企业,没有成本研究⾃⼰的框架, 使⽤Spring Boot 可以更加快速的搭建框架, 降级开发成本, 让开发⼈员更加专注于Spring应⽤的开发,⽽⽆需过多关注XML的配置和⼀些底层的实现

最后⼀句话总结: Spring MVC 和 Spring Boot 都属于 Spring,Spring MVC 是基于 Spring 的⼀个MVC 框架,⽽Spring Boot 是基于 Spring 的⼀套快速开发整合包

这三者专注的领域不同,解决的问题也不⼀样, 总的来说,Spring 就像⼀个⼤家族,有众多衍⽣产品, 但他们的基础都是Spring, ⽤⼀张图来表⽰他们三个的关系:

在这里插入图片描述

bean的存是五大注解和@Bean,使用这六个的时候,Spring会给一个默认的名称,五大注解的名称是类名的小驼峰表示法,特殊情况,前两位字母都为大写,名称就是类名;@Bean的名称就是方法名

bean 的命名

添加之后之前的名称就不再有了,Spring会使用程序员定义的bean名称

  1. 五⼤注解存储的bean

​ ① 前两位字⺟均为⼤写, bean名称为类名

​ ② 其他的为类名⾸字⺟⼩写

​ ③ 通过 value属性设置,比如 @Controller(value = "user")

  1. @Bean 注解存储的bean

​ ① bean名称为⽅法名

​ ②通过name属性设置 @Bean(name = {"u1","user1"})

​ ③一个Bean可以有多个名称,但一个名称只能对应一个Bean

常⻅⾯试题
  1. 三种注⼊⽅式的优缺点
  2. 常⻅注解有哪些? 分别是什么作⽤?

​ web url映射: @RequestMapping

​ 参数接收和接⼝响应: @RequestParam, @RequestBody, @ResponseBody

bean的存储: @Controller, @Service, @Repository, @Component, @Configuration, @Bean

bean的获取: 属性注入、构造方法注入、Setter方法注入

  1. @Autowired@Resource 区别
  2. 说下你对Spring, SpringMVC, Springboot的理解
举报

相关推荐

0 条评论