Spring03——基于xml的Spring应用

发布于:2024-12-08 ⋅ 阅读:(192) ⋅ 点赞:(0)

Spring开发中主要对Bean的配置

Bean的常用配置一览如下:

Xml配置方式 功能描述
<bean id="" class=""> Bean的id和全限定名配置
<bean name=""> 通过name设置Bean的别名,通过别名也能直接获取到Bean实例
<bean scope=""> Bean的作用范围,BeanFactory作为容器时取值singleton和prototype
<bean lazy-init=""> Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效
<bean init-method=""> Bean实例化后自动执行的初始化方法,method指定方法名
<bean destroy-method=""> Bean实例销毁前的方法,method指定方法名
<bean autowire="byType"> 设定自动注入模式,常用的有按类型byType,按名称byName
<bean factory-bean="" factory-method=""/> 指定哪个工厂Bean的哪个方法来完成Bean的创建

 Bean取别名

通过name取别名,同样可以获取实例

 

Bean的范围配置

默认情况下,单纯的Spring环境Bean的作用范围有两个(scope配置):Singleton和Prototype

  • singleton:单例,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean时都是从单例池中获取相同的Bean实例;
  • prototype(默认):原型,Spring容器初始化时不会创建Bean实例,当调用getBean时才会实例化Bean,每次getBean都会创建一个新的Bean实例。

Bean的延迟加载

当lazy-init设置为true时为延迟加载,也就是当Spring容器创建的时候,不会立即创建Bean实例等待用到时在创建Bean实例并存储到单例池中去,后续在使用该Bean直接从单例池获取即可本质上该Bean还是单例

<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" lazy-init="true"/> 
        ApplicationContext applicationContext = new                 
        ClassPathXmlApplicationContext("applicationContext.xml");
        // lazy-init="true"开启,那么只有调用getBean时才会创建Bean实例,而不是之前创建容器时
        UserService userService = (UserService) applicationContext.getBean("aaa");
        System.out.println(userService);

Bean的初始化和销毁方法配置

Bean在被实例化后,可以通过指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作。

<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"
 init-method="init" destroy-method="destroy"/>

定义init方法和destory方法 

public class UserDaoImpl implements UserDao {  
    public UserDaoImpl() { System.out.println("UserDaoImpl创建了..."); }  
    public void init() { System.out.println("初始化方法..."); }  
    public void destroy() { System.out.println("销毁方法..."); }  
}

 

 由上图可见,并没有显示销毁方法

关闭容器Bean就销毁,否则bean销毁不了,因为关闭容器是在虚拟机结束之前执行,如果没有close,那么虚拟机关闭,什么都没有了。 

扩展:除此之外,我们还可以通过实现 InitializingBean 接口,完成一些Bean的初始化操作,如下:

public class UserDaoImpl implements UserDao, InitializingBean {  
    public UserDaoImpl() { System.out.println("UserDaoImpl创建了..."); }  
    
    public void init() { System.out.println("初始化方法..."); }  
    
    public void destroy() { System.out.println("销毁方法..."); }  
    
    // 执行时机早于init-method配置的方法  
    @Override
    public void afterPropertiesSet() throws Exception {  
        System.out.println("InitializingBean...");  
    }  
}

  使用InitializingBean , DisposableBean接口(推荐)

 这样就不需要再Bean标签里写init和destory了

Bean的生命周期 

下面是Spring Bean生命周期示例,包括创建、初始化和销毁的代码。

Bean类

创建一个简单的Bean类,并定义初始化和销毁方法:

import org.springframework.beans.factory.InitializingBean;  
import org.springframework.beans.factory.DisposableBean;  

public class MyBean implements InitializingBean, DisposableBean {  

    private String name;  
    private String version;  
    private String author;  

    public MyBean() {  
        System.out.println("1. 创建对象 - 构造器");  
    }  

    public void setName(String name) {  
        this.name = name;  
        System.out.println("设置属性 - name: " + name);  
    }  

    public void setVersion(String version) {  
        this.version = version;  
        System.out.println("设置属性 - version: " + version);  
    }  

    public void setAuthor(String author) {  
        this.author = author;  
        System.out.println("设置属性 - author: " + author);  
    }  

    @Override  
    public void afterPropertiesSet() throws Exception {  
        System.out.println("4. 执行Bean初始化方法 (afterPropertiesSet)");  
        System.out.println("Bean 信息 - Name: " + name + ", Version: " + version + ", Author: " + author);  
    }  

    @Override  
    public void destroy() throws Exception {  
        System.out.println("关停 Bean - 销毁方法 (destroy)");  
    }  

    public void performTask() {  
        System.out.println("执行业务操作 - " + name);  
    }  
}

Spring配置

使用XML配置来定义Bean,指定其由接口管理的初始化和销毁方法:

 <bean id="myBean" class="com.example.MyBean">  
        <property name="name" value="Spring Bean"/>  
        <property name="version" value="1.0"/>  
        <property name="author" value="John Doe"/>  
    </bean>  

主程序

创建Application类,加载Spring上下文,使用Bean并关闭上下文以触发销毁阶段:

import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  

public class Application {  

    public static void main(String[] args) {  
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  

        MyBean myBean = (MyBean) context.getBean("myBean");  
        // 执行myBean的一些业务

        // 关闭上下文,触发销毁方法  
        ((ClassPathXmlApplicationContext) context).close();  
    }  
}

Bean的3种实例化

构造方式实例化:底层通过构造方法对Bean进行实例化  

工厂方式实例化:底层通过调用自定义的工厂方法对Bean进行实例化

Bean的实例化配置

构造方式实例化Bean又分为无参构造方法实例化和有参构造方法实例化,Spring中配置的<bean>几乎都是无参构造方式,但处不在赘述。下面讲解有参构造方法实例化Bean

// 有参构造方法  
public UserDaoImpl(String name){  
}

在实现类文件里默认有一个无参构造器,如果写一个有参构造器,那么jvm识别不到无参构造器会报错 。

在实现类文件中写入有参构造方法后,有参构造在实例化Bean时,需要参数的注入,通过<constructor-arg>标签(name表示传入的变量名,value表示变量名对应的传入参数,在bean标签里传入即可,调用getBean会自动添加进去,如果有多个参数,就多个<constructor-arg>标签),嵌入在<bean>标签内部提供构造参数,如下:

        <!--上面的有参构造器对应的xml配置-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">  
    <constructor-arg name="name" value="haohao"/>  
</bean>

静态工厂创建对象

factory-method 创建对象:即在xml的bean标签里配置factory-method = "userDao",那么创建getBean对象返回的不是class对应的类的实例,而是class类里面userDao静态方法返回的实例

<bean id="userDao1" class="com.itheima.factory.MyBeanFactory1"
 factory-method="userDao"></bean>
public class MyBeanFactory1 {
    public static UserDao userDao(){
        // bean之前可以进行其他的业务逻辑操作
        return new UserDaoImpl();
    }
}

实例工厂创建对象

<--实例化一个工厂-->
<bean id="myBeanFactory2" class="com.itheima.factory.MyBeanFactory2"
 lazy-init="true"></bean>
<--factory-bean是代指工厂-->
<bean id="userDao2" factory-bean="myBeanFactory2" 
factory-method="userDao" lazy-init="true"></bean>

public class MyBeanFactory2 {
// 实例方法返回对象
    public UserDao userDao(){
        return new UserDaoImpl();
    }
}

FactoryBean创建(实用)

package com.itheima.factory;

import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;

public class UserDaoFactoryBean implements FactoryBean {
    // 返回bean
    @Override
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }
    // 返回Bean的类型
    @Override
    public Class<?> getObjectType() {
        return UserDao.class;
    }
// 是否为单例
  @Override
    public boolean isSingleton() {
        return true;
    }
}
<bean id="UserDao" class="com.itheima.factory.UserDaoFactoryBean"></bean>

 依赖注入的方式

一、setter方法注入引用类型/简单类型(下面以同时注入这两种类型为例)

假设我们有一个Product类,它包含一个简单类型属性(如productId)和一个引用类型属性(如Category)。

1. 定义Category

首先,定义Category类,表示产品类别:

public class Category {  
    private String categoryName;  

    // Setter方法  
    public void setCategoryName(String categoryName) {  
        this.categoryName = categoryName;  
    }  

    // Getter方法  
    public String getCategoryName() {  
        return categoryName;  
    }  

    public void displayInfo() {  
        System.out.println("Category: " + categoryName);  
    }  
}

2. 定义Product

接下来,定义一个Product类,它具有一个简单类型的productId和一个引用类型的Category

public class Product {  
    private String productId; // 简单类型  
    private Category category; // 引用类型  

    // Setter方法注入  
    public void setProductId(String productId) {  
        this.productId = productId;  
    }  

    public void setCategory(Category category) {  
        this.category = category;  
    }  

    public void displayProductInfo() {  
        System.out.println("Product ID: " + productId);  
        if (category != null) {  
            category.displayInfo();  
        } else {  
            System.out.println("No category information available.");  
        }  
    }  
}

3. Spring配置文件

在Spring的XML配置文件中,定义CategoryProduct的Bean,并同时注入简单类型和引用类型:

<!-- applicationContext.xml -->  
<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="category" class="com.example.Category">  
        <property name="categoryName" value="Electronics"/> <!-- 注入简单类型 -->  
    </bean>  

    <bean id="product" class="com.example.Product">  
        <property name="productId" value="P12345"/>        <!-- 注入简单类型name是形参 -->  
        <property name="category" ref="category"/>         <!-- 注入引用类型 -->  
    </bean>  
</beans>

4. 主程序

最后,创建一个主程序,通过Spring上下文来获取Product对象并执行相应操作:

import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;  

public class Application {  
    public static void main(String[] args) {  
        // 加载Spring上下文  
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  

        // 获取product Bean  
        Product product = (Product) context.getBean("product");  
        product.displayProductInfo(); // 显示产品信息  

        // 关闭上下文  
        ((ClassPathXmlApplicationContext) context).close();  
    }  
}

运行结果

运行程序将输出以下内容:

Product ID: P12345  
Category: Electronics

二、构造器注入引用类型/简单类型(下面以同时注入这两种类型为例)

1. 定义Category

Category类保持不变,依然表示产品类别

2. 定义Product

Product类的构造函数修改为接收两个参数:一个简单类型productId和一个引用类型Category

public class Product {  
    private String productId; // 简单类型  
    private Category category; // 引用类型  

    // 构造函数注入  
    public Product(String productId, Category category) {  
        this.productId = productId;  
        this.category = category;  
    }  

    public void displayProductInfo() {  
        System.out.println("Product ID: " + productId);  
        if (category != null) {  
            category.displayInfo();  
        } else {  
            System.out.println("No category information available.");  
        }  
    }  
}

3. Spring配置文件

在Spring的XML配置文件中,使用构造器来注入简单类型和引用类型。我们可以使用constructor-arg来指定构造函数参数:

<!-- applicationContext.xml -->  
<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="category" class="com.example.Category">  
        <property name="categoryName" value="Electronics"/> <!-- 注入简单类型 -->  
    </bean>  

    <bean id="product" class="com.example.Product">  
        <constructor-arg value="P12345"/>              <!-- 注入简单类型 -->  
        <constructor-arg ref="category"/>               <!-- 注入引用类型 -->  
    </bean>  
</beans>

4. 主程序

主程序实例保持不变,只是调用已经不同的注入手段(构造器)来获取Product对象并执行操作

  • 强制依赖使用构造器进行,使用setter注入有概率不进行时导致null对象出现
  • 可选依赖使用setter注入进行,灵活性强
  • Spring框架倡导使用构造器,第三方框架内部多数采用构造器注入的形式进行数据初始化,相对严谨
  • 如果有必要可以同时使用,使用构造器注入完全依赖的注入,使用setter注入完成可选依赖的注入
  • 实际开发过程中需要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
  • 自己开发的模块推荐使用setter注入 

依赖自动装配

  • 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  • 使用按类型装配时(byType)必须保证容器中相同类型的bean唯一,推荐使用
  • 使用按名称装配时(byName)必须保证容器中具有指定名称的bean,因为变量名与配置混合,不推荐使用
  • 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

集合的注入

<!-- 定义一个bean,id为bookDao,对应的实现类为com.itheima.dao.impl.BookDaoImpl -->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <!-- 配置一个名为array的属性,类型为数组 -->
    <property name="array">
        <!-- 数组元素 -->
        <array>
            <!-- 数组中的值 -->
            <value>100</value>
            <value>200</value>
            <value>300</value>
        </array>
    </property>
    <!-- 配置一个名为list的属性,类型为列表 -->
    <property name="list">
        <!-- 列表元素 -->
        <list>
            <!-- 列表中的值 -->
            <value>itcast</value>
            <value>itheima</value>
            <value>boxuegu</value>
            <value>chuanzhihui</value>
        </list>
    </property>
    <!-- 配置一个名为set的属性,类型为集合 -->
    <property name="set">
        <!-- 集合元素 -->
        <set>
            <!-- 集合中的值,注意集合不允许重复值 -->
            <value>itcast</value>
            <value>itheima</value>
            <value>boxuegu</value>
            <!-- 重复的值boxuegu将被忽略 -->
            <value>boxuegu</value>
        </set>
    </property>
    <!-- 配置一个名为map的属性,类型为映射 -->
    <property name="map">
        <!-- 映射元素 -->
        <map>
            <!-- 映射中的键值对 -->
            <entry key="country" value="china"/>
            <!-- 可以继续添加更多的键值对 -->
        </map>
    </property>
</bean>

网站公告

今日签到

点亮在社区的每一天
去签到