Spring之IOC原理、注解开发、Bean生命周期

入门案例

目录结构:

在这里插入图片描述

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lin.spring5</groupId>
    <artifactId>spring5_study</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>spring5_demo01</module>
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-expression</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.1.1</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

User.java

public class User {
    
    
    public void add() {
    
    
        System.out.println("add......");
    }
}

bean1.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--  配置User对象  -->
    <bean id="user" class="com.lin.spring5.User"></bean>
</beans>

GlobalTest.java

/**
 * @author Created by Lin Weihong
 * @date on 2022/9/23 23:05
 */
public class GlobalTest {
    
    
    @Test
    public void testAdd() {
    
    
        //1.加载spring的配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
        //2.获取配置的对象
        User user = applicationContext.getBean("user", User.class);
        user.add();
    }
}

输出结果

在这里插入图片描述

IOC容器

什么是IOC

(1)控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理

(2)使用 IOC 目的:为了耦合度降低

(3)做入门案例就是 IOC 实现

IOC 底层原理

xml 解析+工厂模式+反射

在这里插入图片描述

在这里插入图片描述

最终解耦方案:三者结合

在这里插入图片描述

IOC 接口(BeanFactory)

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

2、Spring提供IOC容器实现的两种方式:(两个接口

(1)BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用

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

(2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人

员进行使用

加载配置文件时候就会把在配置文件对象进行创建,这种更适合web项目,因为这个过程很耗时,可以在tomcat启动的时候就加载这些对象,而不是用到了再加载

3、ApplicationContext 接口有实现类

在这里插入图片描述

ClassPathXmlApplicationContext:对应src

FileSystemXmlApplicationContext:对应系统盘符完整路径

IOC 操作 Bean 管理(概念)

1、什么是 Bean 管理

Bean 管理指的是两个操作

​ (1)Spring 创建对象

​ (2)Spirng 注入属性

IOC 操作 Bean 管理(基于 xml)

1、基于 xml 方式创建对象

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

(2)在 bean 标签有很多属性,介绍常用的属性

​ id 属性:唯一标识

​ class 属性:类全路径(包类路径)

(3)创建对象时候,默认也是执行无参数构造方法完成对象创建

2、基于 xml 方式注入属性

1DI:依赖注入,就是注入属性

1、第一种注入方式:使用 set 方法进行注入

(1)创建类,定义属性和对应的 set 方法

/**
 * 演示使用 set 方法进行注入属性
 */
public class Book {
    
    
    //创建属性
    private String bname;
    private String bauthor;

    //创建属性对应的 set 方法
    public void setBname(String bname) {
    
    
        this.bname = bname;
    }

    public void setBauthor(String bauthor) {
    
    
        this.bauthor = bauthor;
    }
}

(2)在 spring 配置文件配置对象创建,配置属性注入

<!--2 set 方法注入属性--> 
<bean id="book" class="com.atguigu.spring5.Book">
     <!--使用 property 完成属性注入
     name:类里面属性名称
     value:向属性注入的值
     -->
 <property name="bname" value="易筋经"></property>
 <property name="bauthor" value="达摩老祖"></property>
</bean>
2、第二种注入方式:使用有参数构造进行注入

(1)创建类,定义属性,创建属性对应有参数构造方法

public class Orders {
    
    
    //属性
    private String oname;
    private String address;

    //有参数构造
    public Orders(String oname, String address) {
    
    
        this.oname = oname;
        this.address = address;
    }
}

(2)在 spring 配置文件中进行配置

<!--3 有参数构造注入属性-->
<bean id="orders" class="com.atguigu.spring5.Orders">
 <constructor-arg name="oname" value="电脑"></constructor-arg>
 <constructor-arg name="address" value="China"></constructor-arg>
</bean>
3p名称空间注入(了解)

(1)使用 p 名称空间注入,可以简化基于 xml 配置方式

​ 第一步 添加 p 名称空间在配置文件中

在这里插入图片描述

第二步 进行属性注入,在 bean 标签里面进行操作

<bean id="book" class="com.atguigu.spring5.Book" p:bname="九阳神功" p:bauthor="无名氏"></bean>

3、xml注入其他属性

1、字面量

(1)null 值

(2)属性值包含特殊符号

        <!--     注入null值   -->
<!--        <property name="address">-->
<!--            <null/>-->
<!--        </property>-->
        
        <!--属性值包含特殊符号
             1 把<>进行转义 &lt; &gt;
             2 把带特殊符号内容写到 CDATA
        -->
        <property name="address">
            <value><![CDATA[<<南京>>]]></value>
        </property>
    </bean>

2、注入属性-外部 bean

(1)创建两个类 service 类和 dao 类

(2)在 service 调用 dao 里面的方法

public class UserService {
    
    
    //set方法注入
    private UserDao userDao;//dao是接口,真正实现类是UserDaoImpl

    public void setUserDao(UserDao userDao) {
    
    
        this.userDao = userDao;
    }

    public void add() {
    
    
        System.out.println("service....add......");
        userDao.add();
        //传统方法
//        UserDao userDao = new UserDaoImpl();
//        userDao.add();
    }
}

(3)在 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">

    <!-- service和dao对象创建   -->
    <bean id="userService" class="com.lin.spring5.service.UserService">
        <!--    注入userDao
            ref:创建UserDao对象bean标签的id
            -->
        <property name="userDao" ref="userDao"/>
    </bean>
    <!--接口要new 对象-->
    <bean id="userDao" class="com.lin.spring5.dao.impl.UserDaoImpl">
    </bean>
</beans>
@Test
    public void test() {
    
    
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean2.xml");
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.add();
   }

3、注入属性-内部 bean

(1)一对多关系:部门和员工

​ 一个部门有多个员工,一个员工属于一个部门

​ 部门是一,员工是多

(2)在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示

 <!--      内部bean    -->
    <bean id="emp" class="com.lin.spring5.bean.Emp">
        <property name="ename" value="小新"></property>
        <property name="gender" value=""></property>
        <!--    设置对象类型属性    -->
        <property name="dept">
            <bean id="dept" class="com.lin.spring5.bean.Dept">
                <property name="dname" value="开发部"></property>
            </bean>
        </property>
    </bean>

4、Bean 工厂⭐

@ToString
public class MyBean implements FactoryBean<Course> {
    
    


    public Course getObject() throws Exception {
    
    
        Course course = new Course();
        return course;
    }

    public Class<?> getObjectType() {
    
    
        return null;
    }

    public boolean isSingleton() {
    
    
        return false;
    }
}


  @Test
    public void testBeanFactory() {
    
    
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
        Course course = applicationContext.getBean("myBean", Course.class);
        System.out.println(course);
    }
 <bean id="myBean" class="com.lin.spring5.factorybean.MyBean">
    </bean>

5、Bean作用域⭐⭐

  1. 在Spring里面设置设置创建bean实例是单例还是多例
  2. 在Spring中默认是创建单例

在这里插入图片描述

  1. 设置多实例

      <!--
          scope:单例/多例
          -->
    <bean id="book" class="com.lin.spring5.collectiontype.Book" scope="prototype">
        <property name="list" ref="bookList"></property>
    </bean>
    

6、Bean生命周期⭐⭐⭐⭐

1、生命周期

(1)从对象创建到对象销毁的过程

2bean 生命周期(没加后置处理器的)

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

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

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

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

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

代码演示:

@ToString
public class Orders {
    
    
    private String oname;

    public Orders() {
    
    
        System.out.println("1.通过无参构造器创建bean的实例");
    }

    public void setOname(String oname) {
    
    
        System.out.println("2.为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)");
        this.oname = oname;
    }
    //创建初始化方法
    public void initMethod() {
    
    
        System.out.println("3.调用 bean 的初始化的方法(需要进行配置初始化的方法)");
    }
    //创建销毁方法
    public void destroyMethod() {
    
    
        System.out.println("5.bean销毁了");
    }
}
  <bean id="orders" class="com.lin.spring5.beanslife.Orders" init-method="initMethod" destroy-method="destroyMethod">
        <property name="oname" value="手机"></property>
    </bean>
  @Test
    public void testBeanLife() {
    
    
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean4.xml");
        Orders orders = applicationContext.getBean("orders", Orders.class);
        System.out.println("4.bean 可以使用了(对象获取到了)");
        //销毁bean,close方法在子类ClassPathXmlApplicationContext中才有
        applicationContext.close();
    }

结果:

在这里插入图片描述

3bean 生命周期(加后置处理器的)

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

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

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

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
    return null;
}

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

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

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        return null;
    }

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

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

代码演示:

在这里插入图片描述

7、Bean自动装配⭐⭐⭐⭐

    <!--  TODO 自动装配  -->
    <!--autowired属性两个常用值:
        byName:根据属性名注入,要求注入bean的id和byName里面的属性名称一样!!!!!!
        byType:根据属性类型注入,要求类型不能有多个bean,不然byType找不到要用哪个
    -->
    <bean id="emp" class="com.lin.spring5.autowire.Emp" autowire="byType"></bean>
    <bean id="dept1" class="com.lin.spring5.autowire.Dept"></bean>
<!--    <bean id="dept2" class="com.lin.spring5.autowire.Dept"></bean>-->

IOC 操作 Bean 管理(基于注解)

1、什么是注解?

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

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

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

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

(1)@Component

(2)@Service

(3)@Controller

(4)@Repository

* 上面四个注解功能是一样的,都可以用来创建 bean 实例

3、基于注解方式实现对象创建

1、加入spring-aop包
2、开启组件扫描
<?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:util="http://www.springframework.org/schema/util"
       xmlns:context="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/util http://www.springframework.org/schema/util/spring-util.xsd
             http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd"
>
    <!--开启组件扫描-->
    <!--
        1.如果要扫描多个包,可以用逗号分割
        2.直接写到要扫描的包的共同上层目录

    -->
    <context:component-scan base-package="com.lin.spring5"/>

</beans>
3、加入注解
//@Service//默认为userServiceImpl
@Service(value = "userService") //<bean id = userService class="..">,如果不写value,默认是类名且首字母小写
public class UserServiceImpl {
    
    
    public void add() {
    
    
        System.out.println("UserServiceImpl...add...");
    }
}

4、开启组件扫描细节配置
<?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:util="http://www.springframework.org/schema/util"
       xmlns:context="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/util http://www.springframework.org/schema/util/spring-util.xsd
             http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd"
>

    <!--开启组件扫描细节-->
    <!--示例 1
 use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
 context:include-filter ,设置扫描哪些内容-->
    <context:component-scan base-package="com" use-default-filters="false">
        <context:include-filter type="annotation"

                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!--示例 2
     下面配置扫描包所有内容
     context:exclude-filter: 设置哪些内容不进行扫描
    -->
    <context:component-scan base-package="com">
        <context:exclude-filter type="annotation"

                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>
5、基于注解方式实现属性注入
(1)@Autowired:根据属性类型进行自动装配(byType)

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

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

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

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

(3)@Resource:可以根据类型注入(byType),可以根据名称注入(byName)
(4)@Value:注入普通类型属性

@Value(value = “abc”)

package com.lin.spring5.service;

import com.lin.spring5.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author Created by Lin Weihong
 * @date on 2022/9/24 20:31
 */
@Service//默认为userServiceImpl
//@Service(value = "userService") //<bean id = userService class="..">,如果不写value,默认是类名且首字母小写
public class UserServiceImpl {
    
    

//    @Autowired //根据类型进行注入
//    //当UserDao有多个实现类的时候,不能在通过@Autowired注入,因为多个bean,没办法识别要用哪个,所以通过这个注解指定使用的bean名字
//    @Qualifier(value = "userdao2")//这个value就是目标bean里面注解的value,没写就是默认类名首字母小写,必须搭配@Autowired使用
//    private UserDao userDao;

//    @Resource //按照类型注入,如果有多个实现类,这个方法也会报错的
    @Resource(name = "userdao2")
    private UserDao userDao;

    @Value(value = "小新")
    private String name;

    public void add() {
    
    
        userDao.add();
        System.out.println("UserServiceImpl...add...");
        System.out.println(name);
    }
}

@Repository
public class UserDaoImpl implements UserDao {
    
    
    public void add() {
    
    
        System.out.println("UserDaoImpl...add...");
    }
}

@Repository(value = "userdao2")
public class UserDaoImpl2 implements UserDao {
    
    
    public void add() {
    
    
        System.out.println("UserDaoImpl2...add...");
    }
}
6、完全注解开发

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

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

}

(2)编写测试类

  @Test
    public void fullAnnotation() {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); //加载配置类
        UserServiceImpl userServiceImpl = context.getBean("userServiceImpl", UserServiceImpl.class);
        userServiceImpl.add();
    }

println(“UserDaoImpl…add…”);
}
}

@Repository(value = “userdao2”)
public class UserDaoImpl2 implements UserDao {
public void add() {
System.out.println(“UserDaoImpl2…add…”);
}
}


##### 6、完全注解开发

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

```java
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.lin.spring5"})
public class SpringConfig {

}

(2)编写测试类

  @Test
    public void fullAnnotation() {
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); //加载配置类
        UserServiceImpl userServiceImpl = context.getBean("userServiceImpl", UserServiceImpl.class);
        userServiceImpl.add();
    }

猜你喜欢

转载自blog.csdn.net/weixin_48244108/article/details/127030921