理解概念比死记硬背更有效:Spring框架的IOC设计思想+Spring的Bean管理

Posted Lotus_dong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解概念比死记硬背更有效:Spring框架的IOC设计思想+Spring的Bean管理相关的知识,希望对你有一定的参考价值。

Spring框架

IOC(控制反转)

IOC:即Inverse of Control,控制反转是一种设计思想,就是将原本在程序中手动创建对象的控制权交给Spring框架来管理。 它的作用是降低对象间的耦合度,底层实现依靠解析xml文件或扫描注解标签+工厂模式+反射机制。

IOC容器具有依赖注入功能,负责对象的实例化、对象初始化、对象之间依赖关系配置、对象的销毁,对外提供对象的查找等操作,对象的整个生命周期都由容器来控制。我们需要使用的对象不需要通过new一个实例的方法去创建,而是由IOC直接帮我们组装好,需要时从IOC容器中获取即可。

理解两个概念:正控即由我们自己去创建要使用的某个对象;反控即直接从Spring容器中取得需要使用的对象,不关心对象的创建过程,也就把创建对象的控制权反转给了Spring框架。

一个简单的例子大家看一下

1.先建立一个实体类:

public class User {
    private String name;
    private Integer age;

    public User() {
        System.out.println("User类的无参构造方法已创建!!!");
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public void work() {
        System.out.println(this.name+"今年"+this.age+"岁");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

2.我们先在Spring的配置文件中配置java类:

<?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中的类交给Spring统一管理,由Spring容器生成对象-->
    <bean id="user" class="com.cwd.spring1pro.bean.User"></bean>

</beans>

3.在使用中通过Spring框架直接获取对象

@Test
public void test1() {
    /*IOC控制反转:在程序中不需要我们去new对象,由Spring容器创建并管理*/
	/*读取spring的相关xml文件,加载spring配置文件,启动Spring框架(Spring容器)*/
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

    User user = (User) applicationContext.getBean("user");
    System.out.println(user);
}

Spring框架的Bean管理

Spring中的bean指的是由Spring框架生成并管理的对象。Spring底层的实现依靠解析xml文件或扫描注解标签+工厂模式+反射机制。那我们在对java类(Bean)的管理中也需要对其进行配置,具体配置方式要么是配置xml文件,要么是配置注解标签。

基于xml配置方式

使用基于xml的配置方式来进行Bean管理,我们通常使用bean标签,

<!--将java中的类交给Spring统一管理,由Spring容器生成对象-->
<bean id="user" class="com.cwd.spring1pro.bean.User"></bean>

bean标签的常用属性:

(1)id=“生成对象的名称”

(2)class=“java类的全类名”

(3)name=“生成对象的名称” ,可以给多个名称

<bean id="user" class="com.cwd.spring1pro.bean.User" name="user1,user2" ></bean>

(4)scope=“作用域”,这里主要说一下scope的取值

  • singleton(默认):单例的,Spring框架在它的容器中只创建了一个实例,区分单例和单例模式,单例模式指的是类中构造方法私有。
<bean id="user" scope="singleton" class="com.cwd.spring1pro.bean.User" name="user1,user2" ></bean>

测试:

@Test
public void test3() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");

    User user = (User) applicationContext.getBean("user");
    System.out.println(user);
    User user1 = (User) applicationContext.getBean("user1");
    System.out.println(user1);
    User user2 = (User) applicationContext.getBean("user2");
    System.out.println(user2);
}

在这里插入图片描述

  • prototype:原型的(多例的),每一次获得对象(getBean)时Spring容器才会创建对象。
<bean id="user" scope="prototype" class="com.cwd.spring1pro.bean.User" name="user1,user2" ></bean>

测试:还是上面的测试代码

在这里插入图片描述

  • 还有其它的作用域取值这里就不进行展示了,如下图。

在这里插入图片描述

依赖注入

Spring框架创建对象对象叫做控制反转,在创建对象时我们为创建的对象赋值我们就叫做依赖注入,就是将对象依赖属性(简单值,集合,对象)通过配置设置给该对象。它们两个是相互依存的关系。这里的内容比较简单,但也比较多,我们这里只做一些简单演示,详细内容大家到Spring核心技术网站看。

1.实体类set方法注入

首先我们需要在实体类中对属性进行set方法的重写,这是实现set方法注入的前提。

(1)简单值注入。

使用property属性标签注入,name表示实体类中的属性,value表示给属性注入的值。

<bean id="user" class="com.cwd.spring1pro.bean.User">
    <property name="name" value="Tom"></property>
    <property name="age" value="22"></property>
</bean>

(2)集合注入

这里我们只以List集合和Map集合为例。List集合直接使用list标签和value标签赋值,Map集合需要使用map标签和entry标签添加键值对赋值。

<bean id="user" class="com.cwd.spring1pro.bean.User">
    <property name="name" value="Tom"></property>
    <property name="age" value="22"></property>
    <property name="list">
        <list>
            <value>1</value>
            <value>2</value>
            <value>3</value>
        </list>
    </property>
    <property name="map">
        <map>
            <entry key="k1" value="v1"/>
            <entry key="k2" value="v2"/>
            <entry key="k3" value="v3"/>
        </map>
    </property>
</bean>

(3)对象注入

  • 创建实体类userDao
public class UserDao {
    public void saveUser(){
        System.out.println("保存User");
    }
}
  • 创建一个引用实体类的实体类userService
public class UserService {
    private UserDao userDao;

    public void UserSave() {
        userDao.saveUser();
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
  • 在spring.xml文件中进行配置注入

对象注入我们使用ref属性,它引用的是被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">
       
    <!--Spring管理了UserDao-->
    <bean id="userDao" class="com.cwd.spring2pro.dao.UserDao"></bean>

    <!--Spring管理UserService-->
    <bean id="userService" class="com.cwd.spring2pro.service.UserService">
        <property name="userDao" ref="userDao"></property>
    </bean>

</beans>

在这里插入图片描述

2.构造方法注入

需要在实体类中添加有参的构造方法,建议大家把无参的构造方法也写上。java代码会根据在spring.xml文件中配置的参数个数选择不同的构造方法赋值。

(1)简单值注入

  • 实体类中的构造方法
public User(String name, Integer age) {
    this.name = name;
    this.age = age;
    System.out.println("User类的有参构造方法1!!!");
}
  • spring.xml文件中的配置

使用constructor-arg标签进行注入,name和value的作用和property中的作用一样。

<!--
	通过构造方法注入值,通过参数值数量选择不同的构造方法
-->
<bean id="user" class="com.cwd.spring1pro.bean.User">
    <constructor-arg name="name" value="jim"></constructor-arg>
    <constructor-arg name="age" value="20"></constructor-arg>
</bean>

也可以使用constructor-arg标签中的index属性注入,根据不同的参数位置进行注入。

<bean id="user" class="com.cwd.spring1pro.bean.User">
	<constructor-arg index="0" value="tom"/>
    <constructor-arg index="1" value="10"/>
</bean>

(2)集合注入

我们还是以List集合和Map集合为例,基本的使用方法和property一致。

<bean id="user" class="com.cwd.spring1pro.bean.User">
	<constructor-arg name="name" value="jim"/>
    <constructor-arg name="age" value="22"/>
    <constructor-arg name="list">
        <list>
            <value>1</value>
            <value>2</value>
            <value>3</value>
        </list>
    </constructor-arg>
    <constructor-arg name="map">
        <map>
            <entry key="k1" value="v1"/>
            <entry key="k2" value="v2"/>
            <entry key="k3" value="v3"/>
        </map>
    </constructor-arg>
</bean>

基于注解方式实现

我们可以在Spring.xml文件通过配置实现对java类的bean管理和依赖注入,同样的,我们也可以使用注解的方式完成这一步骤。在此之前,我们需要做一些准备工作:首先,我们需要导入注解需要的jar包,注解功能封装在AOP包中,因此导入Spring.aop.jar包即可,其次就是要在spring.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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
        
    <!--开启Spring注解扫描,需要注意context的约束,完善beans标签的属性-->
    <context:component-scan base-package="需要扫描的java类的包名"> </context:component-scan>

</beans>

不同的java类我们通过不同的注解标签进行注解,一般我们按不同的层(bean,dao,service)进行划分,这并不是说每一层的注解标签只能注解本层的java类,只是为了后续的扩展功能。

(1)bean层,普通的java类——Component

@Component(value = “user”) 等同于我们在xml文件中配置bean标签使用id和class属性

@Component(value = "user")
//等同于<bean id = "user" class = "com.cwd.spring3pro.demo1.bean.User">
@Scope(value = "prototype")
public class User {
    private String name;
    private Integer age;

    public User() {
        System.out.println("User类的无参构造方法已创建!!!");
    }

    public void work() {
        System.out.println(this.name+"今年"+this.age+"岁");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

(2)dao层,数据持久层——Respository

@Repository(value = “userDao”)

@Repository(value = "userDao")
public class UserDao {
    public void saveUser(){
        System.out.println("保存User");
    }
}

(3)service层

@Service(value=“userService”)

@Service(value = "userService")
public class UserService {
	/*
	这里需要注入
	*/
    private UserDao userDao;

    public void UserSave() {
        userDao.saveUser();
    }

}

(4)其它标签——用作弥补配置xml文件中标签的各种属性

作用域:@Scope

@Scope(value = "prototype") 原型
@Scope(value = "singleton") 单例
依赖注入

这里的依赖注入主要分为两种:一种是按照属性的类型自动注入,另一种是匹配为java类的配置的id值。

byType

@Autowired 自动注入,按照属性的类型自动注入

@Service(value = "userService")
public class UserService {
    
    @Autowired//byType 根据属性的类型匹配查询 
    private UserDao userDao;
}

@Resource JDK自带的注解方式

@Service(value = "userService")
public class UserService {
    @Resource
    private UserDao userDao;
}
byName

依靠@Qualifier,需要联合使用自动注解方式(@Autowired或@Resource),@Qualifier的value属性值用于指定要匹配的Bean的id值。

@Service(value = "userService")
public class UserService {
    @Autowired
    @Qualifier(value = "userDao") //byName 根据对象名称查找 bean的id
    private UserDao userDao;
}
@Service(value = "userService")
public class UserService {
    @Resource
    @Qualifier(value = "userDao")
    private UserDao userDao;
}

xml和注解方式

以上是关于理解概念比死记硬背更有效:Spring框架的IOC设计思想+Spring的Bean管理的主要内容,如果未能解决你的问题,请参考以下文章

如何理解Spring IOC

Spring IOC的理解

谈谈对Spring IOC的理解

谈谈对Spring IOC的理解

谈谈我对Spring IOC的理解

spring ioc Di