SpringIOC入门,基于XML和基于注解实现

目录

什么是IOC

IOC的作用

所有系统都需要引入 IOC 吗?

IOC容器

容器的分类

IOC原理解析

IOC执行流程图

​编辑 基于XML实现的IOC案例

基于注解实现的IOC案例

Bean知识点

xml中基础知识

标签和属性

 属性注入的几种方式

Bean的生命周期

FactoryBean


什么是IOC

IOC(Inverse of Controll)控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。

提到IOC,不得不提的是DI(Dependency Injection )依赖注入,是IOC的具体实现

  • 控制的是什么?    JavaBean(Java对象)的创建和管理
  • 反转的是什么?    一般情况下对象的创建和管理由开发者控制,反转是把对象的创建和管理交给容器完成,然后再交给开发者。

简言之:原来是自己去做,现在是别人给你做好了送过来

IOC的作用

IOC的主要作用是:解耦。

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

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

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

所有系统都需要引入 IOC 吗?

IoC 容器是面向 迭代 起作用,如果你的应用就 不存在迭代 的情况,即系统是万年不变的,那没必要引入 IoC,因为你每引入一项技术,都势必会增加复杂度,所以额外引入 IoC 也一样会增加你整体应用的复杂度,所以假如 不存在迭代,大可直接写死A类引用B类,B类又写死引用C类,无需引入 IoC

当然,实际上我们大部分应用是 持续迭代 的,在类实现上、互相引用上、甚至接口协议上都有可能变化,所以一般引入 IoC 是合适的(如果是接口协议变化,即参数或返回值发生变化,那还是需要改动类间的代码的)。

在做架构设计的时候记住两个原则:合适简单

IOC容器

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

本质上就是对象工厂

容器的分类

Spring的IOC容器分为两种:提供两种实现方式(两个接口)

1)BeanFactory 接口

顶层的、最简单的容器

是Spring内部的使用接口,不提供开发人员进行使用

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

2)ApplicationContext 接口

继承了BeanFactory ,提供更多更强大的功能,一般由开发人 员进行使用

添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由 org.springframework.context.ApplicationContext 接口定义。通常推荐使用 ApplicationContext。

ApplicationContext 接口的主要实现:

1) FileSystemXmlApplicationContext 基于文件系统中XML文件配置的应用程序上下文 (以XML文件配置,可以放在任何地方)

2) ClassPathXmlApplicationContext 基于ClassPath路径中XML文件配置的应用程序上下文 (以XML文件配置,只能放在类路径下,也就是项目里,一般使用这种)

3) AnnotationConfigApplicationConext 基于注解配置的应用程序上下文 (以注解形式进行配置,用得也比较多)

两者区别:

  • BeanFactory加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

  • ApplicationContext加载配置文件时候就会把在配置文件对象进行创建

在使用过程中,推荐使用后者,将耗时耗资源的事情放在服务器启动时操作,不放在执行其他业务代码中进行操作

IOC原理解析

通过工厂模式+反射机制实现(注解配置需要使用自定义注解,XML配置需要XML解析

和之前的做对比:

解耦:出现了工厂模式,但是耦合度还是不够低

IOC原理: 

IOC执行流程图

 基于XML实现的IOC案例

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

创建Maven项目,导入相关依赖

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.2.8.RELEASE</version>
</dependency>
//CPU接口
public interface Cpu {
    void run();
}
//CPU接口的两个实现类
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 Computer(){}
    public Computer(String brand,Cpu cpu,Memory memory){
        this.brand = brand;
        this.cpu = cpu;
        this.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();
    }
}

在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">
    <!--Java对象配置-->
	<!-- CPU对象 -->
    <bean id="cpu" class="com.blb.ioc_demo.AMDCpu"></bean>
    <!-- Memory对象 -->
    <bean id="memory" class="com.blb.ioc_demo.KingstonMemory"></bean>
    <!-- Computer对象   set方法注入 -->
    <bean id="computer" class="com.blb.ioc_demo.Computer">
        <!-- 依赖注入:value用来注入值类型,ref用来注入引用类型 -->
        <property name="brand" value="联想"></property>
        <!-- ref值对应上面bean标签中的id -->
        <property name="cpu" ref="cpu"></property>
        <property name="memory" ref="memory"></property>
    </bean>
</beans>

测试代码

public class TestComputerSpring {
    public static void main(String[] args) {
        //创建应用程序上下文(容器)
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        //获得对象:通过id来找
        Computer computer = (Computer) context.getBean("computer");
        //第二种获取对象的方式:通过类型来找
        computer = context.getBean(Computer.class);
        computer.start();
    }
}

基于注解实现的IOC案例

修改原来的代码,添加一些注解

  • @Component 组件, 被标记的会被Spring扫描到,交给Spring容器进行管理

  • @ComponentScan 组件扫描,用来扫描被@Component标记的类,也可以标记在配置类上(被@Configuration标记的类)

  • @Configuration 配置类,标记在类上,该类作为配置类替代xml

  • @Value 注入值类型数据,配置在属性或set方法上

  • @Autowired 自动装配,只能按类型进行注入

  • @Resource 自动装配,默认按名字进行注入,如果找不到再类型进行注入,最重要的两个属性:name 和 type

  • @Qualifier 标记名称,配置在类和注入属性上,用于区分类型相同的对象

  • @Repository 类似@Component,标记DAO实现类

  • @Service 类似@Component,标记Service实现类

  • @Controller 类似@Component,标记Controller类(注意,Servlet不能被标记)

@Component注解相当于告诉 IoC 容器:这个类你需要帮我创建和管理;而 @Autowire 注解相当于告诉 IoC 容器:我需要依赖这个类,你需要帮我注入进来。

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

//使用@Autowired注入,不能配置两个同样类型的@Component,否则会报错,因为是按照类型进行注入的
@Component
public class IntelCpu implements Cpu {
    public void run() {
        System.out.println("英特尔CPU在运行。。。");
    }
}

//两个都用了@Component注解,那么可以使用@Qualifier进行区分,也可以使用@Resource解决
@Qualifier("kingston")
@Component
public class KingstonMemory implements Memory {
    public void read() {
        System.out.println("金士顿内存读取数据");
    }

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

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

    public void write() {
        System.out.println("三星内存写入数据");
    }
}
//电脑类
@Component
public class Computer {
    @Value("戴尔")
    private String brand;
    //@Autowired
    //如果名字找不到,就会按类型查找
    @Recource(name="Intel",type=IntelCpu.class)
    private Cpu cpu;
    @Qualifier("sumsung")
    @AutoWired
    private Memory memory;
    public Computer(){}
    public Computer(String brand,Cpu cpu,Memory memory){
        this.brand = brand;
        this.cpu = cpu;
        this.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();
    }
}

配置类,代替spring.xml的作用

//设置基础包,扫描包下的所有文件
@ComponentScan(basePackages="com.blb")
@Configuration
public class SpringConfig{
    public static void main(String[] args){
        //创建基于注解的上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        //获得对象
        Computer computer = context.getBean(Computer.class);
        computer.start();
    }
}

Bean知识点

xml中基础知识

标签和属性

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

bean节点代表一个javabean对象

bean节点的相关属性:

  • id 对象id,id不能重复
  • name 对象名称,name可以重复,如果重复将读取最后一个
  • class 对象的类型,包名+类名
  • scope 作用域
<bean id="computer" class="com.blb.ioc.Computer" autowire="byType" scope="prototype">
	<property name="brand" value="联想"></property>
</bean>

作用域:

  • singleton(默认)单例模式,在IOC容器中仅存在一个实例。加载 spring 配置文件时候就会创建单实例对象

  • prototype  多例,每次从IOC容器调用Bean时,都会返回一个新的实例。不是在加载 spring 配置文件时候创建对象,在调用 getBean 方法时候创建多实例对象

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

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

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

property 节点用来配置javabean的属性依赖

property的相关属性:

  • name 属性的名称

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

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

 属性注入的几种方式

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

     <bean id="computer" class="com.blb.ioc_demo.Computer">
         <!-- 依赖注入:value用来注入值类型,ref用来注入引用类型 -->
         <property name="brand" value="联想"></property>
         <!-- ref值对应上面bean标签中的id -->
         <property name="cpu" ref="cpu"></property>
         <property name="memory">
             <!-- 也可以直接在里面声明 -->
             <bean id="memory" class="com.blb.ioc_demo.KingstonMemory"></bean>
         </property>
     </bean>

2) 构造方法注入

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

 <!-- 构造方法注入属性 -->
 <bean id="computer" class="com.blb.ioc.Computer">
     <constructor-arg name="brand" value="苹果"></constructor-arg>
     <constructor-arg name="cpu" ref="cpu"></constructor-arg>
     <constructor-arg name="memory" ref="memory"></constructor-arg>
 </bean>

3) 自动装配

可以通过bean的autowire属性配置

属性值:

  • no 默认,不自动装配

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

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

  • constructor 通过构造方法装配

 <!-- 注入不了值,brand无法注入,需要手动注入 -->
 <bean id="computer" class="com.blb.ioc.Computer" autowire="byType">
     <property name="brand" value="惠普"></property>
 </bean>
 ​
 <bean id="computer" class="com.blb.ioc.Computer" autowire="byName">
     <property name="brand" value="惠普"></property>
 </bean>
 ​
 <bean id="computer" class="com.blb.ioc.Computer" autowire="constructor">
     <constructor-arg name="brand" value="苹果"></constructor-arg>
 </bean>

4) p 名称空间注入(了解) 本质还是set方法注入

 
<?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:p="http://www.springframework.org/schema/p"   -- 添加 p 名称空间在配置文件中
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 ​
     <!-- 进行属性注入,在 bean 标签里面进行操作 -->
     <bean id="computer" class="com.blb.ioc.Computer" p:brand="惠普" >
         <!-- 对象类型的属性,还是要使用property -->
         <property name="cpu" ref="cpu"></property>
         <property name="memory" ref="memory"></property>
     </bean>
 </beans>

Bean的生命周期

什么是生命周期?

从对象创建到对象销毁的过程

Bean的生命周期是什么?

servlet 生命周期(容器启动装载并实例化servlet类,初始化servlet,调用service方 法,销毁servlet)。

在Spring中,Bean的生命周期包括Bean的定义、初始化、使用和销毁4个阶段

(1)通过构造器创建 bean 实例(无参数构造)

(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

(3)调用 bean 的初始化的方法(需要进行配置初始化的方法)

(4)bean 可以使用了(对象获取到了)

(5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

FactoryBean

Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)

普通 bean:在配置文件中定义 bean 类型就是返回类型

工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样

//第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
 public class MyBean implements FactoryBean<User> {
     //第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
     @Override
     public User getObject() throws Exception {
         User user = new User();
         user.setName("dzd");
         return user;
     }
 ​
     @Override
     public Class<?> getObjectType() {
         return null;
     }
 ​
     @Override
     public boolean isSingleton() {
         return false;
     }
 }
<bean id="mybean" class="com.pipi.spring5.MyBean"></bean>
 //测试
     @Test
     public void test02(){
         ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
         User myBean = context.getBean("mybean",User.class);//mybean对应bean的id,可省略
         System.out.println(myBean.getName());
     }
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Stella呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值