Spring-IOC容器

底层原理

什么是IOC

IOC全称Inversion of Control

控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理,使用 IOC 目的:为了耦合度降低

IOC底层原理

xml解析、工程模式、反射

IOC过程

第一步:xml配置文件,配置创建的对象

第二步:解析xml配置文件,得到对象的class属性,通过反射Class.forName+newInstance()创建对象

image-20210515083347574

IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂

BeanFactory接口(两个接口)

IOC 容器底层就是对象工厂,Spring对于IOC基于工厂的实现提供了两个重要接口BeanFactoryApplicationContext

BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用。加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象

ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。加载配置文件时候就会把在配置文件对象进行创建

ApplicationContext接口有实现类

  • FileSystemXmlApplicationContext:配置文件写盘符路径,配置文件在磁盘的全路径

  • ClassPathXmlApplicationContext:配置文件写类路径

image-20210515084141672

BeanFactory接口有实现类

  • ApplicationContext是BeanFactory的子接口

  • ConfigurableApplicationContext:包含一些扩展功能的实现

image-20210515084634249

Bean管理

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

普通Bean

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

public class MyBean {
}
<!--普通bean-->
<bean id="myBean" class="com.edu.spring.factorybean.MyBean"></bean>
@Test
public void testFactoryBean() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    MyBean bean = context.getBean("myBean", MyBean.class);
    System.out.println(bean);
}

-----
com.edu.spring.factorybean.MyBean@49c43f4e

工厂Bean

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

第一步:创建类,让这个类作为工厂Bean,实现接口FactoryBean

第二步:实现接口里面的方法,在实现的方法中定义返回的Bean类型

public class MyBean implements FactoryBean<Course> {
    // 定义返回Bean类型的对象
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("工厂bean");
        return course;
    }
    // 得到对象类型
    @Override
    public Class<?> getObjectType() {
        return null;
    }
    // 是否是单例模式
    @Override
    public boolean isSingleton() {
        return false;
    }
}
<!--工厂bean-->
<bean id="myBean" class="com.edu.spring.factorybean.MyBean"></bean>
@Test
public void testFactoryBean() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Course bean = context.getBean("myBean", Course.class);
    System.out.println(bean);
}

-----
Course{cname='工厂bean'}

Bean作用域

在Spring里面,设置创建Bean实例是单实例还是多实例,默认情况下,Bean是单实例对象

证明:创建二个Bean的实例对象,实例对象的地址相同说明是同一对象,也就是Bean是单例对象

image-20210515105215461

如何设置单实例多实例

spring配置文件bean标签里面有属性用于设置单实例多实例scope

image-20210515105520039

singleton:默认值 表示单实例对象

prototype 表示多实例对象

image-20210515105520039

singleton和prototype 区别

singleton表示单实例,prototype 表示多实例。设置scope为singleton的时候,在加载spring配置文件时候就会创建单实例对象,如果scope为prototype 的时候,不是在加载spring配置文件的时候创建对象,而是在调用getBean方法的时候创建多实例对象

Bean生命周期

生命周期:从对象创建到销毁的过程

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

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

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

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

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

public class Orders {
    private String onme;
    public Orders() {
        System.out.println("第一步 执行无参构造器创建bean实例");
    }
    public void setOnme(String onme) {
        System.out.println("第二步 调用set方法设置属性值");
        this.onme = onme;
    }
    // 创建执行的初始化方法
    public void initMethod() {
        System.out.println("第三步 执行初始化方法");
    }
    // 创建执行的销毁方法
    public void destroyMethod(){
        System.out.println("第五步 执行销毁方法");
    }
}
<!--bean声明周期-->
<bean id="orders" class="com.edu.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="onme" value="电脑"/>
</bean>
@Test
public void testBeanLive() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Orders bean = context.getBean("orders", Orders.class);
    System.out.println("第四步 获取创建bean实例的对象");
    System.out.println(bean);
    // 手动让bean销毁
    context.close();
}

image-20210515113426243

如果有bean后置处理器,则bean的生命周期有七种

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

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

(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization

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

(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

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

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

配置后置处理器,实现BeanPostProcessor接口

public class MyBeanPost implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的方法");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return bean;
    }
}

在bean配置文件中声明后置处理器,配置之后会为所有的bean实例添加后置处理器

<!--配置后置处理器 配置之后会为所有的bean实例添加后置处理器-->
<bean id="myBeanPost" class="com.edu.spring.bean.MyBeanPost"></bean>

image-20210515114425436

基于xml配置文件的Bean管理方式

什么是Bean管理

Bean管理包含两个过程:1. Spring创建对象 2. Spring注入属性

基于xml方式创建对象

在spring配置文件中,使用bean标签, 标签里面添加对应属性,就可以实现对象创建

id:给该对象取一个唯一标识,通过这个表示可以获取该对象

class:创建对象的包类全路径

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">
    
    <!--配置User对象创建-->
    <bean id="user" class="com.edu.spring.bean.User"></bean>

</beans>

基于xml方式注入属性

DI(Dependency Injection):注入属性,DI是IOC注入属性的具体实现,需要在创建对象的基础之上实现

第一种注入方式:通过set方法注入

// 第一步 创建类属性的set方法
public class Book {
    private String bname;
    private String bauthor;
    public void setBname(String bname) {
        this.bname = bname;
    }
    public void setBauthor(String bauthor) {
        this.bauthor = bauthor;
    }
}

// 第二步 配置文件创建对象,用property标签注入属性
<bean id="book" class="com.edu.spring.bean.Book">
	<property name="bname" value="Spring学习"/>
	<property name="bauthor" value="xyx"/>
</bean>
    
// 第三步 测试
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Book book = context.getBean("book", Book.class);
System.out.println(book);

-----
Book(bname=Spring学习, bauthor=xyx)

第二种注入方式:通过有参构造器注入

// 第一步 创建类属性的有参构造器
public class Order {
    private String oname;
    private String address;
    public Order(String oname, String address) {
        this.oname = oname;
        this.address = address;
    }
}

// 第二步 通过有参构造注入属性
<bean id="order" class="com.edu.spring.bean.Order">
    // 通过属性名称注入
	<constructor-arg name="oname" value="abc"/>
	<constructor-arg name="address" value="china"/>
    // 通过索引名称注入
    <constructor-arg index="0" value="abc"/>
	<constructor-arg index="1" value="china"/>
</bean>

// 第三步 测试
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
Book book = context.getBean("order", Book.class);
System.out.println(order);

-----
Order(oname=abc, address=china)

基于P名称空间注入

可以简化基于xml配置方式

第一步:添加p名称空间到配置文件中xmlns:p="http://www.springframework.org/schema/p"

image-20210515091851309

第二步: 通过p标签在进行属性注入

<bean id="book" class="com.edu.spring.bean.Book" p:bname="spring" p:bauthor="xyx"/>

注入空值和特殊符号

设置空值:在属性property里使用null标签实现

<bean id="book" class="com.edu.spring.bean.Book">
    <property name="bname" value="Spring学习"/>
    <!--设置null值-->
    <property name="bauthor">
        <null></null>
    </property>
</bean>

设置特殊符号:在属性property里使用value标签通过![CDATA[xxx]]实现

<bean id="book" class="com.edu.spring.bean.Book">
    <property name="bname" value="Spring学习"/>
    <!--设置null值-->
    <property name="bauthor">
        <value><![CDATA[<<南京>>]]></value>
    </property>
</bean>

注入外部bean

场景:service层调用dao层

使用属性property的ref注入外部对象,接口不能new对象,需要new接口实现类

<!--注入service-->
<bean id="userService" class="com.edu.spring.service.UserService">
    <property name="userDao" ref="userDao"></property>
</bean>

<!--注入dao-->
<bean id="userDao" class="com.edu.spring.dao.Impl.UserDaoImpl"></bean>

注入内部bean和级联赋值

场景:一对多

内部bean:在bean里面嵌套bean

<bean id="emp" class="com.edu.spring.bean.Emp">
    <property name="ename" value="xyx"/>
    <property name="gender" value="man"/>
    <property name="dept">
        <bean id="dept" class="com.edu.spring.bean.Dept">
            <property name="dname" value="security"/>
        </bean>
    </property>
</bean>

ref级联赋值

<!--注入emp-->
<bean id="emp" class="com.edu.spring.bean.Emp">
    <property name="ename" value="xyx"/>
    <property name="gender" value="man"/>
    <property name="dept" ref="dept"/>
</bean>
<!--注入dept-->
<bean id="dept" class="com.edu.spring.bean.Dept">
    <property name="dname" value="security"/>
</bean>

注入集合类型属性

注入数组类型属性

<bean id="stu" class="com.edu.spring.collectionbean.Stu">
    <!--数组类型-->
    <property name="arrays">
        <array>
            <value>java</value>
            <value>python</value>
        </array>
    </property>
</bean>

注入List集合类型属性

<bean id="stu" class="com.edu.spring.collectionbean.Stu">
    <!--list类型-->
    <property name="list">
        <list>
            <value>mysql</value>
            <value>redis</value>
        </list>
    </property>
</bean>

注入Map集合类型属性

<bean id="stu" class="com.edu.spring.collectionbean.Stu">
    <!--map类型-->
    <property name="maps">
        <map>
            <entry key="JAVA" value="java"></entry>
            <entry key="PHP" value="php"></entry>
        </map>
    </property>
</bean>

注入Set集合类型属性

<bean id="stu" class="com.edu.spring.collectionbean.Stu">
    <!--set类型-->
    <property name="sets">
        <set>
            <value>MYSQL</value>
            <value>REDIS</value>
        </set>
    </property>
</bean>

集合注入对象类型的值

private List<Course> courseList;

创建多个要注入的对象

<bean id="course1" class="com.edu.spring.collectionbean.Course">
    <property name="cname" value="Sprig"/>
</bean>

<bean id="course2" class="com.edu.spring.collectionbean.Course">
    <property name="cname" value="Mybatis"/>
</bean>

注入对象类型集合

<bean id="stu" class="com.edu.spring.collectionbean.Stu">
    <!--对象类型集合-->
    <property name="courseList">
        <list>
            <ref bean="course1"></ref>
            <ref bean="course2"></ref>
        </list>
    </property>
</bean>

集合注入部分抽取成公共部分

在配置文件中引入名称空间xmlns:util="http://www.springframework.org/schema/util"http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd

image-20210515102716185

使用util标签完成list集合注入提取

<!--提取集合类型属性注入-->
<util:list id="bookList">
    <value>Java</value>
    <value>Mysql</value>
    <value>HTML</value>
</util:list>

抽取注入对象类型集合

<util:list id="bookList">
    <ref bean="book1"></ref>
    <ref bean="book2"></ref> 
</util:list>

其他类型注入方式类似,使用不同标签即可

image-20210515103132223

list集合注入抽取属性

<bean id="book" class="com.edu.spring.collectionbean.Book">
    <property name="list" ref="bookList"/>
</bean>

XML自动装配

根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入

byName: 根据属性名称进行注入 注入值 bean 的 id 值和类属性名称一样

byType: 根据属性类型进行注入 配置文件中只能有一个实例类型

<bean id="empAuto" class="com.edu.spring.autowire.Emp" autowire="byType"></bean>

<bean id="deptAuto" class="com.edu.spring.autowire.Dept"></bean>

引入外部配置文件

场景:配置druid数据库连接池

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

直接配置连接池

<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/education"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
</bean>

引入外部属性文件配置

编写外部配置文件

jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/education
jdbc.username=root
jdbc.password=root

引入context名称空间xmlns:context="http://www.springframework.org/schema/context"http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

image-20210515121002218

编写bean配置文件:context引入外部文件,${}取值

<!--引入外部属性配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driverClass}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>

基于注解的Bean管理方式

什么是注解

(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)

(2)使用注解,注解作用在类上面,方法上面,属性上面

(3)使用注解目的:简化 xml 配置

Spring针对Bean管理中创建对象提供的注解

(1)@Component

(2)@Service

(3)@Controller

(4)@Repository

上面四个注解功能是一样的,都可以用来创建 bean 实例,表明区别分层可以对代码逻辑更清晰

基于注解方式创建对象

环境:引入依赖jar包spring-aop,引入context名称空间,引入方法见上文

第一步:开启组件扫描

  1. 如果扫描多个包,多个包使用逗号隔开
  2. 扫描包上层目录(推荐)
<!--开启组件扫描-->
<context:component-scan base-package="com.edu.spring.annotation"/>

第二步: 创建类 类上面添加创建对象注解

在注解里面value属性值可以省略不写,默认值是类名称,首字母小写UserService-->userService

注解配置等同xml这段配置<bean id="userService" class="...">

@Service(value = "userService")
public class UserService {
    public void add() {
        System.out.println("annotation service add ...");
    }
}

组件扫描配置

<!--开启组件扫描-->
<context:component-scan base-package="com.edu.spring.annotation"/>

场景:spring会扫base-package下的所有注解,如何配置在base-package下哪些类扫描 哪些类不扫描

示例一:只扫描base-package下带Controller的注解

use-default-filters="false":表示现在不适用默认的filter,使用自定义的filter,即自定义扫描规则

include-filter: 配置扫描哪些内容

<context:component-scan base-package="com.edu.spring.annotation" use-default-filters="false">
    <!--只扫描带Controller的注解-->
    <context:include-filter type="annotation"
                            expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

示例二:不扫描base-package下带Controller的注解

exclude-filter:设置哪些内容不进行扫描

<context:component-scan base-package="com.edu.spring.annotation">
    <context:exclude-filter type="annotation"
                            expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

基于注解方式注入属性

(1)@Autowired:根据属性类型进行自动装配

第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解

@Service(value = "userService")
public class UserService {
    public void add() {
        System.out.println("annotation service add ...");
    }
}
-----
@Repository(value = "userDao")
public class UserDao {
    public void add() {
        System.out.println("annotation dao add...");
    }
}

第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解

@Service(value = "userService")
public class UserService {

    // 注入userDao对象
    @Autowired
    private UserDao userDao;

    public void add() {
        userDao.add();
        System.out.println("annotation service add ...");
    }
}

image-20210515124017336

(2)@Qualifier:根据名称进行注入

这个@Qualifier 注解的使用,和上面@Autowired 一起使用

使用场景:由于@Autowired注解是按照属性类型进行注入,如果bean容器中有多个同一类型的实例对象,就需要使用@Qualifier根据实例名称指定注入哪一个实例对象

@Autowired
@Qualifier(value = "userDao")
private UserDao userDao;

(3)@Resource:可以根据类型注入,可以根据名称注入 javax扩展包中的注解

import javax.annotation.Resource;

@Resource(name = "userDao")
private UserDao userDao;

(4)@Value:注入普通类型属性

@Value(value = "笑一笑")
private String name;

完全注解开发

(1)创建配置类,替代 xml 配置文件

@Configuration: 作为配置类 替代xml配置文件

@ComponentScan: 开启组件扫描

@Configuration// @Configuration作为配置类 替代xml配置文件
@ComponentScan(basePackages = {"com.edu.spring.annotation"})
public class SpringConfig {
}

(2)编写测试类

使用AnnotationConfigApplicationContext加载配置类

@Test
public void testAnnotationConfiguration() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService service = context.getBean("userService", UserService.class);
    service.add();
}

image-20210515125313553

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值