Spring5 概述及Spring IOC学习
阅读原文时间:2023年07月10日阅读:2

Spring Framework 5

1.1 Spring框架概述




1.2 主要内容

Spring框架是一个开源的JavaEE的应用程序

主要核心是 IOC(控制反转)和AOP(面向切面编程) 两大技术。

主要学习内容:

  1. Spring IOC (控制反转和依赖注入) Inversion of Control
  2. Spring AOP Aspect Oriented Programming
  3. Spring JDBC + 事务

Dao层:

​ JDBC操作

​ 对应的框架:Mybatis

Service层:

​ 业务逻辑十分复杂,Spring框架不是针对Service层的业务逻辑,目前没有适合的框架

Controller层:

​ Servlet(接收请求 响应数据 地址配置 页面转发)

​ 对应框架:Spring MVC

Spring基于分布式的应用程序

  1. 基于轻量级的框架(引入的依赖和jar包少,体积小,可以独立使用)

​ 配置管理

​ Bean对象的实例化——IOC

  1. 可以集成第三方的框架

​ Mybatis、Hibernate框架(持久层框架)

​ Spring MVC

​ Spring Security权限

​ Quartz时钟框架(定时任务处理)

  1. 自带服务

​ Mail邮件发送

​ 定时任务处理-定时调度(定时短信、定时任务)

​ 消息处理(异步处理)

Spring IOC模块: Bean对象的实例化Bean的创建

Spring AOP模块:动态代理面向切面编程

Spring JDBC + 事务模块

Spring Web模块

4.1 主要内容

​ IOC底层原理、IOC接口(BeanFactory)、IOC操作Bean管理(基于XML、基于注解)

4.2 IOC概念和原理

​ IOC,即控制反转,是用来降低代码之间的耦合度,通过将对象的创建和对象之间的调用过程,交给Spring来进行管理,从而达到解耦的目的。

​ IOC底层原理,主要涉及到xml解析、工厂模式、反射


4.3 IOC容器:BeanFactory接口

4.4 IOC操作Bean管理

​ Bean管理指的是两个操作:

1. Spring创建对象:

​ 通过配置文件或注解方法进行对象的创建。

2. Spring注入属性:

​ 不直接调用类对象内部的set方法设置属性值,而是通过xml配置文件或注解的方法。

​ 而这两个操作是基于xml配置文件方式基于注解方式实现。

4.5 IOC操作Bean管理——基于xml方式

4.5.1 Bean对象实例化

4.5.1.1 构造器实例化*

默认使用方式,通过默认构造器创建,则空构造方法必须存在,否则创建失败,即提供有参构造器的同时,必须提供无参构造器。

1. 定义Bean属性对象类

定义一个User类,此Bean对象需要提供无参构造器

public class User { // 没有有参构造方法,class使用默认构造器,无需显示声明无参构造器
    // 创建属性
    private String name;
    private String brother;
    private Emp emp;

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

    public void setBrother(String brother) {
        this.brother = brother;
    }

    // Test
    public void printTest() {
        System.out.println("This is a Test");
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", brother='" + brother + '\'' +
                ", emp=" + emp +
                '}';
    }

    public void setEmp(Emp emp) {
        this.emp = emp;
    }

    public Emp getEmp() {
        return emp;
    }
}
2. 设置配置xml文件

使用bean标签创建对象

<?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.atguigu.spring5.User"></bean>

</beans>
3. 获取实例化对象
        // 1. 加载spring配置文件
        ApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("UserBean.xml");
        // 2. 获取配置创建的对象(两种方法)
        User user = (User) classPathXmlApplicationContext.getBean("user"); // 需要类型转换
        User user1 = classPathXmlApplicationContext.getBean("user", User.class); // 无需类型转换

        // 3. 测试
        user.printTest();
        user1.printTest();

/**
* This is a Test
* This is a Test
*/
4.5.1.2 静态工厂实例化(了解)
1. 定义Bean属性对象类
public class TypeService {
    public void printTest() {
        System.out.println("TypeService:printTest!");
    }
}
2. 定义静态工厂类
public class StaticFactory {
    // 定义对应的静态方法
    public static TypeService createService() {
        return new TypeService();
    }
}
3. 设置配置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">
    <!--
        静态工厂实例化:
            1. 定义工厂类及对应的静态方法
            2. 配置bean对象对应的工厂类及静态方法

            class:静态工厂类的路径
            factory-method:静态工厂类中实例化bean对象的静态方法
    -->
    <bean id="typeService" class="com.atguigu.factorybean.StaticFactory" factory-method="createService"></bean>

</beans>
4. 测试
public class Test {
    public static void main(String[] args) {
        // 静态工厂实例化
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("UserBean.xml");

        TypeService typeService = context.getBean("typeService", TypeService.class);

        typeService.printTest();
    }
}

TypeService:printTest!

​ 构造器实例化是在IOC容器中创建了一个对象,而静态工厂实例化中的对象实例创建则由对应的静态方法实现。

即:

    public static TypeService createService() {
        // 在对象创建前,可以进行一些操作

        return new TypeService();
    }

这样做的好处就是,在Bean对象实例化创建之前,可以添加一些逻辑操作。

4.5.1.3 实例化工厂实例化(了解)

注:

  • 工厂方法为非静态方法
  • 需要配置工厂bean,并在业务bean中配置factory-bean,factory-methods属性
1. 定义Bean属性对象类
public class UserService {
    public void printTest() {
        System.out.println("UserService:printTest!");
    }
}
2. 定义实例化工厂类
public class InstanceFactory {
    // 定义方法,返回实例化对象
    public UserService createUserService() { // 并不是静态方法
        return new UserService();
    }
}
3. 设置配置xml文件
    <!--
        实例化工厂
            1. 定义实例化工厂bean
            2. 配置bean对象,引用工厂bean,并指定工厂创建方法(方法为非静态)
    -->
    <bean id="instanceFactory" class="com.atguigu.factorybean.InstanceFactory"></bean>
    <bean id="userService" factory-bean="instanceFactory" factory-method="createUserService"></bean>
4. 测试
        UserService userService = context.getBean("userService", UserService.class);
        userService.printTest();
4.5.1.4 Spring三种实例化Bean的方法比较

4.5.2 Bean对象属性注入

DI:依赖注入,就是注入属性。(Dependency Injection,注入属性是在创建对象的基础上完成的

推荐使用set方法注入

4.5.2.1. set方法注入(无参构造注入属性)
1. 业务对象 JavaBean注入
  1. 属性字段提供set方法

  1. 配置文件的bean标签中设置property标签

    一定要先在配置文件中,配置对象创建,再配置属性

    <?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">
    &lt;!--配置Book对象创建--&gt;
    &lt;bean id="book" class="com.atguigu.spring5.Book"&gt;
        &lt;!-- 使用property,用set方法完成注入属性(其实就是无参构造) --&gt;
        &lt;property name="bname" value="《Java编程思想》"&gt;&lt;/property&gt;
        &lt;property name="bauthor" value="老外"&gt;&lt;/property&gt;
    &lt;/bean&gt;
    </beans>
    // 1. 加载spring配置文件
    ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("BookBean.xml");
    // 2. 获取配置创建的对象
    Book book = (Book) classPathXmlApplicationContext.getBean("book");</code></pre></li>
2. 字面量注入

3. 外部对象Bean注入

当前类中,其中有属性值需要创建一个外部类的实例,可以通过在配置文件中先创建对象,然后在注入属性操作中,完成引用。

也可以使用构造器构造注入,见4.5.2.2

4. 内部对象Bean注入


5. 级联赋值

​ 第一种写法:

​ 第二种写法:

​ 级联赋值


6. 集合类型注入






4.5.3 构造器构造(有参构造注入属性)

1. 单个或多个Bean对象作为参数
  1. 编写两个Java Bean对象类

    TypeService类:

    public class TypeService {
        public void printTest() {
            System.out.println("TypeService:printTest!");
        }
    }

    UserService类:

    public class UserService {
        public void printTest() {
            System.out.println("UserService:printTest!");
        }
    }
  2. 调用外部对象Bean类的构造器类

    ConService类:

    public class ConService {
        private TypeService typeService;
        private UserService userService;
    /* 构造器注入 */
    public ConService(TypeService typeService, UserService userService) {
        this.typeService = typeService;
        this.userService = userService;
    }
    
    // 测试方法
    public void print() {
        typeService.printTest();
        userService.printTest();
    }
    }
  3. 配置Spring配置xml文件

    使用constructor-arg标签,如果不使用的话,则配置文件是默认通过无参构造器进行创建对象的。

    <?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">
        <!--
            构造器注入
                设置构造器所需要的参数
                通过constructor-arg标签设置构造器的参数
                    name:属性名称
                    ref:要注入的bean对象对应的bean标签的id属性值
         !-->
        <bean id="conService" class="com.atguigu.service.ConService"> <!-- 必须有有参构造器,否则报错 -->
            <constructor-arg name="typeService" ref="typeService"></constructor-arg> <!-- 有参构造器参数 -->
            <constructor-arg name="userService" ref="userService"></constructor-arg> <!-- 有参构造器参数 -->
        </bean>
        <bean id="typeService" class="com.atguigu.service.TypeService"></bean> <!-- 创建对象 -->
        <bean id="userService" class="com.atguigu.service.UserService"></bean> <!-- 创建对象 -->
    
    </beans>
2. Bean对象和常用属性作为参数

在前面的基础上,对ConService类添加一个name属性,如下:

public class ConService {
    private TypeService typeService;
    private UserService userService;
    private String name;

    /* 构造器注入 */
    public ConService(TypeService typeService, UserService userService, String name) {
        this.typeService = typeService;
        this.userService = userService;
        this.name = name;
    }

    // 测试方法
    public void print() {
        typeService.printTest();
        userService.printTest();
        System.out.println("name:" + name);
    }
}

然后改一下xml配置文件如下:

    <!--
        构造器注入
            设置构造器所需要的参数
            通过constructor-arg标签设置构造器的参数
                name:属性名称
                ref:要注入的bean对象对应的bean标签的id属性值
                value:数据具体的值
                index:参数的位置(从0开始)
     !-->
    <bean id="conService" class="com.atguigu.service.ConService"> <!-- 必须有有参构造器,否则报错 -->
        <constructor-arg name="typeService" ref="typeService"></constructor-arg> <!-- 有参构造器参数 -->
        <constructor-arg name="userService" ref="userService"></constructor-arg> <!-- 有参构造器参数 -->

        <!--  <constructor-arg index="2" value="David"></constructor-arg>  -->
        <constructor-arg name="name" value="David"></constructor-arg> <!-- 常用属性注入 -->

    </bean>
    <bean id="typeService" class="com.atguigu.service.TypeService"></bean> <!-- 创建对象 -->
    <bean id="userService" class="com.atguigu.service.UserService"></bean> <!-- 创建对象 -->
3. 循环依赖问题

循环依赖问题产生的原因:

Bean对象是通过构造器进行注入的,之间彼此相互依赖对方注入对象,则会导致bean无法实例化。

Java代码展示:

public class RoleService {
    private AccountService accountService;

    public RoleService(AccountService accountService) {
        this.accountService = accountService;
    }
}


public class AccountService {
    private RoleService roleService;

    public AccountService(RoleService roleService) {
        this.roleService = roleService;
    }

    public void print() {
        System.out.println("AccountService");
    }
}

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">

    <bean id="accountService" class="com.atguigu.service.AccountService">
        <constructor-arg name="roleService" ref="roleService"></constructor-arg>
    </bean>
    <bean id="roleService" class="com.atguigu.service.RoleService">
        <constructor-arg name="accountService" ref="accountService"></constructor-arg>
    </bean>

</beans>

测试:

public class TestAccountRole {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("AccountRole.xml");
        AccountService accountService = context.getBean("accountService", AccountService.class);
        accountService.print();
    }
}

报错:

Error creating bean with name 'accountService' defined in class path resource [AccountRole.xml]: Cannot resolve reference to bean 'roleService' while setting constructor argument

Error creating bean with name 'roleService' defined in class path resource [AccountRole.xml]: Cannot resolve reference to bean 'accountService' while setting constructor argument;

解决方案:

使用set方法注入,替代构造器注入

​ 为什么?首先,因为使用构造器方法注入Bean对象属性,会先将构造器的参数对象进行实例化,这样就容易陷入死循环中。而set方法进行对象属性注入,IOC容器会先将各个用bean标签修饰的对象注入实例化,实例化创建成功后,才会通过set方法,将对象实例传给其他对象实例中。

public class AccountService {
    private RoleService roleService;

//    // 构造器注入
//    public AccountService(RoleService roleService) {
//        this.roleService = roleService;
//    }

    // set方法进行属性注入
    public void setRoleService(RoleService roleService) {
        this.roleService = roleService;
    }

    public void print() {
        System.out.println("AccountService");
    }
}

public class RoleService {
    private AccountService accountService;

//    // 构造器注入
//    public RoleService(AccountService accountService) {
//        this.accountService = accountService;
//    }

    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }
}

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">

<!--    <bean id="accountService" class="com.atguigu.service.AccountService">-->
<!--        <constructor-arg name="roleService" ref="roleService"></constructor-arg>-->
<!--    </bean>-->
<!--    <bean id="roleService" class="com.atguigu.service.RoleService">-->
<!--        <constructor-arg name="accountService" ref="accountService"></constructor-arg>-->
<!--    </bean>-->

    <!--
        如果出现循环依赖,需要通过set注入解决
     -->
    <bean id="accountService" class="com.atguigu.service.AccountService">
        <property name="roleService" ref="roleService"></property>
    </bean>
    <bean id="roleService" class="com.atguigu.service.RoleService">
        <property name="accountService" ref="accountService"></property>
    </bean>

</beans>

4.5.4 p名称空间注入(了解)

4.5.5 xml自动装配(了解)


4.6 Bean的两种类型

4.7 Bean的作用域

  1. 在Spring里面,可以设置创建bean的实例是单实例还是多实例。
  2. 在Spring里面,默认情况下,bean是单实例对象。

也就是说,在IOC容器里面,只创建了一个实例对象,当需要被调用的时候,就返回索引

  1. 如何设置单实例还是多实例

4.8 Bean生命周期




4.9 配置外部数据库属性文件

引入外部属性文件配置数据库连接池

  1. 创建外部属性文件,properties格式文件,写数据库信息

    ​ 配置文件中,名称加一个前缀,是为了防止出现重名冲突

    prop.driverClass=com.mysql.cj.jdbc.Driver
    prop.url=jdbc:mysql://localhost:3306/school
    prop.userName=root
    prop.userPwd=zouwenhao
  2. 把properties属性文件引入到Spring配置文件中

    引入contex名称空间


    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context">

    <!--引入数据库配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--配置数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClass}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.userName}"></property>
        <property name="password" value="${prop.userPwd}"></property>
    </bean>

4.10 IOC操作Bean管理——基于注解方法(重点)

4.10.1 基于注解方式实现Bean对象实例化



4.10.2 基于注解方式实现属性注入


4.10.3 完全注解开发