• 【spring】初识spring基础


    1. Spring 的介绍

    1.1 Spring是什么?

    Spring: Spring Framework(Spring 框架)Spring ⽀持⼴泛的应⽤场景,它可以让 Java 企业级的 应⽤程序开发起来更简单。用一句简单的话来说,Spring就是一个拥有众多工具的ioc容器。

    1.2 什么是容器,什么是IOC 容器?

    容器: ⽤来容纳某种物品的(基本)装置
    在我们学习Java基础的时候,就接触过一些关于数据结构的容器,他们都是根据一定的组织结构来组织众多的数据,例如:Map,List,Stack,Queue... 它们是组织数据的容器

    Spring也是一个容器。是一个IOC容器。
    IOC(Inversion of control) 翻译为控制反转,IOC不是一种技术而是一种设计思想,其实这里的控制反转指的是,我们在创建对象的时候,对象的创建控制权交给了外部,在使用对象时,在程序中不要自己使用new创建对象,转换成为由外部主动提供对象。 ioc容器用于充当ioc思想中的外部

    IOC容器负责对象的创建,初始化等一系列的工作,被创建或者被管理的对象在IOC容器中统称为Bean
    在传统程序设计,我们直接在对象内部通过new进行创建对象。如果我们在A类中需要一个B类中的对象,那按照以前我们在A类中就可以直接new出一个B类型的对象,那么此时我们不妨想一下,如果这个在A类中需要B类对象的时候,直接new 出B类型对象的思想,放到复杂的项目中,那么就有可能程序的调用链就会很长。例如:在A类中new一个B类型的对象,在B类中new一个C类型的对象,在C类中new一个D类型的对象… 那么如果此时的程序需求一旦有了变化,那么就会牵一发而动全身,这些类中的代码都需要改动。传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象

    那么既然Spring是一个拥有众多工具的ioc容器,那么Spring这个容器就有控制反转的思想,那么是谁控制谁,又控制了什么,为什么在Spring中需要控制反转。

    是谁控制谁?控制了什么?

    其实Spring的ioc容器,控制了在类中创建和管理对象的权限。也就是ioc容器控制了在该类中new出其他类型的对象。在传统的程序设计中,如果要想在A类中得到B类型的对象,那么就会直接new出B类型的对象,但是在这样的操作在后期维护代码的时候是非常费劲的。只要在A类中依赖的B对象的成员属性如果需要改动,那么整个代码就要进行大规模的更改。 但是如果使用的ioc容器,创建和管理对象的权限就交给了ioc容器。把B对象传给ioc容器,当我们在A类中需要B对象的时候,只要引用Spring ioc 容器中的B对象即可。

    为什么Spring需要控制反转,在Spring中又控制反转了什么?

    因为在传统的程序设计中,如果要想在A类中得到B类型的对象,那么就会直接new出B类型的对象,但是在这样的操作在后期维护代码的时候是非常费劲的。只要在A类中依赖的B对象的成员属性如果需要改动,那么整个代码就要进行大规模的更改。,在ioc容器中创建和管理对象,在容器中创建对象A,并且有该容器管理A对象。当在类A中需要对象B的时候,那么此时就不需要在类A中创建出一个对象B,此时对象A中的引用就可以被动的接受ioc容器中传来的对象B.

    spring ioc 优点:

    成功实现了解耦耦合是指:两个或两个以上对象存在依赖,当一方修改之后会影响另一方,那么就说这些对象间存在耦合。我们知道较好的代码应该是 "高内聚,低耦合 "的。这里把创建和管理对象的权限交给了ioc容器。

    1.3 Spring IOC 的核心理念

    我们目前知道了Spring是一个拥有众多工具的IOC容器,那么怎样理解这就话呢?
    所谓的容器:最大的用处就是把某物添加到容器中,再从容器中把某物取出来,这里的spring ioc容器也是一样的。IOC的核心就是把bean添加到ioc容器中,然后在从ioc容器中把bean对象取出来。 其实Spring ioc 是一种具体的思想。它不是一种具体的行为措施。

    将对象存放到容器中的好处:将对象存储在 IoC 容器相当于 将以后可能⽤的所有⼯具制作好都放到仓库中,需要的时候直接取就⾏了,⽤完再把它放回到仓库。 ⽽ new 对象的⽅式相当于,每次需要⼯具 了,才现做,⽤完就扔掉了也不会保存,下次再⽤的时候还得重新做,这就是 IoC 容器和普通程序开 发的区别。

    1.4 什么是DI?

    DI (Dependency Injection) “依赖注⼊”
    那么什么又是“依赖注入”?
    在ioc容器中创建bean 和 bean之间依赖关系的整个过程使用对象的时候不仅可以直接从ioc容器获取,并且获取到的bean已经绑定了所有的依赖关系。
    所谓依赖注⼊,就是由 IoC 容器在运⾏期间,动态地将某种依赖关系注⼊到对象之中。所以, 赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,就是指通过引⼊ IoC 容 器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦。

    1.5 经典面试题:说一说 IOC 和 DI 的区别

    依赖注入不是一种设计实现,而是一种具体的技术,它是在 IoC 容器运行期间,动态地将某个依赖对象注入到当前对象的技术就叫做DI(依赖注入)。
    IoC 和 DI 都是 Spring 框架中的重要概念,它们都是用来实现对象解耦的,其中 IoC(控制反转)是一种设计思想,而 DI(依赖注入)是一种具体的实现手段

    2. 手把手创建一个Spring项目

    第一步先创建一个maven 项目 在这里插入图片描述
    在这里插入图片描述
    第二步:在spring_test项目中配置pom.xml文件,因为我们要基于maven创建一个Spring项目,所以要引入一个spring-context(Spring上下文)依赖,还有要把bean对象添加到spring中,就需要引入spring_beans依赖我们也可以在maven的官网去找。
    在这里插入图片描述
    这里就是spring-context和spring-beans依赖,可以直接添加到pom.xml文件中,加入之后在idea的右边栏maven栏中点击重新加载按钮。

     <dependencies>
       <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <ersion>5.2.3.RELEASE</version>
      </dependency>
    
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-beans</artifactId>
          <version>5.2.3.RELEASE</version>
      </dependency>
    </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    到此我们的第一个spring项目基本就创建完成了,但是我们还要往spring ioc容器中添加对象。

    往spring ioc 容器中添加依赖对象:

    src --> main --> resource中添加配置,在resource下创建一个文件,这个文件名可以随便起,但是在这里还是简明知意的好,在这里我们创建一个spring-config.xml文件,并且在这个.xml文件中写入如下内容。

     <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:content="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
        
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    此时还没有引入bean对象到ioc 容器中。那么就要在这个spring-config.xml中写入一下信息,即就把bean对象添加到了spring ioc容器中了。

    如果要想再spring ioc 容器中添加对象,com.model包下先创建一个User类,在这个类中创建一个sayHi()方法

    package com.model; 
    public class User {
        public void sayHi(){
            System.out.println("say hi");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    那么就在 标签中添加对象即可,其中在标签中的属性有 id 其中 这个 id表示的是在从spring ioc 容器中得到相应对象的时候的beanName,就是通过这个beanName就可以找到spring ioc 容器中找到这个对象 还有一个属性 class 表示的是这个spring ioc 容器中的user对象所在的类,就是一个具体的类的路径

     <beans>
            <bean id = "user" class="com.model.User"></bean>
     </beans>
    
    • 1
    • 2
    • 3

    从spring ioc 容器中得到对象

    创建一个App类,在该类中得到存入spring ioc 容器中的user对象,那么此时就不得不介绍一下spring的上下文对象了

    spring上下文即是Spring容器抽象的一种实现;而我们常见的ApplicationContext本质上说就是一个维护Bean定义以及对象之间协作关系的高级接口

    一种是不常用的BeanFactory这是最简单的容器,只能提供基本的DI功能;还有一种就是继承了BeanFactory后派生而来的应用上下文,其抽象接口也就是我们上面提到的的ApplicationContext,它能提供更多企业级的服务,例如解析配置文本信息等等,这也是应用上下文实例对象最常见的应用场景。有了上下文对象,我们就能向容器注册需要Spring管理的对象了。

    ApplicationContext 和 BeanFactory 的区别:
    相同点: 都可以实现从容器中获取Bean,都提供了getBean()方法
    不同点:

    • ApplicationContext属于BeanFactory的子类,而ApplicationContext除了拥有BeanFactory 的所有功能之外,所提供了更多的方法实现,比如对国际化的支持,资源访问的支持,以及事件和传播等方面的支持
    • 从性能方面来说,二者是不同的,BeanFactory是按需加载BeanApplicationContext是饿汉模式,在创建时会将所有的Bean都加载起来,以备以后使用。

    如果使用BeanFactory作为spring的上下文对象,在加载spring-config配置文件的时候,是按需加载的,也就是说我现在在这个类中需要使用到spring ioc 容器所管理的某个对象,那么此时只把我要的Bean对象进行加载,这种就属于懒汉模式,你要的时候,我才给你拿。但是每次加载spring-config.xml文件中的某个对象到spring ioc容器会消耗一部分时间,相比,AppliactionContext作为spring的上下文对象,在spring ioc 容器加载时,就会把spring-config.xml文件中的全部Bean对象加载进来。如果在以后从spring ioc 容器中获取对象的时候,直接获取即可,无需加载,这样只是第一次加载spring-config.xml文件的时候,时间会消耗长一点。所以说在计算机中,懒汉模式不一定比饿汉模式好

    在model包下,在创建出一个User2类,并且把这个user2对象,添加到 spring ioc 容器中。

    package com.model;
    public class User2 {
    //在spring ioc 容器加载user2 对象到 spring ioc 容器的时候就会调用User2类中的构造方法
        public User2(){
            System.out.println("加载User2类");
        }
        public void sayHi(){
            System.out.println("hello world");
        }
    }
    
    //把user2对象添加到spring ioc 容器中
    <bean id="user2" class="com.model.User2"></bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在User类中,添加一个无参的构造方法

     package com.model; 
    public class User {
        public User(){
            System.out.println("加载User类");
        }
        public void sayHi(){
            System.out.println("say hi");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在AppliactionContext作为spring上下文对象的时候,会一次性加载spring-config.xml中的所有Bean
    在这里插入图片描述
    在BeanFactory作为spring上下文对象的时候,会按需加载spring-config.xml中的Bean对象。
    在这里插入图片描述
    言回正传,从spring ioc 容器中获取对象。

     public class App {
        public static void main(String[] args) {
        	 // 使用ApplicationContext作为spring的上下文对象,把spring-config.xml文件作为参数传给这个对象。就能通过getBean()方法,得到spring ioc 容器中所管理的对象。
             ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
             //因为getBean()方法的返回值是一个Object的类型,所以要在这里进行强制类型转换
             User user = (User) context.getBean("user");
             //得到spring ioc 中的对象,调用对象所在类的方法
             user.sayHi();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在上述到代码中,一定要注意的是在getBean()方法中,所传入的参数一定要和spring-config.xml文件中Bean对象的id是一样的。

    getBean()方法的更多用法:

    • 第一种: UserController user = context.getBean(UserController.class); 根据类的类型,在spring ioc 容器中获得Bean对象
    • 第二种: UserController user = context.getBean("user", UserController.class); 根据类的类型,和在spring-config.xml文件中的Bean的id在spring ioc 容器中获得Bean对象
    • 第三种: UserController user = context.getBean("user") 根据spring-config.xml文件中的Bean的id在spring ioc 容器中获得Bean对象

    但是第一种方法获得Bean对象,有一个诟病,那就是如果在spring-config.xml配置文件中出现了两个相同的类型网spring ioc容器中添加Bean对象,那么在按照对象的类型在spring ioc 容器中获得对象的时候,就不知道你此时要获取那个对象(此时spring ioc 容器中有多个类型相同的对象)

    在这里插入图片描述
    运行结果:
    在这里插入图片描述
    期望有一个匹配的 bean,但是找到了2: user,user2 所以说在spring ioc 容器中如果存在相同类型的对象时,就不能使用上述的第一种方法从容器中获得对象。

    那么此时的第二种getBean()的重载方法就应运而生,在getBean()方法中的参数为Bean的 id 和 这个Bean的类型。
    在这里插入图片描述
    运行结果:
    在这里插入图片描述
    第三种方法,我们已经在上述代码中介绍过了,在这里不做赘述。
    概述以上流程:
    在这里插入图片描述

    3. Spring 更简单的读取和存储对象

    其实这里所说的更简单的读取和存储对象,就是注解式编程,那么什么是注解式编程呢?

    注解用来定义一个类。属性或一些方法,以便程序能被编译处理。它相当于一个说明文件,告诉应用程序某个被注解的类或属性是什么,要怎么处理,注解可以用于标志包,类,方法和变量等。

    前置工作在spring-config.xml文件中配置一下内容:

     <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:content="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
            <content:component-scan base-package="com.model"></content:component-scan>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    其中配置文件中的: 中的base-package表示要在该com.model包中扫描是否有5大类注解修饰的类,如果有那么就把这个类的对象添加到spring ioc 容器中。
    那么什么又是5大类注解哪?
    想要将对象存储在 Spring 中,有两种注解类型可以实现:

    • 类注解@Controller、@Service、@Repository、@Component、@Configuration
    • 方法注解: @Bean

    使用@Controller(控制存储) 把Bean存储到spring ioc 容器中

    在model包下,新建一个UserController类

    package com.model;
    
    import org.springframework.stereotype.Controller;
    //在spring-config.xml配置文件中的base-package就会扫表这个包中是否有那个类上到有上述的5大类注解,就会把对应的对象添加到spring ioc 容器中
    //在这里就可以通过@Controller注解,把userController对象添加到spring ioc容器中 
    @Controller
    public class UserController {
        public void sayHi(){
            System.out.println("hi controller");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    那么此时怎样从spring ioc 容器中取出对象呢?
    首先有一种方法,就是根据spring ioc 容器中的对象的类型在容器中寻找。使用UserController controller = context.getBean(UserController.class)从spring ioc 容器中得到对象。
    在这里插入图片描述
    那么如果spring ioc容器中有两个类型相同的对象,那么使用上述的这种方法就是不对的。
    在这里我们还是把类名的第一个字母该文小写,试使用userController作为beanName类型作为参数,是否能从spring ioc 容器中找到响应的对象。
    在这里插入图片描述
    那么为什么这种BeanName的写法是正确可用的,那如果就使用原本的类名(首字母大写),作为BeanName可不可以获得ioc 中的对象呢?
    在这里插入图片描述
    从运行结果中,我们可以看返回的错误提示:没有名为'UserController'可用 所以说不能使用类名来充当BeanName.
    那么在idea中是如何定义BeanName的格式的呢?
    在这里插入图片描述

     public static String decapitalize(String name) {
            if (name == null || name.length() == 0) {
                return name;
            }
            if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&2
                            Character.isUpperCase(name.charAt(0))){
                return name;
            }
            char chars[] = name.toCharArray();
            chars[0] = Character.toLowerCase(chars[0]);
            return new String(chars);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    解读一下上述的代码起先这里的BeanName 起先是从类名变化来的

    1. 如果传来的BeanName 为 null 或者 BeanName 的字符串长度为0,那么就直接返回这个空字符串
    2. 如果类名的字符串程度大于1,并且类名的第一个字符和第二个字符都为大写,那么这个类名,就可以直接返回为BeanName
    3. 把类名的第一个字符转变成为小写,进行返回,返回之后的结果就是BeanName.

    在这里插入图片描述

    使用@Service(服务存储) 把Bean存储到spring ioc容器中

     package com.model;
    
    import org.springframework.stereotype.Service;
    @Service
    public class UserService {
        public void sayHi(){
            System.out.println("hi Service");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    使用@Repository(仓库存储) 把Bean存储到spring ioc 容器中

     package com.model;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserRepository {
        public void sayHi(){
            System.out.println("hi Repository");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    使用@Component(组件存储) 把Bean存储到spring ioc 容器中

     package com.model;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class UserComponent {
        public void sayHi(){
            System.out.println("hi Component");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    使用@Configuration(配置存储) 把Bean存储到spring ioc 容器中

     package com.model;
    
    import org.springframework.context.annotation.Configuration;
    
     
    @Configuration
    public class UserConfiguration {
        public void sayHi(){
            System.out.println("hi UserConfiguration");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    为什么要这么多类注解?

    我们可以在上述得代码中看到,这五大类注解的功能都是一样的,都是把所修饰的类的对象添加到spring ioc 容器中,那么为什么要规定这么多的注解功能相同,只是名字不同的注解呢?

    其实我们根据五大类注解的汉语意思也可以想到,@Controller 表示的是数据控制层,@Service 表示的是服务层,@Repository 数据持久层(操作数据库层),@Configuration 表示的是配置层。@Component 表示的是组件。 这样的分层是为以后管理代码,有了明确的规范,简单的说就是方便管理复杂程序中的代码,如果在一个复杂的项目中,所有的程序都写到一个类注解下,那么代码得有多长。

    在这里插入图片描述

    各个类注解之间的关系
    在这里插入图片描述
    我们可以在 @Controller @Service @ Configuration @ Respository 的源码中清晰的看到这四类注解分别都继承了@Component 注解

    ⽅法注解 @Bean

    那么如果在一个类中的一个具体的方法中,这个方法的返回值是一个具体对象的时候,需要把这个方法返回的对象添加到spring ioc 容器中进行管理,那么该怎么呢?
    使用@Bean 方法注解 但是光是用方法注解还是不行滴!,需要配合五大类注解中的一个,这样扫描类注解修饰的类的时候,才能扫描到这个@Bean注解所修饰的方法。

     package com.model;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    
     
    @Component
    public class UserBeans {
        @Bean
        public User retUser(){
            User user = new User();
            user.setUsername("张三");
            user.setPassword("123456");
            return user;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在User类中,声明了 username 用户名,password 用户密码 这个方法返回一个User对象。

    从spring ioc 容器中获得User对象

     public class App {
        public static void main(String[] args) {
              ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
              User user = context.getBean("retUser",User.class);
              System.out.println(user);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意在这里 在没有重命名BeanName之前,如果要从spring ioc 容器中的到user对象。那么此时的BeanName默认就是,在UserBeans类下的返回User对象的 方法名

    重命名 Bean
    在上述的代码中,如果要得到 ioc容器中的User对象,需要使用方法名作为BeanName,这样就不利于BeanName的规范,所以需要把BeanName重命名。

     @Component
    public class UserBeans {
        @Bean(name = "user")
        public User retUser(){
            User user = new User();
            user.setUsername("张三");
            user.setPassword("123456");
            return user;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    @Bean 注解提供了一个 name属性,可以重命名BeanName.
    在这里插入图片描述
    但是此处需要谨记,这里如果把BeanName 重命名之后,那么就不能那原来的方法名作为BeanName了,否则就会报错。

    获取 Bean 对象(对象装配)

    前面介绍了更简单的方法把Bean从spring ioc 容器中存进去,那么是否有从ioc 容器中把Bean取出来还有简单的方法呢?

    对象装配(对象注⼊)的实现⽅法以下 3 种:

    1. 属性注⼊
    2. 构造⽅法注⼊
    3. Setter 注⼊
      把spring ioc 中的一个对象,添加到一个对象中,于是我们就能联系到上述所说的 DI(依赖注入)
      在这里还是使用注解,一个注解是 @Autowired,另一个注解是@Resource

    属性注入

     package com.model;
    
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class UserConfiguration {
    @Bean(name = "user")
         public User retUser(){
             User user = new User();
             user.setUsername("lisi");
             user.setPassword("123456");
             return user;
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    使用@Autowired 属性注入

    package com.model;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
     
    
    @Repository
    public class UserRepository {
         @Autowired
         private UserConfiguration configuration;
         public User getUser(){
             User user = configuration.retUser();
             return user;
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用@Resource 属性注入

     package com.model;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Repository;
     
    
    @Repository
    public class UserRepository {
         @Resource
         private UserConfiguration configuration;
         public User getUser(){
             User user = configuration.retUser();
             return user;
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述
    这样就可以使用注解把spring ioc 容器中的对象取出来,在其他的类中调用这个对象原有类中的方法。

    构造方法方法注入

    使用@Autowired 构造方法注入

     package com.model;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
     
    @Controller
    public class UserController {
        private UserService userService;
        @Autowired
        public UserController(UserService userService){
            this.userService = userService;
        }
        //在UserController类中就可以调用UserService类中的方法
        public void say(){
            userService.sayHi();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里还要说上一嘴,其实当在使用构造方法注入的时候,如果只是在这个类中只涉及到一个属性注入,就是说就在该类中创建一个依赖关系,那么此时就不用@Autowried注解。
    在这里插入图片描述

    setter方法注入

    使用@Autowired setter方法注入

     package com.model;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    
    @Controller
    public class UserController {
        private UserService userService;
        @Autowired
        public void setUserService(UserService userService){
            this.userService = userService;
        }
        //在UserController类中就可以调用UserService类中的方法
        public void say(){
            userService.sayHi();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    使用 @Resource setter方法注入

     @Controller
    public class UserController {
        private UserService userService;
        @Resource
        public void setUserService(UserService userService){
            this.userService = userService;
        }
        //在UserController类中就可以调用UserService类中的方法
        public void say(){
            userService.sayHi();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    经典面试题:属性注入和构造方法注入以及setter方法注入的区别

    • `属性注入特点写法简单,但是通用性不好,它只能运行在 ioc 容器中,如果是非 ioc 容器就会出现问题
    • 早期spring版本推荐的写法是setter方法注入 ,但是在后期发现,setter方法注入的通用性没有构造方法通用。
    • 构造方法注入通用性更好,它确保在使用注入对象之前,此注入对象是一定被初始换化过了,当构造方法注入的参数过多时,此时开发者就要检查自己所写的代码是否符合单一设计原则的规范了,此注入方法是spring后期版本中推荐的注入方法。

    经典面试题: @Autowired 和 @Resource 的区别

    • 出身不同:@Resource 来自JDK @AutoWired是spring 框架提供的
    • 用法不同:@AutoWried 支持属性注入,构造方法注入和setter方法注入,而Resource不支持构造方法注入。
    • 支持的参数不同:@Resource 支持更多的参数,比如name,type设置,而AutoWried只支持required参数设置

    同⼀类型多个 Bean 报错处理

    当出现以下多个 Bean,返回同⼀对象类型时程序会报错

    在这里插入图片描述
    没有符合条件的类型为“ com.model. User”的 bean 可用: 预期的单个匹配 bean,但找到了2: user1,user2
    在这里还需要说明一点,如果使用的是属性注入,那么就要从ioc容器中找到对应的对象,那么此时@Autowried默认是根据类的类型,在spring ioc 容器中查找对象的。那么此时我们使用@Service 和@Bean注解 在UserService类中,往spring ioc 容器中添加了两个相同的类型的对象,类型都是User的,那么此时@AutoWried即使是找到对象了,但是他不是到要哪个Bean,所以就会报出上述的错误。
    那么此时 就不得不介绍一下 @Qualifier,在这个注解中提供了一个value属性,此时我们就可以指定从ioc容器中取出我们想要Bean
    在这里插入图片描述
    那么如果此时我们使用的是@Resouse 也 进行的是属性注入,这个注解是默认按照BeanName在ioc容器中找到Bean的,还是上述的情形,那么该怎样才能从spring ioc 容器中取到相应的Bean呢?

    其实在@Resouse注解中提供了一个属性name="",通过这个属性就可以找到相应的Bean

    在这里插入图片描述

    4.Bean 作⽤域

    我们所熟知的作用域也许是这样的:
    作用域在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。

     public class test {
        public static void main(String[] args) {
            {
                int a = 10;
            }
            System.out.println(a);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上述的代码,是会出现编译时错误的,因为变量a 不是一个全局变量。
    那么接下来就介绍一个Bean的作用域:是指 Bean 在 Spring 整个框架中的某种⾏为模式
    请看一下代码:
    在这里插入图片描述

    运行结果:
    在这里插入图片描述

    此时打印出来的居然是两个一样的结果,这是为什么呢?
    操作以上问题的原因是因为 Bean 默认情况下是单例状态(singleton) 也就是所有⼈的使⽤的都是同 ⼀个对象,使⽤单例可以很⼤程度上提⾼性能,所以在 Spring 中 Bean 的作⽤域默认也是 singleton 单例模式。singleton 单例作⽤域,就表 示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个 ⼈读取到的就是被修改的值

    Bean 的 6 种作⽤域

    Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。Spring有 6 种作⽤域, 最后四种是基于 Spring MVC ⽣效的:

    1. singleton:单例作⽤域
    2. prototype:原型作⽤域(多例作⽤域)
    3. request:请求作⽤域
    4. session:回话作⽤域
    5. application:全局作⽤域
    6. websocket:HTTP WebSocket 作⽤域

    singleton

    • 官⽅说明:(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.

    • 描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀ 个对象

    • 场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新

    • 备注Spring默认选择该作⽤域

    prototype

    • 官⽅说明:Scopes a single bean definition to any number of object
      instances.
    • 描述每次对该作⽤域下的Bean的请求都会创建新的实例:获取Bean(即通过
      applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是新的对象实例。
    • 场景:通常有状态的Bean使⽤该作⽤域

    request

    • 官⽅说明:Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
    • 描述每次http请求会创建新的Bean实例,类似于prototype
    • 场景⼀次http的请求和响应的共享Bean
    • 备注:限定SpringMVC中使⽤

    session

    • 官⽅说明:Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
    • 描述在⼀个http session中,定义⼀个Bean实例
    • 场景:⽤户回话的共享Bean, ⽐如:记录⼀个⽤户的登陆信息
    • 备注:限定SpringMVC中使⽤

    application(了解)

    • 官⽅说明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext
    • 描述在⼀个http servlet Context中,定义⼀个Bean实例
    • 场景:Web应⽤的上下⽂信息,⽐如:记录⼀个应⽤的共享信息
    • 备注:限定SpringMVC中使⽤

    websocket(了解)

    • 官⽅说明:Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
    • 描述在⼀个HTTP WebSocket的⽣命周期中,定义⼀个Bean实例
    • 场景:WebSocket的每次会话中,保存了⼀个Map结构的头信息,将⽤来包裹客户端消息头。第⼀次初始化后,直到WebSocket结束都是同⼀个Bean。
    • 备注:限定Spring WebSocket中使⽤

    singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域;
    singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。

    设置 Bean 的作用域有两种方式

    • 在 Spring 配置文件中,对于注册的每一个 Bean 可以增加一个 scope 属性来设置 Bean 的作用域
    • 通过 @Scope 注解就可以声明 Bean 的作用域,它既可以修饰方法也可以修饰类

    第一种方法:
    在这里插入图片描述
    每次对该作⽤域下的Bean的请求都会创建新的实例 还可以使用@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

    第二种方法:
    在这里插入图片描述

    Bean 执行流程

    在这里插入图片描述
    Bean 执⾏流程(Spring 执⾏流程):启动 Spring 容器 -> 实例化 Bean(分配内存空间,从⽆到有) -> Bean 注册到 Spring 中(存操作) -> 将 Bean 装配到需要的类中(取操作)

    5.Bean的生命周期

    所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期

    Bean 的⽣命周期分为以下 5 ⼤部分: Bean ⽣命周期

    1. 实例化 Bean(为 Bean 分配内存空间)
    2. 设置属性(Bean 注⼊和装配)
    3. Bean 初始化 实现了各种 Aware 通知的⽅法,如 BeanNameAware、BeanFactoryAware、 ApplicationContextAware 的接⼝⽅法执⾏ BeanPostProcessor 初始化前置⽅法; 执⾏
      @PostConstruct 初始化⽅法,依赖注⼊操作之后被执⾏; 执⾏⾃⼰指定的 init-method ⽅法(如果有指定的话);执⾏ BeanPostProcessor 初始化后置⽅法。
    4. 使⽤ Bean
    5. 销毁 Bean
     @Component
    public class BeanLife  implements BeanNameAware  {
        public void setBeanName(String s) {
            System.out.println("执⾏了 setBeanName ⽅法:" + s);
        }
       @PostConstruct
       public void postConstruct() {
          System.out.println("执⾏ PostConstruct()" + "初始化方法");
       }
       public void init() {
          System.out.println("执⾏ BeanLifeComponent init-method" + "初始化");
       }
       @PreDestroy
       public void preDestroy() {
          System.out.println("执⾏:preDestroy()" + "销毁");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
     <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:content="http://www.springframework.org/schema/context"
              xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
             <content:component-scan base-package="com.bit.component">
    </content:component-scan>
             <beans>
     <bean id="beanLifeComponent"
                 class="com.model.BeanLife" init-method="init"></bean>
            </beans>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

  • 相关阅读:
    卷四 周纪四
    指针和数组试题解析(4)字符数组部分续集
    脚手架安装
    uniapp 开发公众号 h5(openid,微信支付,订阅通知)
    k8s部署kafka,并使用zookeeper做注册中心
    “要疯”六年,安踏与年轻人疯出“新宇宙”
    长江智城智慧平台框架的规划与实践
    Spring 源码(11)Spring Bean 的创建过程(2)
    Docker的运用场景 103.53.124.2
    文件包含总结
  • 原文地址:https://blog.csdn.net/qq_54883034/article/details/126458733