Spring 5 基础

179 阅读16分钟

学习内容

  • Spring 概念
  • IOC 容器
  • AOP
  • JdbcTemplate
  • 事务管理
  • Spring5 新特性

Spring 框架概述

  1. Spring 是轻量级的开源 JavaEE 框架

  2. Spring 可以解决企业应用开发的复杂性

  3. Spring 有两个核心部分:IOC 和 AOP

    • IOC:控制反转,将创建对象过程交给 Spring 进行管理
    • AOP:面向切面,在不修改源代码的情况下,进行功能增强
  4. Spring 的特点

    • 方便解耦,简化开发
    • AOP 编程支持
    • 方便程序的测试
    • 方便和其它框架进行整合
    • 方便进行事务操作
    • 降低 API 开发

入门案例

  1. 下载 Spring5

  2. 创建 Spring项目

  3. 导入 Spring5 相关 jar 包:Project Structure -> Modules -> "+" JARs

    • Beans、Core、Context、Expression
  4. 创建普通类,在这个类中创建一个普通的方法

package com.atjava.spring5;

/**
 * @author lv
 * @create 2021-11-12 22:12
 */
public class User {

    public void add () {
        System.out.println("add...");
    }
}
  1. 创建 Spring 配置文件,在配置文件中配置创建的对象
    • Spring 配置文件使用 xml 格式
    • bean 标签,属性 id表示类名、class表示类的文件路径
<?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.atjava.spring5.User"></bean>
</beans>
  1. 进行测试代码编写
    • ApplicationContext 获取指定的 xml配置文件
    • ClassPathXmlApplicationContext 可以直接获取在 src 下的 xml配置文件
    • getBean() 创建指定的 类对象
package com.atjava.test;

import com.atjava.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author lv
 * @create 2021-11-12 22:22
 */
public class TestSpring5 {
    @Test
    public void testAdd () {
        // 1.加载 Spring配置文件
        ApplicationContext context
                = new ClassPathXmlApplicationContext("bean1.xml");

        // 2.获取配置文件创建的对象
        User user = context.getBean("user", User.class);
        System.out.println(user);
        user.add();
    }
}

IOC 容器

IOC 原理

IOC 的作用

  1. 控制反转,将对象创建和对象之间的调用过程,交给 Spring进行管理
  2. 使用 IOC目的:降低各类之间的耦合度

IOC 底层原理

  1. xml 解析
  2. 工厂模式
  3. 反射

图解 IOC底层原理

  1. 第一步,xml配置文件,配置创建的对象
<bean id="userDao" class="com.atjava.UserDao"></bean>
  1. 第二步,创建工厂类,解析 xml,通过 反射创建对象
class XxxFactory {
    public static UserDao getDao () {
        // 1.解析 xml
        String classValue = class属性值;
        // 2.通过反射创建对象
        Class clazz = Class.forName(classValue);
        return (UserDao)clazz.newInstance();
    }
}

IOC 接口(BeanFactory)

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

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

  1. BeanFactory:IOC容器基本实现,是 Spring的内部使用接口,不推荐开发人员使用;注意,使用此接口加载配置文件时不会创建对象,在获取(使用)对象时,才会创建对象
  2. ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能给开发者使用;此接口在加载配置文件时就会创建配置文件中所有的对象

ApplicationContext 接口的实现类

  • FileSystemXmlApplicationContext:需写出配置文件具体的盘符路径
  • ClassPathXmlApplicationContext:专门获取 src下的配置文件

IOC 操作 Bean 管理

Bean 管理,指的是两个操作

  • Spring 创建对象
  • Spring 注入属性

IOC 操作 Bean 管理(基于 xml)

基于 xml方式创建对象:

  1. 在 Spring 配置文件中,使用 bean标签,标签中添加对象属性,可以完成对象的创建
  2. bean标签中很多属性,介绍常用属性
    • id 属性:唯一标识
    • class 属性:类全路径(包类路径)
  3. 创建对象时,默认是执行无参数构造方法完成对象创建

基于 xml 方式注入属性: DI是 IOC中的一种具体实现,它表示 依赖注入。

  1. DI:依赖注入,就是注入属性,需在创建对象的基础上完成
  2. property标签,设置属性值

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

  • 使用 set方法,注入属性
  • 在类中添加对应属性的 setXxx()方法,在 xml中添加对应属性的 property标签
<bean id="book" class="com.atjava.spring5.Book">
    <!--
        set 方法注入属性:
            name:类中属性名称
            value:向属性中注入值
    -->
    <property name="bName" value="易筋经"></property>
    <property name="bAuthor" value="少林"></property>
</bean>
package com.atjava.spring5;

/**
 * @author lv
 * @create 2021-11-14 12:13
 */
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;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bName='" + bName + ''' +
                ", bAuthor='" + bAuthor + ''' +
                '}';
    }
}
@Test
public void testBook () {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Book book = context.getBean("book", Book.class);
    System.out.println(book); // Book{bName='易筋经', bAuthor='少林'}
}

第二种注入方式:使用有参数构造进行注入:

  • constructor-arg标签,设置对应的构造参数值
<bean id="orders" class="com.atjava.spring5.Orders">
    <constructor-arg name="oName" value="123"></constructor-arg>
    <constructor-arg index="2" value="789"></constructor-arg>
    <constructor-arg name="address" value="456"></constructor-arg>
</bean>
package com.atjava.spring5;

import java.math.BigDecimal;

/**
 * @author lv
 * @create 2021-11-14 15:21
 */
public class Orders {

    private String oName;
    private String address;
    private BigDecimal price;

    public Orders() {
    }

    public Orders(String oName, String address) {
        this.oName = oName;
        this.address = address;
    }

    public Orders(String oName, String address, BigDecimal price) {
        this.oName = oName;
        this.address = address;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Orders{" +
                "oName='" + oName + ''' +
                ", address='" + address + ''' +
                ", price=" + price +
                '}';
    }
}
@Test
public void testOrders () {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Orders orders = context.getBean("orders", Orders.class);
    System.out.println(orders.toString());
    // Orders{oName='123', address='456', price=789}
}

第三种注入方式:使用 p名称空间 进行注入,简化property注入方式(了解):

  1. 在配置文件中,添加 p名称空间
    xmlns:p="http://www.springframework.org/schema/p"
    
  2. 进行属性注入,在 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"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--<bean id="book" class="com.atjava.spring5.Book">-->
        <!--&lt;!&ndash;-->
            <!--set 方法注入属性:-->
                <!--name:类中属性名称-->
                <!--value:向属性中注入值-->
        <!--&ndash;&gt;-->
        <!--<property name="bName" value="易筋经"></property>-->
        <!--<property name="bAuthor" value="少林"></property>-->
    <!--</bean>-->

    <bean id="book" class="com.atjava.spring5.Book" p:bName="九阴震惊" p:bAuthor="作者"></bean>

</beans>

字面量:

  • null值:
  • 属性值包含特殊符号
    1. 特殊符号转义
    2. <![CDATA[xxx]]>
<bean id="book" class="com.atjava.spring5.Book">
    <!--
        set 方法注入属性:
            name:类中属性名称
            value:向属性中注入值
    -->
    <property name="bName" value="易筋经"></property>
    <property name="bAuthor" value="少林"></property>
    <!--
        设置属性值为 null
    -->
    <property name="pages">
        <null />
    </property>

    <!--
        属性值包含特殊符号
            1. 将 <>进行转义 &lt; &gt;
            2. 将含有特殊符号的值放到 CDATA中
    -->
    <property name="publish">
        <value><![CDATA[<@北平>]]></value>
    </property>
</bean>

注入属性,外部 bean

  • 创建两个类 service类 和 dao类
package com.atjava.spring5.dao;

/**
 * @author lv
 * @create 2021-11-14 21:12
 */
public interface UserDao {

    public void update ();
}
// ...
package com.atjava.spring5.dao;

/**
 * @author lv
 * @create 2021-11-14 21:13
 */
public class UserDaoImpl implements UserDao {

    @Override
    public void update() {
        System.out.println("dao update......");
    }
}
  • 在 service中调用 dao里面的方法
    1. 将 外部类设置为 此类的属性
package com.atjava.spring5.service;

import com.atjava.spring5.dao.UserDao;

/**
 * @author lv
 * @create 2021-11-14 21:11
 */
public class UserService {
    // 创建 UserDao类型属性,生成 set方法
    private UserDao userDao;

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

    public void add () {
        System.out.println("add.......");
        /**
         * 在 service中调用 dao中的方法:
         */
        // 1.使用普通方法调用
        // UserDao userDao2 = new UserDaoImpl();
        // userDao2.update();

        // 2.使用 xml方式调用
        userDao.update();
    }
}
  • 在 Spring配置文件中进行配置
    1. ref属性值:是 xml文件中 bean标签的 id属性值
<?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="userService" class="com.atjava.spring5.service.UserService">
        <!--
            注入外部 bean:
            name属性值:类中属性名称
            ref属性值:xml中 bean标签 id属性值
        -->
        <property name="userDao" ref="userDaoImpl"></property>
    </bean>

    <bean id="userDaoImpl" class="com.atjava.spring5.dao.UserDaoImpl"></bean>
</beans>

注入属性,内部 bean 和 级联赋值 数据库中的表关系有:一对多、一对一、多对多,内部 bean 和 级联赋值就与此有关。

  1. 一对多关系:部门和员工
  2. 在实体类之间表示 一对多关系,员工表示所属部门,使用对象类型属性进行表示
  3. 在 Spring 的 xml配置文件中进行相关配置
  • 部门和员工类
package com.atjava.spring5.bean;

/**
 * @author lv
 * @create 2021-11-21 11:28
 */
public class Dept {
    private String dName;

    public void setdName(String dName) {
        this.dName = dName;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "dName='" + dName + ''' +
                '}';
    }
}
package com.atjava.spring5.bean;

/**
 * @author lv
 * @create 2021-11-21 11:30
 */
public class Emp {
    private String eName;
    private String gender;
    // 员工部门,使用对象形式表示
    private Dept dept;

    public void seteName(String eName) {
        this.eName = eName;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "eName='" + eName + ''' +
                ", gender='" + gender + ''' +
                ", dept=" + dept +
                '}';
    }
}
  • Spring中的 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">

    <!--
        内部 bean 和 级联操作
        1. 内部 bean,在一个 bean中,内部可以嵌套另一个 bean
    -->
    <bean id="emp" class="com.atjava.spring5.bean.Emp">
        <!--普通属性设置-->
        <property name="eName" value="lv"></property>
        <property name="gender" value="男"></property>
        <!--
            对象类型属性设置:
            内部 bean 的设置
        -->
        <property name="dept">
            <bean id="dept" class="com.atjava.spring5.bean.Dept">
                <property name="dName" value="安保部"></property>
            </bean>
        </property>
    </bean>

    <!--
        级联赋值:
    -->
    <bean id="emp2" class="com.atjava.spring5.bean.Emp">
        <!--普通属性设置-->
        <property name="eName" value="lv"></property>
        <property name="gender" value="男"></property>
        <!--
            对象类型属性设置:
            内部 bean 的设置
        -->
        <property name="dept" ref="dept2"></property>
    </bean>
    <bean id="dept2" class="com.atjava.spring5.bean.Dept">
        <property name="dName" value="财务部"></property>
    </bean>

</beans>

IOC 操作 Bean 管理(xml 注入集合属性)

  • 注入 数组类型属性 array value
  • 注入 List集合类型属性 list value
  • 注入 Map集合类型属性 map、entry
  • 注入 Set集合类型属性 set value
package com.atjava.collectiontype;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author lv
 * @create 2021-11-21 15:33
 */
public class Stu {
    // 数组类型属性
    private String[] courses;

    // list集合类型属性
    private List<String> list;

    // map集合类型属性
    private Map<String, String> maps;

    // set集合类型属性
    private Set<String> sets;

    public void setCourses(String[] courses) {
        this.courses = courses;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }

    public void setSets(Set<String> sets) {
        this.sets = sets;
    }

    @Override
    public String toString() {
        return "Stu{" +
                "courses=" + Arrays.toString(courses) +
                ", list=" + list +
                ", maps=" + maps +
                ", sets=" + sets +
                '}';
    }
}
<?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="stu" class="com.atjava.collectiontype.Stu">
        <!--数组类型属性注入-->
        <property name="courses">
            <!--<list></list>-->
            <array>
                <value>java</value>
                <value>javaScript</value>
                <value>sql</value>
            </array>
        </property>
        <!--list类型属性注入-->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
            </list>
        </property>
        <!--map类型属性注入-->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="JavaScript" value="js"></entry>
            </map>
        </property>
        <!--set类型属性注入-->
        <property name="sets">
            <set>
                <value>MySql</value>
                <value>Redis</value>
            </set>
        </property>
    </bean>
</beans>

在集合中设置对象类型的值:

  • 使用 ref标签引入对象
<?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="stu" class="com.atjava.collectiontype.Stu">
        
        <!--集合类型的属性中存放对象-->
        <property name="courseList">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>
    </bean>
    <!--创建多个 course对象-->
    <bean id="course1" class="com.atjava.collectiontype.Course">
        <property name="cName" value="java"></property>
    </bean>
    <bean id="course2" class="com.atjava.collectiontype.Course">
        <property name="cName" value="JavaScript"></property>
    </bean>
</beans>
package com.atjava.collectiontype;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author lv
 * @create 2021-11-21 15:33
 */
public class Stu {

    private List<Course> courseList;

    public void setCourseList(List<Course> courseList) {
        this.courseList = courseList;
    }

    @Override
    public String toString() {
        return "Stu{" +
                "courses=" + Arrays.toString(courses) +
                ", list=" + list +
                ", maps=" + maps +
                ", sets=" + sets +
                ", courseList=" + courseList +
                '}';
    }
}

将集合注入部分提取出来,作为公共部分

  1. beans标签添加属性
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
    
  2. 使用 utils:xxx标签,提取对应的集合类型
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       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
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!--提取 list集合类型属性,作为公共部分-->
    <util:list id="bookList">
        <value>java</value>
        <value>JavaScript</value>
        <value>React</value>
    </util:list>

    <!--将提取出的部分 bookList,注入使用-->
    <bean id="book" class="com.atjava.collectiontype.Book">
        <property name="list" ref="bookList">
            <!--将此部分提取出来,作为公共部分使用-->
            <!--<list>-->
                <!--<value>java</value>-->
                <!--<value>JavaScript</value>-->
                <!--<value>React</value>-->
            <!--</list>-->
        </property>
    </bean>
</beans>
package com.atjava.collectiontype;

import java.util.List;

/**
 * @author lv
 * @create 2021-11-21 17:05
 */
public class Book {
    private List<String> list;

    public void setList(List<String> list) {
        this.list = list;
    }

    @Override
    public String toString() {
        return "Book{" +
                "list=" + list +
                '}';
    }
}

IOC 操作 Bean管理(FactoryBean)

  • Spring 有两种类型 bean,一种是普通 bean,另一种是工厂 bean(FactoryBean)
  • 普通 bean:在配置文件中定义 bean类型就是返回类型
  • 工厂 bean:在配置文件中定义 bean类型可以返回和返回的类型不同
  1. 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
  2. 第二部 实现接口里面的方法,在实现的方法中定义返回的 bean类型
package com.atjava.collectiontype.factorybean;

import com.atjava.collectiontype.Course;
import org.springframework.beans.factory.FactoryBean;

/**
 * @author lv
 * @create 2021-11-22 22:58
 */
public class MyBean implements FactoryBean<Course> {

    /**
     * 定义返回 bean类型
     *
     */
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setcName("芜湖");
        return course;
    }

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

    @Override
    public boolean isSingleton() {
        return false;
    }
}
<?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="myBean" class="com.atjava.collectiontype.factorybean.MyBean"></bean>
</beans>
@Test
public void testBean () {
    ApplicationContext context = new ClassPathXmlApplicationContext("factoryBean.xml");
    Course myBean = context.getBean("myBean", Course.class);
    System.out.println(myBean); // Course{cName='芜湖'}
}

IOC 操作 Bean 管理(bean作用域)

  1. 在 Spring中,设置创建 bean实例是 单实例还是 多实例
  2. 在 Spring中,默认情况下,bean是单实例对象
  3. 在 Spring中,设置对象的单实例或多实例方法
    • 在 Spring配置文件的 bean标签中通过属性 scope属性,设置单例多例
    • scope属性值:singleton (默认)单实例、prototype多实例、request、session
  4. 单实例和多实例的特点:
    • singleton单实例,在加载 Spring配置文件时就会去创建单实例对象
    • prototype多实例,只有在调用方法 getBean(...)时,才会去创建新的多实例对象
    • request,创建的对象会放到 request域对象中
    • session,创建的对象会放到 session域对象中
<bean id="book" class="com.atjava.collectiontype.Book" scope="prototype">
    <property name="list" ref="bookList">
    </property>
</bean>
// com.atjava.collectiontype.Book@7a36aefa
// com.atjava.collectiontype.Book@17211155

IOC 操作 Bean 管理(bean的生命周期)

bean 的生命周期

  1. 通过 构造器创建 bean实例(无参构造器)
  2. 为 bean的属性设置值和对其它 bean引用(调用 set方法)
    • 将 bean实例传递给 bean后置处理器的方法
  3. 调用 bean的初始化方法(需要进行配置初始化的方法)
    • 将 bean实例传递给 bean后置处理器的方法
  4. bean可以使用了(对象获取到了)
  5. 当容器关闭时,调用 bean的销毁方法(需要进行配置销毁的方法)
  • bean的后置处理器,bean生命周期共有七步
  1. 通过 构造器创建 bean实例(无参构造器)
  2. 为 bean的属性设置值和对其它 bean引用(调用 set方法)
  3. 将 bean实例传递给 bean后置处理器的方法 postProcessBeforeInitialization
  4. 调用 bean的初始化方法(需要进行配置初始化的方法)
  5. 将 bean实例传递给 bean后置处理器的方法 postProcessAfterInitialization
  6. bean可以使用了(对象获取到了)
  7. 当容器关闭时,调用 bean的销毁方法(需要进行配置销毁的方法)

bean 生命周期的演示

  • 手动调用销毁方法 context.close();
package com.atjava.collectiontype.bean;

import org.springframework.core.annotation.Order;

/**
 * @author lv
 * @create 2021-11-25 22:47
 */
public class Orders {
    private String oName;

    public Orders () {
        System.out.println("第一步:执行无参构造,创建 bean实例");
    }
    public void setoName(String oName) {
        System.out.println("第二步:调用 setXxx方法,设置属性");
        this.oName = oName;
    }
    // 创建 bean执行初始化的方法,无需手动调用
    public void initMethod () {
        System.out.println("第三步:创建 bean执行初始化的方法");
    }

    // 创建 bean执行销毁的方法,需手动调用
    public void destroyMethod () {
        System.out.println("第五步:创建 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">

    <!--
        init-method:指定初始化的方法
    -->
    <bean
            id="orders"
            class="com.atjava.collectiontype.bean.Orders"
            init-method="initMethod"
            destroy-method="destroyMethod"
    >
        <property name="oName" value="jiji"></property>
    </bean>
</beans>
@Test
public void testBean2 () {
    // ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
    Orders orders = context.getBean("orders", Orders.class);
    System.out.println("第四步:获取创建 bean实例的对象");
    System.out.println(orders);
    // ((ClassPathXmlApplicationContext) context).close();
    // 手动调用销毁方法
    context.close();
    /**
     * 第一步:执行无参构造,创建 bean实例
     * 第二步:调用 setXxx方法,设置属性
     * 在 第三步:初始化之前执行的方法
     * 第三步:创建 bean执行初始化的方法
     * 在 第三步:初始化之后执行的方法
     * 第四步:获取创建 bean实例的对象
     * com.atjava.collectiontype.bean.Orders@33723e30
     * 第五步:创建 bean销毁的方法
     */
}

bean 演示添加后置处理器的效果

  1. 创建类,实现接口 BeanPostProcessor,创建后置处理器
package com.atjava.collectiontype.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @author lv
 * @create 2021-11-25 23:23
 */
public class MyBeanPostProcessor 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;
    }

}
  1. 在配置文件中添加 后置处理器实现类
<?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">

    <!--
        init-method:指定初始化的方法
    -->
    <bean
            id="orders"
            class="com.atjava.collectiontype.bean.Orders"
            init-method="initMethod"
            destroy-method="destroyMethod"
    >
        <property name="oName" value="jiji"></property>
    </bean>
    <!--
        配置后置处理器 MyBeanPostProcessor
        当在 配置文件中,添加了实现 BeanPostProcessor的类时,
        配置文件会为配置文件中的所有 bean实例,默认添加此后置处理器
    -->
    <bean id="myBeanPostProcessor" class="com.atjava.collectiontype.bean.MyBeanPostProcessor"></bean>
</beans>

IOC 操作 Bean 管理(xml 自动装配)

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

演示自动装配过程

  • bean标签属性 autowire,配置自动装配
  • autowire属性常用的两个值:
    1. byName 根据属性名称注入,注入值 bean的 id值和类属性名称一样
    2. byType 根据属性类型注入
<?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标签属性 autowire,配置自动装配
            autowire 属性常用两个值:
                byName根据属性名称注入,注入值 bean的 id值和类属性名称一样
                byType根据属性类型注入,
    -->
    <!--<bean id="emp" class="com.atjava.autowrite.Emp" autowire="byName">-->
    <bean id="emp" class="com.atjava.autowrite.Emp" autowire="byType">
        <!--手动装配-->
        <!--<property name="dept" ref="dept"></property>-->
    </bean>
    <bean id="dept" class="com.atjava.autowrite.Dept"></bean>
</beans>

IOC 操作 Bean 管理(外部属性文件,数据库必备)

直接配置数据库信息

  1. 配置德鲁伊连接池
  2. 引入德鲁伊连接池依赖 jar包
<?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">

    <!--
        配置 druid连接池
        使用 properties配置文件
    -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--
            driverClassName:驱动名称
            // com.mysql.jabc.Driver
            url:数据库地址
            // jdbc:mysql://localhost:3306/userDb
            username:数据库用户名 root
            password:密码 root
        -->
        <property name="driverClassName" value="com.mysql.jabc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        
    </bean>
</beans>

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

  1. 创建外部属性文件 jdbc.properties,写入数据库信息
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.username=root
prop.password=root
  1. 将外部 properties属性配置文件,引入到 Spring配置文件中,使用 context名称空间

        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/beans/spring-context.xsd"
    
    • 在 Spring配置文件中使用标签 context 引入外部属性文件
  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: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/context http://www.springframework.org/schema/beans/spring-context.xsd"
>

    <!--
        配置 druid连接池
        使用 properties配置文件
    -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!--
            driverClassName:驱动名称
            // com.mysql.jabc.Driver
            url:数据库地址
            // jdbc:mysql://localhost:3306/userDb
            username:数据库用户名 root
            password:密码 root
        -->
        <!--
            引入外部属性文件:
            classpath:在 src文件下使用
        -->
        <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
        <property name="driverClassName" value="${prop.driverclass}"></property>
        <property name="url" value="${prop.username}"></property>
        <property name="username" value="${prop.username}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean>
</beans>

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

注解:

  1. 注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值...)
  2. 使用注解,注解可以作用在 类、属性、方法上
  3. 使用注解的目的,简化 xml配置,使用更优雅、更简便的方式实现配置

Spring 针对 Bean管理中创建对象提供的注解 以下四个注解功能是一样的,都可以用来创建 Bean实例,但建议不同的注解用于不同的 Bean实例

  1. @Component:普通注解,一般都能使用
  2. @Service:一般用于业务逻辑层 或 service层
  3. @Controller:一般用于 web层
  4. @Repository:一般用于 DAO层 或 持久层

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

  1. 第一步,引入依赖 spring-aop-5.2.6.RELEASE.jar
  2. 第二步,开启组件扫描
  • 如果多个包,多个包分别使用逗号隔开
  • 扫描包上层目录
  1. 创建类,在类上面添加创建对象注解
  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: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/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--
        Bean管理,基于注解方式
        开启组件扫描:
            base-package:标明扫描的文件
            1.如果多个包,多个包分别使用逗号隔开
            2.扫描包上层目录
    -->
    <!--<context:component-scan base-package="com.atjava.spring5.service,com.atjava.spring5.dao"></context:component-scan>-->
    <context:component-scan base-package="com.atjava.spring5"></context:component-scan>
</beans>
package com.atjava.spring5.service;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * @author lv
 * @create 2021-12-09 23:28
 */

/**
 * 在注解里面 value属性值可以省略不写,
 * 默认值为类名称,首字母小写
 */
//@Component(value = "userService") // 此功能与 xml中 <bean id="userService" class="..." /> 类似
@Service(value = "userService")
public class UserService {

    public void add() {
        System.out.println("add userService ...");
    }
}
package com.atjava.spring5.test;

import com.atjava.spring5.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author lv
 * @create 2021-12-09 23:34
 */
public class TestSpring5 {

    @Test
    public void testSpring () {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
        /**
         * com.atjava.spring5.service.UserService@f2f2cc1
         * add userService ...
         */
    }
}

开启组件扫描细节配置

  • 属性 use-default-filters="false":表示不使用 Spring默认的 filter,使用自己设置的 filter
  • 标签 context:include-filter:设置被扫描的内容
  • 标签 context:exclude-filter:设置不被扫描的内容
    • 属性 type="annotation":表示注解
    • 属性 expression="...":表示路径
<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--
        Bean管理,基于注解方式
        开启组件扫描:
            base-package:标明扫描的文件
            1.如果多个包,多个包分别使用逗号隔开
            2.扫描包上层目录
    -->
    <!--<context:component-scan base-package="com.atjava.spring5.service,com.atjava.spring5.dao"></context:component-scan>-->
    <context:component-scan base-package="com.atjava.spring5"></context:component-scan>

    <!--
        示例1:
            use-default-filters="false":表示不使用默认 filter,使用自己配置的 filter
            context:include-filter:设置被扫描的内容
    -->
    <context:component-scan base-package="com.atjava.spring5" use-default-filters="false">
        <context:include-filter
                type="annotation"
                expression="org.springframework.stereotype.Controller"
        ></context:include-filter>
    </context:component-scan>

    <!--
        示例2:
            context:exclude-filter:设置不被扫描的内容
    -->
    <context:component-scan base-package="com.atjava">
        <context:exclude-filter
                type="annotation"
                expression="org.springframework.stereotype.Controller"
        ></context:exclude-filter>
    </context:component-scan>
</beans>

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

  • @Autowired:根据属性类型进行自动装配

  • @Qualifier:根据属性名称进行注入

  • @Resource:根据属性类型注入 或 根据属性名称注入

  • @Value:注入普通类型属性

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

  1. 把 service和 dao对象创建,在 service和 dao类添加创建对象注解
  2. 在 service注入 dao对象,在 service类添加 dao对象类型属性,在属性上使用注解
package com.atjava.spring5.dao;

import org.springframework.stereotype.Repository;

/**
 * @author lv
 * @create 2021-12-10 22:34
 */
@Repository(value = "userDaoImpl")
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("userDao add...");
    }
}
package com.atjava.spring5.service;

import com.atjava.spring5.dao.UserDao;
import com.atjava.spring5.dao.UserDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * @author lv
 * @create 2021-12-09 23:28
 */

/**
 * 在注解里面 value属性值可以省略不写,
 * 默认值为类名称,首字母小写
 */
//@Component(value = "userService") // 此功能与 xml中 <bean id="userService" class="..." /> 类似
@Service(value = "userService")
public class UserService {

    /**
     * 定义 dao类型属性
     * 不需要添加 set方法
     * 添加注入属性注解
     */
    @Autowired // 根据类型实现注入,类似于 bean标签的 byType
    private UserDao userDao;

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

    public void add() {
        System.out.println("add userService ...");
        userDao.add();
    }
}
package com.atjava.spring5.test;

import com.atjava.spring5.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author lv
 * @create 2021-12-09 23:34
 */
public class TestSpring5 {

    @Test
    public void testSpring () {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();

        /**
         * com.atjava.spring5.service.UserService@501edcf1
         * add userService ...
         * userDao add...
         */
    }
}

@Qualifier:根据属性名称进行注入

  • 这个 @Qualifier 注解的使用的前提,要和上面的 @Autowired一起使用
  • 因为一个接口的实现类可能不止一个,如果只靠类型去获取对应的实现类时,无法判断具体是哪个实现类,所以需要对应实现类的 名称进行获取
package com.atjava.spring5.dao;

import org.springframework.stereotype.Repository;

/**
 * @author lv
 * @create 2021-12-10 23:02
 */
@Repository(value = "userDaoImpl2")
public class UserDaoImpl2 implements UserDao {
    @Override
    public void add() {
        System.out.println("userDaoImpl2 add...");
    }
}
package com.atjava.spring5.service;

import com.atjava.spring5.dao.UserDao;
import com.atjava.spring5.dao.UserDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * @author lv
 * @create 2021-12-09 23:28
 */

/**
 * 在注解里面 value属性值可以省略不写,
 * 默认值为类名称,首字母小写
 */
//@Component(value = "userService") // 此功能与 xml中 <bean id="userService" class="..." /> 类似
@Service(value = "userService")
public class UserService {

    /**
     * 定义 dao类型属性
     * 不需要添加 set方法
     * 添加注入属性注解
     */
    @Autowired // 根据类型实现注入,类似于 bean标签的 byType
    @Qualifier(value = "userDaoImpl2")
    private UserDao userDao;

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

    public void add() {
        System.out.println("add userService ...");
        userDao.add();
    }
}
package com.atjava.spring5.test;

import com.atjava.spring5.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author lv
 * @create 2021-12-09 23:34
 */
public class TestSpring5 {

    @Test
    public void testSpring () {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();

        /**
         * com.atjava.spring5.service.UserService@22635ba0
         * add userService ...
         * userDaoImpl2 add...
         */
    }
}

@Resource:根据类型注入 或 根据名称注入

  • @Resource 是 java的扩展包 javax中的功能,Spring建议使用 Autowired 和 Qualifier;import javax.annotation.Resource;
package com.atjava.spring5.service;

import com.atjava.spring5.dao.UserDao;
import com.atjava.spring5.dao.UserDaoImpl;
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.Component;
import org.springframework.stereotype.Service;

// @Resource 在 java的扩展包中,不是 Spring中的,所以 Spring不建议使用 @Resource
import javax.annotation.Resource;

/**
 * @author lv
 * @create 2021-12-09 23:28
 */

/**
 * 在注解里面 value属性值可以省略不写,
 * 默认值为类名称,首字母小写
 */
//@Component(value = "userService") // 此功能与 xml中 <bean id="userService" class="..." /> 类似
@Service(value = "userService")
public class UserService {

    /**
     * 定义 dao类型属性
     * 不需要添加 set方法
     * 添加注入属性注解
     */

    // @Resource // 根据类型注入,如果同一个接口有多个实现类,那根据类型注入会报错
    @Resource(name = "userDaoImpl2") // 根据名称注入
    private UserDao userDao;

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

    public void add() {
        System.out.println("add userService ...");
        userDao.add();
    }
}
package com.atjava.spring5.test;

import com.atjava.spring5.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.annotation.Resource;

/**
 * @author lv
 * @create 2021-12-09 23:34
 */
public class TestSpring5 {

    @Test
    public void testSpring () {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();

        // @Resource(name = "userDaoImpl2") // 根据名称注入
        /**
         * com.atjava.spring5.service.UserService@4f638935
         * add userService ...
         * userDaoImpl2 add...
         */
    }
}

@Value:注入普通类型属性

package com.atjava.spring5.service;

import com.atjava.spring5.dao.UserDao;
import com.atjava.spring5.dao.UserDaoImpl;
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.Component;
import org.springframework.stereotype.Service;

// @Resource 在 java的扩展包中,不是 Spring中的,所以 Spring不建议使用 @Resource
import javax.annotation.Resource;

/**
 * @author lv
 * @create 2021-12-09 23:28
 */

/**
 * 在注解里面 value属性值可以省略不写,
 * 默认值为类名称,首字母小写
 */
//@Component(value = "userService") // 此功能与 xml中 <bean id="userService" class="..." /> 类似
@Service(value = "userService")
public class UserService {

    @Value(value = "lv")
    private String name;

    @Value(value = "123")
    private Integer age;
    /**
     * 定义 dao类型属性
     * 不需要添加 set方法
     * 添加注入属性注解
     */

    // @Resource // 根据类型注入,如果同一个接口有多个实现类,那根据类型注入会报错
    @Resource(name = "userDaoImpl2") // 根据名称注入
    private UserDao userDao;

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

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

    @Override
    public String toString() {
        return "UserService{" +
                "name='" + name + ''' +
                ", age=" + age +
                ", userDao=" + userDao +
                '}';
    }
}

// UserService{name='lv', age=123, userDao=com.atjava.spring5.dao.UserDaoImpl2@3901d134}