Spring学习(二)容器、基于XML管理Bean

容器:IoC

Inversion of Control,作为一种重要的面向对象编程设计思想,指导设计出低耦合、更优良的程序;
Spring通过IoC容器来管理**所有Java对象的实例化和初始化,控制对象与对象间的依赖关系;
由IoC容器管理的对象称为SPring Bean,但实质上与new创建的Java对象没有任何区别;

IoC容器

控制反转

  1. 作为一种重要的面向对象编程的设计思想;
  2. 目的是为了降低程序耦合度,提高程序扩展力;
  3. 反转的内容是:

将对象的创建权利移交给第三方容器负责;
将对象与对象间的关系维护权移交给第三方容器负责;

  1. 由DI(Dependency Injection)依赖注入实现该思想;

依赖注入

  1. 定义:Spring创建对象的过程中,将对象依赖属性通过配置进行注入,有两种方式:

set注入
构造注入

  1. Bean管理:Bean对象的创建,以及Bean对象中属性的赋值(或称为Bean对象间关系的维护);

IoC容器在Spring中的实现

  1. BeanFactory:作为IoC容器的基本实现形式,是Spring内部接口,不提供给使用者;
  2. ApplicationContext:BeanFactory的子接口,面向使用者,几乎所有场合都使用该接口;
  3. AppliicationContext的主要实现类:
    在这里插入图片描述

基于XML管理Bean

搭建子模块spring_ioc_xml

  1. 将原子项目中的依赖移至父工程的依赖中,让整个工程都能够使用;
  2. 创建子模块spring_ioc_xml
  3. 创建User类和bean.xml配置文件;
    在这里插入图片描述

获取Bean

根据id获取,返回object类型,需要强制类型转换;
根据类型获取,返回对应类型,要求指定类型Bean只能有一个,多个则选择第三个方法;
根据id和类型获取,返回对应类型;

在这里插入图片描述
同理,当接口实现类的Bean唯一时,可以通过接口类型直接获取Bean。反之多个实现类,则会出现Bean不唯一的错误;

    <bean id="userDaoImpl" class="com.jobs.spring6.iocxml.bean.UserDaoImpl"></bean>

在这里插入图片描述

依赖注入

  1. 类属性在创建对象过程中有两种方式设定属性值:

基于set方法:对象.set(xxx);
基于构造器:类 对象 = new 类("xxx");

  1. set方法注入
    创建类,生产对应setter方法
    在这里插入图片描述
    <bean id="car" class="com.jobs.spring6.iocxml.di.Car">
        <!--
            property:通过组件类的setXxx()方法给组件对象设置属性
            name:指定属性名(其通过getXxx、setXxx方法定义的,与成员变量无关)
            value:指定属性值
        -->
        <property name="name" value="雷军"></property>
        <property name="brand" value="小米汽车"></property>
    </bean>
  1. 构造器注入
    创建类,生成有参构造方法
    <bean id="car2" class="com.jobs.spring6.iocxml.di.Car">
        <!--
            constructor-arg;通过组件类的有参构造方法对组件对象设置属性
            name:指定属性名
            index:指定属性索引
            value:指定属性值
        -->
        <constructor-arg name="name" value="马克思"></constructor-arg>
        <constructor-arg index="1" value="Tesla"></constructor-arg>
    </bean>

特殊值处理

  1. 字面量赋值
    使用value属性赋值时,spring会将该属性的值视为字面量;
  2. null
    value会将"null"视为字面量并赋值给对象,若要留空值需:
<property name="name">
	<null/>
</property>
  1. xml实体
    在XML文档中存在特殊标签,无法直接作为值使用,需要使用转义字符代替:
eg.
&lt; = <
&gt; = >
...value="&lg;&gt;"... ===> "<>" 
  1. CDATA
    CDATA代表纯文本数据,使得XML解析器不会对节中内容当作XML标签或属性解析:
eg.
<property name="name">
	<value><![CDATA[a < b]]></value>
</property>

对象类型赋值

  1. 外部Bean注入
  • 注入的部门类
public class Department {
    private String dept;

    public String getDept() {
        return dept;
    }

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

    public String info(){
        return dept;
    }
}
  • 被注入的面试者类
package com.jobs.spring6.iocxml.di;

public class Employee {
    public Employee(String name, int age, Department dept) {
        this.name = name;
        this.age = age;
        this.dept = dept;
    }
    public Employee() {}
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void setDept(Department dept) {
        this.dept = dept;
    }
    private String name;
    private int age;
    private Department dept;
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", dept=" + dept.info() +
                '}';
    }
}
  • 注入过程
<bean id="dept" class="com.jobs.spring6.iocxml.di.Department">
	<property name="dept" value="保安"></property>
</bean>
    
<bean id="employee" class="com.jobs.spring6.iocxml.di.Employee">
	<property name="name" value="Lux"></property>
	<property name="age" value="19"></property>
	<!--
		ref:指定其它Bean的id,以此完成外部Bean注入
	-->
	<property name="dept" ref="dept"></property>
</bean>
  1. 内部Bean注入
  • 注入过程
<bean id="employee2" class="com.jobs.spring6.iocxml.di.Employee">
    <property name="name" value="Jinx"></property>
    <property name="age" value="17"></property>
    <property name="dept">
        <!--
            在value位置用需要注入的Bean代替
        -->
        <bean id="dept2" class="com.jobs.spring6.iocxml.di.Department">
            <property name="dept" value="保安"></property>
        </bean>
    </property>
</bean>

但好像内部注入的Bean不会创建?
在这里插入图片描述
3. 级联属性赋值

<bean id="employee3" class="com.jobs.spring6.iocxml.di.Employee">
    <property name="name" value="Jax"></property>
    <property name="age" value="35"></property>
    <!--
         用ref标签外部注入后,
         在name属性中指定注入对象的成员,
         可以在此处重新注入对象值,
         或许意味着可以多重注入(内部或外部)
    -->
    <property name="dept" ref="dept3"></property>
    <property name="dept.dept" value="大厅保洁"></property>
</bean>

数组类型赋值

  • 数组类
public class Person {
    private String name;
    private String[] habit;
    public Person(String name, String[] habit) {
        this.name = name;
        this.habit = habit;
    }
    public Person() {}
    public void setName(String name) {
        this.name = name;
    }
    public void setHabit(String[] habit) {
        this.habit = habit;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", habit=" + Arrays.toString(habit) +
                '}';
    }
}
  • 注入过程
<bean id="person" class="com.jobs.spring6.iocxml.di_assemble.Person">
    <property name="name" value="于谦"></property>
    <property name="habit">
        <!--
            数组用array标签,
            在后续用value依次存储单个元素
        -->
        <array>
            <value>抽烟</value>
            <value>喝酒</value>
            <value>烫头</value>
        </array>
    </property>
</bean>
  • 结果
    在这里插入图片描述

集合类型赋值

  1. List集合
    先在Department中添加List集合,与对应方法
private List<Employee> employeeList;
  • 注入过程与数组类似,使用list标签,同时用ref引用外部Bean
<bean id="employee1" class="com.jobs.spring6.iocxml.di.Employee">
    <property name="name" value="Ash"></property>
    <property name="age" value="28"></property>
</bean>
<bean id="employee2" class="com.jobs.spring6.iocxml.di.Employee">
    <property name="name" value="Ajax"></property>
    <property name="age" value="24"></property>
</bean>

<bean id="dept" class="com.jobs.spring6.iocxml.di.Department">
    <property name="dept" value="地勤"></property>
    <property name="employeeList">
        <list>
            <ref bean="employee1"></ref>
            <ref bean="employee2"></ref>
        </list>
    </property>
</bean>
  1. Map集合
    student类创建集合,及对应方法
private Map<String, Teacher> teacherMap;
  • 注入过程类似,
<bean id="teacherone" class="com.jobs.spring6.iocxml.di_assemble.Teacher">
    <property name="name" value="Walter"></property>
    <property name="number" value="100"></property>
</bean>

<bean id="teachertwo" class="com.jobs.spring6.iocxml.di_assemble.Teacher">
    <property name="name" value="Jason"></property>
    <property name="number" value="101"></property>
</bean>

<bean id="student" class="com.jobs.spring6.iocxml.di_assemble.Student">
    <property name="name" value="Lihua"></property>
    <!--
        类似的,
        使用map标签对集合进行赋值,
        单个元素用entry标签(List集合直接value或ref),
        key标签对Map.key赋值,
        value或ref标签对Map.value赋值
    -->
    <property name="teacherMap">
        <map>
            <entry>
                <key>
                    <value>100111</value>
                </key>
                <ref bean="teacherone"></ref>
            </entry>
            <entry>
                <key>
                    <value>101000</value>
                </key>
                <ref bean="teachertwo"></ref>
            </entry>
        </map>
    </property>
</bean>
  1. 引用集合的Bean
    bean.xml开始的beans标签内添加util约束(共3行),添加后才可以使用
    在这里插入图片描述
  • Student添加List,及对应方法
private List<Lesson> lessonList;
  • 注入过程中,先创建元素Bean,再使用util标签,最后向目标处赋值
<bean id="teacherone" class="com.jobs.spring6.iocxml.di_assemble.Teacher">
    <property name="name" value="Walter"></property>
</bean>

<bean id="teachertwo" class="com.jobs.spring6.iocxml.di_assemble.Teacher">
    <property name="name" value="Jason"></property>
</bean>

<bean id="lessonone" class="com.jobs.spring6.iocxml.di_assemble.Lesson">
    <property name="name" value="cook"></property>
</bean>

<bean id="lessontwo" class="com.jobs.spring6.iocxml.di_assemble.Lesson">
    <property name="name" value="drug"></property>
</bean>
<!--
    加载约束后使用util标签单独建立集合的Bean,
    在使用场景直接引用对应util的id即可
-->
<util:map id="teacherMap">
    <entry>
        <key>
            <value>100111</value>
        </key>
        <ref bean="teacherone"></ref>
    </entry>
    <entry>
        <key>
            <value>101000</value>
        </key>
        <ref bean="teachertwo"></ref>
    </entry>
</util:map>

<util:list id="lessonList">
    <ref bean="lessonone"></ref>
    <ref bean="lessontwo"></ref>
</util:list>

<bean id="student" class="com.jobs.spring6.iocxml.di_assemble.Student">
    <property name="name" value="Lihua"></property>
    <property name="teacherMap" ref="teacherMap"></property>
    <property name="lessonList" ref="lessonList"></property>
</bean>

p名称空间注入

beans标签内添加p空间和注入的Bean,完成注入
在这里插入图片描述### 引入外部属性文件

  1. pom.xml中加入依赖
<!--MySQL驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.30</version>
</dependency>

<!--数据源-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.31</version>
</dependency>
  1. 创建jdbc.properties
jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.driver=com.mysql.cj.jdbc.Driver
  1. bean.xml中引入context约束后,用context标签引入外部属性文件jdbc.properties
    在这里插入图片描述
  2. 注入过程
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <!--
        在存在外部属性文件时,
        可以在value标签中用${}引入外部属性
    -->
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.user}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="driverClassName" value="${jdbc.driver}"></property>
</bean>
  1. 试验与对比
    @Test
    public void demo2(){
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean_jdbc.xml");
        DruidDataSource dataSource = context.getBean(DruidDataSource.class);
        System.out.println(dataSource.getUrl()/*getUsername、getPassword、getDriverClassName*/);
    }

在这里插入图片描述

Bean的作用域

取值含义创建时机
singleton(默认)在IoC容器中,该类Bean对象始终为单实例,即多次获取相同目标IoC容器初始化时
prototype该类Bean对象在IoC容器中存在多个实例,即每次获取新的实例获取Bean时

注入时声明

<!--不使用scope指明时默认为singleton,单独指明为prototype才为多实例Bean-->
<bean id="user1" class="com.jobs.spring6.iocxml.User"
    scope="singleton">
    <property name="name" value="Max"></property>
</bean>
<bean id="user2" class="com.jobs.spring6.iocxml.User"
      scope="prototype">
    <property name="name" value="Wax"></property>
</bean>

对两种类型的Bean进行实验

@Test
public void userTest(){
    ApplicationContext context =
            new ClassPathXmlApplicationContext("bean_jdbc.xml");
    User user1 = context.getBean("user1", User.class);
    System.out.println(user1);
    User user2 = context.getBean("user1", User.class);
    System.out.println(user2);
    User user3 = context.getBean("user2", User.class);
    System.out.println(user3);
    User user4 = context.getBean("user2", User.class);
    System.out.println(user4);
}

由输出可知,单实例Bean对象唯一,多实例每次获取新对象;
同时在IoC容器初始化时完成单实例对象的创建,多实例只有在获取时才创建(所以不在输出中)

2024-04-03 14:21:17 689 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5c08c46a
2024-04-03 14:21:17 856 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [bean_jdbc.xml]
2024-04-03 14:21:17 888 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'user1'
com.jobs.spring6.iocxml.User@63f34b70
com.jobs.spring6.iocxml.User@63f34b70
com.jobs.spring6.iocxml.User@56b78e55
com.jobs.spring6.iocxml.User@76318a7d

Bean的生命周期

  1. Bean对象的创建(默认调用类的无参构造)
  2. 向Bean对象注入相关属性
  3. Bean后置处理器(初始化前)
  4. Bean对象初始化(调用指定初始化方法,用init-method指定)
  5. Bean后置处理器(初始化后)
  6. Bean对象的使用
  7. Bean对象的销毁(调用指定销毁方法,用destroy-method指定)
<bean id="user" class="com.jobs.spring6.iocxml.User"
    init-method="initMethod" destroy-method="destroyMethod">
    <property name="name" value="Tax"></property>
</bean>
  • 手动使用后置处理器需要类实现特定接口,同时后置处理并不针对特定某个Bean,而是对IoC容器中所有Bean对象生效
public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
    <bean id="myBeanProcessor" class="com.jobs.spring6.iocxml.MyBeanPost"></bean>

FactoryBean

不同于BeanFactory是spring内部的接口,用以达成IoC容器的实现,FactoryBean是spring提供的一种整合第三方框架的常用机制。配置该类型获取Bean时,得到的并不是class属性中的类对象,而是getObject()方法的返回值。

基于xml自动装配

  1. 自动装配:根据制定策略,在IoC容器中匹配某个Bean,自动为其中所依赖的类类型或接口类型属性赋值;
  2. 根据业务顺序,controller层需要注入service层,service层需要注入dao层,原生完成方式是在上层中写入对下层接口的直接调用;
  3. 创建controller/service/dao
//controller
public class Controller {
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    private UserService userService;

    public void controller(){
        System.out.println("controller");
        userService.service();
    }
}
//serviceinterface
public interface UserService {
    public void service();
}

//serviceimplement
public class UserServiceImpl implements UserService{
    private UserDao userDao;

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

    @Override
    public void service() {
        System.out.println("service");
        userDao.dao();
    }
}
//daointerface
public interface UserDao {
    public void dao();
}

//daoimplement
public class UserDaoImpl implements UserDao{
    @Override
    public void dao() {
        System.out.println("dao");
    }
}
  1. 自动装配
    <!--
        autowire作用为自动装配:

        byType时按照类型进行自动选择,
        若没有对应类型===》装配为NULL,
        若只有一个对应类型===》正确装配,
        若有多个对应类型===》抛出异常;

        byName时按照名称进行自动装配,
        选择id与对应注入属性的属性名相同者进行赋值;
    -->
<bean id="controller" class="com.jobs.spring6.iocxml.auto.controller.Controller"
      autowire="byType"></bean>

<bean id="userService" class="com.jobs.spring6.iocxml.auto.service.UserServiceImpl"
    autowire="byName"></bean>

<bean id="userDao" class="com.jobs.spring6.iocxml.auto.dao.UserDaoImpl"
      autowire="byType"></bean>
  1. 测试
public class UserTest {
    @Test
    public void userTest(){
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean_auto.xml");
        Controller controller = context.getBean("controller", Controller.class);
        controller.controller();
    }
}
2024-04-03 15:44:23 435 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@61c9c3fd
2024-04-03 15:44:23 591 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 3 bean definitions from class path resource [bean_auto.xml]
2024-04-03 15:44:23 620 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'controller'
2024-04-03 15:44:23 657 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
2024-04-03 15:44:23 658 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userDao'
controller
service
dao
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值