快速入门Spring之SpringIOC

学习目标

1、Spring框架简介
2、IOC概念
3、IOC容器
4、IOC的XML配置案例
5、IOC的注解配置案例
6、IOC的实现原理

1、Spring框架简介

1.1 Spring基本概念

Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
  • 轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:Spring应用中的对象不依赖于Spring的特定类。

  • 控制反转——通过控制反转(IoC)技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。

  • 面向切面——分离应用的业务逻辑与系统级服务。应用对象只实现业务逻辑。它们并不负责其它的系统级关注点,例如日志或事务支持。

  • 容器——Spring是一种容器,能创建对象并管理对象的配置和生命周期。

  • 框架——Spring可以将第三方框架灵活的整合起来,完成各种JavaEE企业级项目开发。

    所有Spring的这些特征使我们能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。

1.2 Spring框架的组成

在这里插入图片描述

上面是Spring的主要组件:

  • Core 核心组件 ,提供IOC功能
  • Context 上下文组件,提供包的扫描、国际化、事件等功能
  • Beans JavaBean的管理
  • SpEL Spring表达式
  • AOP 面向切面编程
  • Test 集成单元测试
  • MVC SpringMVC框架
  • JDBC 集成JDBC开发
  • ORM 数据库开发
  • Cloud 微服务开发框架
  • Securty 权限控制框架

2、IOC的概念

2.1 什么是IOC

IOC(Inverse of Control)控制反转是一种程序设计思想。

控制是什么?

​ JavaBean(Java对象)的创建和管理

反转是什么?

​ 一般情况下对象的创建和管理由开发者控制,反转是把对象的创建和管理交给容器完成,然后再交给开发者。

2.2 IOC有什么用

IOC的主要作用是:解耦。

解耦,是降低程序耦合度,也就是减少程序代码之间的依赖性,如果代码之间的依赖性很高,修改一处代码会影响很多其他的代码,这就给项目的稳定性带来的问题,不利于代码的扩展和维护。

没有IOC的程序中,我们使用new来完成对象的创建,如果需要的对象的类型发生改变,就需要手动修改代码。

有了IOC后,对象的创建由第三方(Spring容器)完成,由Spring来管理应用中所有对象的生命周期,开发者只需要关注自己的业务逻辑,代码更利于扩展和维护。

2.3 什么是DI

DI(Dependency Injection)依赖注入,是IOC另一种说法,也就是对象由容器来管理,注入到我们需要依赖中。

生活中的DI:我们身体不生产疫苗,由医院生产,需要的使用再注射进来。

3、IOC容器

3.1 容器的概念

IOC容器是 Spring 框架的核心。容器将创建对象,配置对象,并管理对象的整个生命周期。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。

3.2 容器的分类

Spring的IOC容器分为两种:

1)BeanFactory

最简单的容器,给 DI 提供了基本的支持,它用 org.springframework.beans.factory.BeanFactory 接口来定义。BeanFactory 或者相关的接口,如 BeanFactoryAware,InitializingBean,DisposableBean,在 Spring 中仍然存在具有大量的与 Spring 整合的第三方框架的反向兼容性的目的。

2)ApplicationContext

继承了BeanFactory,添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由 org.springframework.context.ApplicationContext 接口定义。通常推荐使用 ApplicationContext。
ApplicationContext 接口的主要实现:
1) FileSystemXmlApplicationContext 
	文件绝对路径 D:\xx\xxx
	基于文件系统中XML文件配置的应用程序上下文
2) ClassPathXmlApplicationContext 
	src  resources --> classpath
	文件相对路径
	基于ClassPath路径中XML文件配置的应用程序上下文
3) AnnotationConfigApplicationConext 
	基于注解配置的应用程序上下文

4、IOC的入门案例

4.1 案例概述

在这里插入图片描述

模拟电脑的装配,定义Cpu和Memory(内存)接口,Cpu接口有IntelCpu和AMDCpu两个实现类,Memory接口有KingstonMemory和SumsungMemory两个实现类,Computer类中定义了Cpu和Memory类型的两个属性

4.2 案例实现

  1. 创建Maven项目

  2. 导入依赖

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.2.8.RELEASE</version>
</dependency>
  1. 编写代码
/**
 * CPU接口
 */
public interface Cpu {

    void run();
}
public class AMDCpu implements Cpu {
    public void run() {
        System.out.println("AMD的CPU在运行。。。");
    }
}

public class IntelCpu implements Cpu {
    public void run() {
        System.out.println("英特尔CPU在运行。。。");
    }
}
/**
 * 内存
 */
public interface Memory {

    void read();
    void write();
}

public class KingstonMemory implements Memory {
    public void read() {
        System.out.println("金士顿内存读取数据");
    }

    public void write() {
        System.out.println("金士顿内存写入数据");
    }
}

public class SumsungMemory implements Memory {
    public void read() {
        System.out.println("三星内存读取数据");
    }

    public void write() {
        System.out.println("三星内存写入数据");
    }
}
/**
 * 电脑
 */
public class Computer {

    private String brand;

    private Cpu cpu;

    private Memory memory;

    public Cpu getCpu() {
        return cpu;
    }

    public void setCpu(Cpu cpu) {
        this.cpu = cpu;
    }

    public Memory getMemory() {
        return memory;
    }

    public void setMemory(Memory memory) {
        this.memory = memory;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public void start(){
        System.out.println(brand + "电脑启动了!");
        cpu.run();
        memory.read();
        memory.write();
    }
}
  1. 添加Spring配置文件

在resources目录下,添加Spring配置文件

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cpu" class="com.blb.ioc_demo.AMDCpu"></bean>
    <bean id="memory" class="com.blb.ioc_demo.KingstonMemory"></bean>
    <!--Java对象配置-->
    <bean id="computer" class="com.blb.ioc_demo.Computer">
        <property name="brand" value="联想"></property>
        <property name="cpu" ref="cpu"></property>
        <property name="memory" ref="memory"></property>
    </bean>
</beans>
  1. 测试代码
public class TestComputerSpring {

    public static void main(String[] args) {
        //创建应用程序上下文
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        //获得对象
        Computer computer = (Computer) context.getBean("computer");
        computer.start();
    }
}

在这里插入图片描述

4.3 知识点讲解

4.3.1 beans 节点

​ beans节点是Spring配置文件的根节点,所有Javabean都定义在beans内部

4.3.2 bean 节点

​ 代表一个javabean对象
​ bean节点的属性

  • id 对象id,id不能重复

  • name 对象名称,name可以重复,如果重复将读取最后一个

  • class 对象的类型,包名+类名

  • scope 作用域

    • singleton

      单例模式,在IOC容器中仅存在一个实例

    • prototype

      多例,每次从IOC容器调用Bean时,都会返回一个新的实例

    • request

      每次Http请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境

    • session

      同一个会话共享一个Bean,不同的会话使用不同的Bean,仅适用于WebApplicationContext环境

    • application

      一般用于portlet应用环境,仅适用于WebApplicationContext环境

4.3.3 property 节点

​ 用来配置javabean的属性依赖

​ property节点的属性

  • name 属性的名称

  • ref 注入引用类型的对象,值就是前面定义bean的id

在这里插入图片描述

  • value 注入基本数据类型和String类型的值,如给brand的值注入“联想”

4.3.4 属性注入的几种方式

​ 注入属性有几种方式:

​ 1) set方法注入,就是上面通过property进行配置

​ 2) 构造方法注入

​ 给Computer类添加带参数的构造方法,将property改为:

<constructor-arg name="brand" value="苹果"></constructor-arg>
<constructor-arg name="cpu" ref="cpu"></constructor-arg>
<constructor-arg name="memory" ref="memory"></constructor-arg>

​ 3) 自动装配

​ 可以通过bean的autowire属性配置

​ 类型:

  • no 默认,不自动装配

  • byType 通过类型查找对象,如果相同类型的对象有多个,会出现异常

  • byName 通过名称查找对象,如果找不到对应的id或name的对象,会出现空指针异常

  • constructor 通过构造方法装配

4.3.5 ClassPathXmlApplicationContext

​ 通过ClassPath路径查找XML配置文件的IOC容器

  • 创建方式
new ClassPathXmlApplicationContext("spring.xml");
注意:src和resources都在ClassPath下,所以可以直接写文件名,如果文件不在ClassPath下,则会出现异常
  • getBean方法

​ 用于在配置文件中查找对象,创建对象,返回对象

Object getBean(Class)	通过对象的类型查找

Object getBean(String)	通过对象id或name查找

5、注解配置的IOC案例

5.1 Spring的重要的注解

  • @Component 组件,被标记的类会被Spring扫描到,交给Spring容器进行管理
  • @ComponentScan 组件扫描,标记在配置类上,用于扫描某一个包下带@Component的类
  • @Configuration 配置类,标记在类上,该类作为配置类代替XML
  • @Value 注入值类型数据,配置属性或set方法上
  • @Autowrie 自动装配,默认按类型进行注入
  • @Qualifier 标记名称,配置在类和注入属性上,用于区分类型相同的对象
  • @Resource 自动装配,类似Autowired,默认按名称注入,名称没有再按类型注入
  • @Repository 类似@Component,标记DAO实现类
  • @Service 类似@Component,标记Service实现类
  • @Controller 类似@Component,标记Controller类

@Autowire和@Resource的区别

1) @Autowire按类型注入;@Resource默认按名称注入,名称没有再按类型

​ @Resource不设置名称,默认按属性名查找,指定名称 @Resource(name=“名称”)

2) @Autowire是Spring提供的;@Resource是JAVAEE的

BeanFactory和FactoryBean的区别

BeanFactory是Spring的顶层IOC容器,用于创建和管理对象

FactoryBean是一种JavaBean,提供创建对象的方法,在定义对象时使用factory-bean和factory-method指定对象的创建方式

/**
 * 工厂对象,提供组件的创建方法
 */
public class ComputerFactoryBean {

    public ICpu getAMDCpu(){
        return new AMDCpu();
    }

    public ICpu getIntelCpu(){
        return new IntelCpu();
    }

    public IMemory getKingstonMemory(){
        return new KingstonMemory();
    }

    public IMemory getSumsungMemory(){
        return new SumsungMemory();
    }
}
<bean id="factory" class="com.hopu.computer.ComputerFactoryBean"></bean>

<bean id="cpu" factory-bean="factory" factory-method="getIntelCpu"></bean>

bean的属性:init-method(初始化)、destroy-method(销毁)

5.2 注解实现IOC

修改上面的案例,给每个接口的一个实现类加上@Component注解

@Component
public class AMDCpu implements Cpu {
    public void run() {
        System.out.println("AMD的CPU在运行。。。");
    }
}

@Component
public class KingstonMemory implements Memory {
    public void read() {
        System.out.println("金士顿内存读取数据");
    }

    public void write() {
        System.out.println("金士顿内存写入数据");
    }
}

给Computer类添加@Component注解,值类型属性用@Value注入,引用类型用@Autowired注入

@Component
public class Computer {

    @Value("戴尔")
    private String brand;

    @Autowired
    private Cpu cpu;

    @Autowired
    private Memory memory;
/**
 * 配置类
 */
@ComponentScan(basePackages = "com.blb.ioc_demo")
@Configuration
public class ComputerConfig {

    public static void main(String[] args) {
        //创建注解应用程序上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComputerConfig.class);
        //获得对象
        Computer computer = context.getBean(Computer.class);
        computer.start();
    }
}

思考问题

如果给每个接口的所有实现类都加上@Component注解会出现什么问题?如何解决?

6、 IOC的实现原理

6.1 思考

思考问题1

上面案例中,配置了@Component注解后,Spring是怎么知道这个类上有这个注解的?

思考问题2

没有使用new关键字,Spring是如何创建对象的?

思考问题3

Spring是如何将创建的对象注入到对应的属性中的?

6.2 IOC实现原理

问题的答案是:通过反射机制+工厂模式实现

工厂模式是GOF23设计模式之一,主要封装JavaBean的创建过程

自定义注解

@Target(ElementType.值)  //Target元注解 目标  值:FIELD 属性,METHOD 方法,TYPE 类、接口
@Retention(RetentionPolicy.值)  //时机 值:SOURCE 源代码 ,RUNTIME 运行时
@Documented
public @interface 注解名{
	类型 方法名();
}
/**
 * 注入值的注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValue {
    //注入的值
    String value();
}
/**
 * 注入对象的注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
	//注入的类型
    Class value();
}
/**
 * 电脑
 */
public class Computer {

    @MyValue("戴尔")
    private String brand;

    @MyComponent(IntelCpu.class)
    private Cpu cpu;

    @MyComponent(SumsungMemory.class)
    private Memory memory;
    ...省略其余部分...
/**
 * 电脑工厂
 */
public class ComputerFactory {

    /**
     * 创建电脑对象
     * @param computerClass
     * @return
     */
    public Computer createComputer(Class computerClass) throws Exception {
        //反射创建对象
        Object computer = computerClass.newInstance();
        //遍历所有的属性
        Field[] fields = computerClass.getDeclaredFields();
        for(Field field : fields){
            String fName = field.getName();
            //读取自定义注解
            MyValue myValue = field.getDeclaredAnnotation(MyValue.class);
            if(myValue != null){
                //通过反射调用set方法注入值
                String mName = "set" + fName.substring(0,1).toUpperCase() + fName.substring(1);
                Method set = computerClass.getDeclaredMethod(mName, field.getType());
                set.invoke(computer,myValue.value());
            }
            MyComponent myComponent = field.getDeclaredAnnotation(MyComponent.class);
            if(myComponent != null){
                //通过反射调用set方法注入对象
                String mName = "set" + fName.substring(0,1).toUpperCase() + fName.substring(1);
                Method set = computerClass.getDeclaredMethod(mName, field.getType());
                //通过配置的类型创建对象
                Object obj = myComponent.value().newInstance();
                set.invoke(computer,obj);
            }
        }
        return (Computer) computer;
    }
}
public class TestFactory {

    public static void main(String[] args) {
        ComputerFactory factory = new ComputerFactory();
        try {
            Computer computer = factory.createComputer(Computer.class);
            computer.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p32sWGl6-1654389340507)(SpringIOC.assets/1607587570167.png)]

面试题:

  1. Spring是什么?
  2. Spring有哪些主要组件?
  3. 介绍一下IOC
  4. 介绍Spring的容器有哪些?
  5. @Autowired和@Resource的区别
  6. 自动装配有几种方式
  7. 属性注入有几种方式
  8. BeanFactory和FactoryBean的区别
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值