当前位置: 首页 > article >正文

【Java SpringIOC与ID随感录】 基于 XML 的 Bean 装配

前言


        我们知道了 Spring 是⼀个开源框架,他让我们的开发更加简单。他⽀持⼴泛的应⽤场 景,有着活跃⽽庞⼤的社区,这也是 Spring 能够⻓久不衰的原因。

这里来举个例子:

开发业务逻辑层一般是:控制层、业务逻辑层、持久层。

SoldierServlet - 控制层
SoldierService soldierService = new SoldierServicelmpl();

SoldierService - 业务逻辑层
SoldierServicelmpl
SoldierDao soldierDao = new SoldierDaolmpl();

SoldierDao - 持久层
SoldierDaolmpl
SoldierServlet - 控制层
SoldierService soldierService = new SoldierServicelmpl() -> (导致功能失效);

SoldierService - 业务逻辑层
SoldierServicelmpl
SoldierDao soldierDao = new SoldierDaolmpl() -> SoldierDaolmpl2;

SoldierDao - 持久层
SoldierDaolmpl -> SoldierDaolmpl2

        如果现在需要把 SoldierDaolmpl 改成 SoldierDaolmpl2 。那么 SoldierService 层也要接着改,而且这样会导致 SoldierServlet 层用不了。下层改动,导致上层不得不改动。我们将这个现象称之为层与层之间的耦合。在开发中,我们要尽量降低层之间的耦合。

        那么Spring是怎么解决这个问题的呢? 

SoldierServlet - 控制层
SoldierService soldierService = null;

SoldierService - 业务逻辑层
SoldierServicelmpl
SoldierDao soldierDao = null;

SoldierDao - 持久层
SoldierDaolmpl -> SoldierDaolmpl2

        将 soldierService 与 soldierDao 的值置为空,那么就算底层 SoldierDao 改动了,也不会影响上层。虽然这种方法解决了耦合,但是这样是肯定不行,会报 NullPointerException。因此,不能让它等于null。怎么解决呢?解决方法:在调用它的方法之前给它赋值。

那么此时就会存在两个问题:

① 对象实例谁去创建
② 谁负责给这个变量赋值

        之前,这个对象的创建以及变量的赋值都是程序员负责的。而现在Spring提供了一个工厂BeanFactory,这个工厂帮我们解决这两个问题。

  • 对象的创建
  • 依赖关系的维护

        也就是说,对象的创建和依赖关系的维护从程序员的手中转移到 BeanFactory 我们将这个现象称之为控制反转(IOC),在 BeanFactory 工厂中负责注入每一个依赖关系的这种行为称之为(ID)。

控制:组件的生命周期的控制权
反转:控制权从程序员手中反转到 IOC 容器

        本章知识就是基于 XML 配置文件去理解 SpringIOC 的使用。


前期回顾:【Java Maven框架】


目录

前言

快速搭建spring项目

1.添加依赖

2.创建实体类

3.配置 XML 文件

4.测试获取 UserDemo 实例对象

基于 XML 的 Bean 装配

SpringIOC与反射

属性赋值

setter 注入对象属性

ref 注入引用对象

内部 bean 的注入

引入外部 Properties 配置参数

构造器注入

p 名称空间注入

集合类型的注入

Bean 的作用域

bean 的初始化与销毁方法

 

快速搭建spring项目


1.添加依赖

    <properties>
        <maven.compiler.source>22</maven.compiler.source>
        <maven.compiler.target>22</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>6.1.14</spring.version>
        <junit.version>5.11.3</junit.version>
        <lombok.version>1.18.34</lombok.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
    </dependencies>

2.创建实体类

@Data
public class UserDemo {
    private String Username;
    public void User(){
        System.out.println(Username+"欢迎用户登入");
    }
}

  • 注解使用效果: 使用 @Data 后,Lombok 会自动为 UserDemo 类的 name 字段生成 getter、setter、构造函数、toString 和 equals、hashCode 方法。

3.配置 XML 文件

        在 resources 包下创建名字为 applicationContext.xml 的IOC配置文件,描述 UserDemo 这个类。

<?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 -->
    <bean id="u01" class="com.thz.UserDemo">
        <property name="Username" value="东方"/>
    </bean>
</beans>

4.测试获取 UserDemo 实例对象

        这样就不是程序员自己去 new 对象了,而是交给 IOC 容器,让它帮我们创建对象。

public class UserDemoTest {
    @Test
    public void UserTest() {
        // 获取IOC容器
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从IOC容器中取出userDemo
        UserDemo userDemo = (UserDemo) beanFactory.getBean("u01");
        userDemo.User();
    }
}

基于 XML 的 Bean 装配


SpringIOC与反射

        Spring的IOC容器实质上也是通过反射技术去创建bean实例,以上面代码为例,利用反射去创建对象实例并获取。

        // 创建对象实例
        Class clazz = Class.forName("com.thz.UserDemo");
        clazz.getDeclaredConstructor().newInstance();
        // 获取对象实例
        UserDemo userDemo = (UserDemo) clazz.getDeclaredConstructor().newInstance();
        userDemo.setUsername("东方");
        userDemo.User();

运行结果: 

东方欢迎用户登入

属性赋值


setter 注入对象属性

    <bean id="u01" class="com.thz.UserDemo">
        <property name="Username" value="东方"/>
    </bean>

        我们的实体类的属性赋值,是 IOC 通过 property 中的 value 标签直接赋值给对象属性吗?其实并不然,IOC 是通过 setter 来注入对象属性。setter 注入简单来说就是调用类的 set 方法对该类属性进行赋值。

public class UserDemo {
    private String Username;
    public void User(){
        System.out.println(Username+"欢迎用户登入");
    }

    public void setUsername(String username) {
        System.out.println("正在注入依赖~");
        Username = username;
    }
}
...
    <bean id="u01" class="com.thz.UserDemo">
        <property name="Username" value="东方"/>
    </bean>
...

        我们可以测试一下,取消掉注解并在 set 方法这里添加一行 print 语句观察打印结果如何?

运行结果:

正在注入依赖~
东方欢迎用户登入

        发现 SpringIOC 在赋值类属性时自动调用了 set 方法。使用 setter 注入的好处就是写法上比较直观。 

ref 注入引用对象

 mxl 配置文件

<?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="u01" class="com.thz.UserDemo">
        <property name="Username" value="东方"/>
        <property name="books" ref="b01"/>
    </bean>
    <bean id="b01" class="com.thz.Book">
        <property name="bookName" value="楚辞"/>
        <property name="author" value="屈原"/>
        <property name="bookPrice" value="9.9"/>
    </bean>
</beans>

Book类

@Data
public class Book {
    private String bookName;
    private String author;
    private double bookPrice;
}

UserDemo类

@Data
public class UserDemo {
    private String Username;
    private Book books;
    public void User(){
        System.out.println(Username+"购买了"+ books.getAuthor()+"作者的"+books.getBookName()+"作品,花了"+books.getBookPrice()+"元~");
    }
}

运行结果:

东方购买了屈原作者的楚辞作品,花了9.9元~
标签描述
ref 通过 bean 的 id 引用另一个 bean

        只要声明到 ioc 容器的 bean 对象,都可被其他 bean 引用。



内部 bean 的注入

        内部 bean 与内部类差不多就是 bean 包 bean ,这样就避免了使用 ref 引用。

 <bean id="u01" class="com.thz.UserDemo">
        <property name="Username" value="东方"/>
        <property name="books">
            <bean class="com.thz.Book">
                <property name="bookName" value="楚辞"/>
                <property name="author" value="屈原"/>
                <property name="bookPrice" value="9.9"/>
            </bean>
        </property>
    </bean>

引入外部 Properties 配置参数

        在 resources 资源包下新增一个 properties 文件 books.properties,写入 Book 对应属性:

bookName=MySQL
author=Monty
bookPrice=9.9

XML配置文件:

    <!-- 先引入 properties 文件 -->
    <context:property-placeholder location="classpath:books.properties"/>
    <bean id="u01" class="com.thz.UserDemo">
        <property name="Username" value="东方"/>
        <property name="books">
            <bean class="com.thz.Book">
                <property name="bookName" value="${bookName}"/>
                <property name="author" value="${author}"/>
                <property name="bookPrice" value="${bookPrice}"/>
            </bean>
        </property>
    </bean>
</beans>

运行结果:

东方购买了Monty作者的MySQL作品,花了9.9元~

        这也算是 xml 的常见用法了,虽然现在用的大多是注解。我们在 xml 中导入 properties 文件;在利用 ${} 对类中对应属性进行提取,在通过 setter 注入。这样修改一些属性的时候便可以直接对文件进行操作,就避免了去看复杂的 xml 配置文件了。

构造器注入

        依旧以上面的例子为例:

Book类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
    private String bookName;
    private String author;
    private double bookPrice;
}

 @NoArgsConstructor、@AllArgsConstructor 都是 Lombok 提供的注解,作用如下

注解作用
@NoArgsConstructor在类上使用,这个注解可以生成无参构造方法
@AllArgsConstructor在类上使用,这个注解可以自动生成一个包含所有实例变量的构造函数

xml 配置文件 

<bean id="u01" class="com.thz.UserDemo">
        <property name="Username" value="东方"/>
        <property name="books">
            <bean class="com.thz.Book">
                <constructor-arg value="诗经"/>
                <constructor-arg value="孔子"/>
                <constructor-arg value="9.9"/>
            </bean>
        </property>
</bean>

        使用构造器注入,类中一定要有一个无参构造方法。如果没有就会报错。构造注入使用的标签是 <constructor-arg> 必须按照类中定义的顺序进行注入,否则抛出 BeanCreationException 类型不匹配异常。

        不过我们可以使用 index[索引] 来解决这个问题:

<bean id="u01" class="com.thz.UserDemo">
        <property name="Username" value="东方"/>
        <property name="books">
            <bean class="com.thz.Book">
                <constructor-arg value="9.9" index="2"/>
                <constructor-arg value="诗经"/>
                <constructor-arg value="孔子"/>
            </bean>
        </property>
</bean>

 

p 名称空间注入

        p名称空间注入走的也是 setter 方法,简化 setter 注入需要 property 标签这个步骤,一步到位。

<bean id="u01" class="com.thz.UserDemo" p:username="东方" p:books-ref="b01"/>
<bean id="b01" class="com.thz.Book" p:bookName="诗经" p:author="孔子" p:bookPrice="9.9"/>

集合类型的注入

        Spring框架支持注入集合类型的数据,下面将通过一个例子来说明如何使用Spring框架注入集合类型的数据:

        假设我们的实体类包含数组类型、List类型、Set类型、Map类型等数据,具体如下:

Book类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
    private String bookName;
    private String author;
    private double bookPrice;
}

Student类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private String StudentName;
    private Book[] books;
    private List<Book> bookList;
    private Set<Book> bookSet;
    private Map<String,Book> bookMap;
}

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"
       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="book1" class="com.thz.Book" p:bookName="诗经" p:author="孔子" p:bookPrice="9.9"/>
    <bean id="book2" class="com.thz.Book" p:bookName="离骚" p:author="屈原" p:bookPrice="19.9"/>
    <bean id="book3" class="com.thz.Book" p:bookName="吕氏春秋" p:author="吕不韦" p:bookPrice="29.9"/>
    <bean id="book4" class="com.thz.Book" p:bookName="平凡世界" p:author="路遥" p:bookPrice="59.9"/>
    <bean id="student1" class="com.thz.Student">
        <property name="studentName" value="东方"/>
        <!-- 数组注入 -->
        <property name="books">
            <array>
                <ref bean="book1"/>
                <ref bean="book2"/>
                <ref bean="book3"/>
            </array>
        </property>
        <!-- list注入 -->
        <property name="bookList">
            <list>
                <ref bean="book2"/>
                <ref bean="book3"/>
            </list>
        </property>
        <!-- set注入 -->
        <property name="bookSet">
            <set>
                <ref bean="book3"/>
                <ref bean="book4"/>
            </set>
        </property>
        <!-- map注入 -->
        <property name="bookMap">
            <map>
               <entry key="001" value-ref="book1"/>
               <entry key="002" value-ref="book2"/>
               <entry key="003" value-ref="book3"/>
               <entry key="004" value-ref="book4"/>
            </map>
        </property>
    </bean>
</beans>

测试类 

public class StudentDemoTest {
    @Test
    public void StudentTest() throws Exception {
        // 获取IOC容器
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从IOC容器中取出Student
        Student student = (Student) beanFactory.getBean("student1");
        System.out.println(student);
    }
}

 

Bean 的作用域


常用作用域:

取值含义创建对象的时机默认值
singleton在 IOC容器中,这个 bean 的对象始终为单实例I0C容器初始化时
prototype这个 bean 在 IOC 容器中有多个实例获取 bean 时

案例:

Book类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
    private String bookName;
    private String author;
    private double bookPrice;
}

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"
       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 name="book1" class="com.thz.Book" p:bookName="诗经" p:author="孔子" p:bookPrice="9.9" scope="singleton"/>
</beans>

测试代码:

public class BookDemoTest {
    private BeanFactory beanFactory;
    @BeforeEach
    public void setUp() {
        beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
    @Test
    public void BookTest() {
        Book book1 = (Book)beanFactory.getBean("book1");
        Book book2 = (Book)beanFactory.getBean("book1");
        System.out.println(book1 == book2);
    }
}

运行结果为:

true

        我们发现结果为 true ,说明创建的是同一个对象,所以这种默认标签 singleton 创建的是单例的。将标签改成 prototype 发现结果为 false。说明它不是单例的。

bean 的初始化与销毁方法


案例:

Book类

@Data
public class Book {
    public Book(){
        System.out.println("Book 正在被创建");
    }

    public void BookInit(){
        System.out.println("Book 正在初始化");
    }

    public void BookDestroy(){
        System.out.println("Book 正在被销毁");
    }
}

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 name="Book1" class="com.thz.Book" init-method="BookInit" destroy-method="BookDestroy"/>
</beans>

测试代码:

public class BookTest {
    private BeanFactory beanFactory;

    @BeforeEach
    public void setUp() {
        beanFactory = new ClassPathXmlApplicationContext("applicationContext.xml");
    }

    @Test
    public void Test(){
        System.out.println(beanFactory.getBean("Book1"));
        ((ClassPathXmlApplicationContext)beanFactory).close();
    }
}

运行结果:

Book 正在被创建
Book 正在初始化
Book 正在被销毁

        通过以上案例,我们可以知道:可以在 XML 文件中使用 init-method、destroy-method 来指定初始化与销毁时的内容。


http://www.kler.cn/a/375495.html

相关文章:

  • 【C语言】宏封装的实用总结
  • 2024年10月文章一览
  • 视频设备一体化监控运维方案
  • 【golang/navmesh】使用recast navigation进行寻路
  • 微服务实战系列之玩转Docker(十六)
  • 深度|谁在为OpenAI和Anthropic的AI编程竞赛提供“军火”?已赚得盆满钵满
  • vue3官方示例-简单的 markdown 编辑器。
  • Unity3D MMORPG游戏服务器之物理模拟系统详解
  • Python进阶 | Django框架数据库与Model之间的关系
  • 谷歌将差异隐私扩展到近 30 亿台设备
  • HTML 文档规范与解析模式:DOCTYPE、<html> 标签以及结构化页面
  • 指定用户开启自启vncserver
  • 深入探讨SEO分析技巧助力网站流量提升
  • linux基础-lvm逻辑卷组分区实操
  • MySql基础:事务
  • Python代码优雅解析PDF文件
  • 游戏和各大APP改IP地址方法教程
  • java控制台打印减法口诀
  • 【机器学习】22. 聚类cluster - K-means
  • python openai API token超限制
  • 测试Bug提交报告模板
  • Linux-期末考试试题8套(含答案)
  • JavaIO流操作
  • BGP路由优选+EVPN
  • npm入门教程6:npm脚本
  • PHP实现雪花算法生成唯一ID