Spring(上)

2023-04-26 18:39:35 浏览数 (1)

参考:https://gitee.com/turnon/spring-tutorial

参考:https://www.bilibili.com/video/BV1kR4y1b7Qc?p=1&vd_source=0c3c1f43c75954a15fba4e42c1d7883e

1. 概述

1.1 文档

  • Spring官方文档:https://spring.io
  • Spring Framework官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/index.html

1.2 Spring

1.2.1 广义&侠义Spring

  • 广义上的Spring泛指以Spring Framework为核心的Spring技术栈
    • 经过十多年的发展,Spring 已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如 Spring Framework、Spring MVC、SpringBoot、Spring Cloud、Spring Data、Spring Security 等,其中 Spring Framework 是其他子项目的基础。
  • 狭义的Spring:Spring Framework
    • 狭义的 Spring 特指 Spring Framework,通常我们将它称为 Spring 框架。
    • Spring 框架是一个分层的、面向切面的 Java 应用程序的一站式轻量级解决方案,它是 Spring 技术栈的核心和基础,是为了解决企业级应用开发的复杂性而创建的。

1.3 Spring优势

  • 方便解耦,简化开发
    • Spring通过容器,将对象的创建从代码中剥离出来,交给Spring控制,避免直接编码造成模块之间的耦合度高,用户也不必自己编码处理对象的单例和多例控制,主要关注接口功能即可,不用关注具体使用哪个实现类和实现细节问题
  • AOP切面编程
    • AOP切面编程是程序设计的一种概念,Spring对该概念实现的比较好,通过切面编程我们可以在不修改原有代码的情况下实现功能的增加,通常用于 事务控制,日志记录,性能检测,权限控制等等
  • 声明式事务
    • 事务的控制可以托管给Spring,我们通过注解或者配置文件声明事务的处理方式即可,不用我们自己去编码处理
  • 整合JUNIT,方便测试
    • Spring整合JUNIT单元测试,对于项目的功能都可以进行轻松快速的测试,便于我们调试程序
  • 方便整合各种优秀的框架
    • SSM > Spring SpringMVC MyBatis
    • SSH > Spring Hibernate Strust
    • 各种其他框架
  • 丰富的功能封装
    • Spring对JAVAEE(JDBC, JAVAMail)都进行了一系列的封装,简化我们对于API的使用,提高程序的开发效率
  • 规范的源码学习样本
    • Spring的源码设计巧妙,结构清晰,大量使用了设计模式,是java代码规范编写的典范,也是高级程序员面试中经常会问到的源码

1.4 体系结构

Spring 当前框架有20个 jar 包,大致可以分为6大模块

  • Core Container
    • BeanFactory
    • ApplicationContext
  • AOP and Instrumentation
  • Messaging
  • Data Access / Integaration
  • Web
  • Test

1.4.1 Core Container

  • spring core提供了IOC,DI,Bean配置装载创建的核心实现。核心概念: Beans、BeanFactory、BeanDefinitions、ApplicationContext。
  • 模块
    • Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。jar包:spring-core。
    • Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。jar包:spring-beans。
    • Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。jar包:spring-context
    • Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。jar包:spring-expression。
    • springcontext-support包:集成第三方库到 Spring application context。
  • Spring自带了几种容器的实现,可归纳为两种类型
    • BeanFactory:由 org.springframework.beans.factory.BeanFactory 接口定义。 它是最简单的容器,提供基本的 DI 支持。
    • ApplicationContext:由 org.springframework.context.ApplicationContext 接口定义。 它是基于 BeanFactory 之上构建,并提供面向应用的服务,例如从属性文件解析文本信息的能力,以及发布应用事件给感兴趣的事件监听者的能力。 注:Bean 工厂对于大多数应用来说往往太低级了,所以应用上下文使用更广泛。推荐在开发中使用应用上下文容器。
  • Spring 自带了多种应用上下文,最可能遇到的有以下几种
    • ClassPathXmlApplicationContext:从类路径下的 XML 配置文件中加载上下文定义,把应用上下文定义文件当做类资源。
    • FileSystemXmlApplicationContext:读取文件系统下的 XML 配置文件并加载上下文定义。
    • XmlWebApplicationContext:读取 Web 应用下的 XML 配置文件并装载上下文定义。

1.4.2 AOP and Instrumentation

  • 模块
    • AOP 模块:提供了面向切面编程实现,整合ASM,CGLib,JDK Proxy。允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。jar包:spring-aop。
    • Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。jar包:spring-aspects。
    • Instrumentation 模块:提供了类工具( instrumentation)的支持和类加载器的实现,可以在特定的应用服务器中使用。动态Class Loading模块。jar包:spring-instrument,spring-instrument-tomcat(Spring 对 Tomcat 的 instrumentation 代理)。

1.4.3 Messaging

  • 相关 jar 包
    • spring-messaging:spring 4.0提供的,为Spring集成一些基础的报文传送服务。包含 spring 的消息处理功能,如 Message,MessageChannel,MessageHandler。

1.4.4 Data Access / Integaration(数据访问/集成)

  • 模块
    • JDBC 模块:spring对JDBC的封装,用于简化jdbc操作。提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。jar包:spring-jdbc。
    • ORM 模块:java对象与数据库数据的映射框架。对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。jar包:spring-orm。
    • OXM 模块:对象与xml文件的映射框架。提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。jar包:spring-oxm。
    • JMS 模块:Spring对Java Message Service(java消息服务)的封装,用于服务之间相互通信。指 Java 消息服务,包含的功能为生产和消费的信息。jar包:spring-jms。
    • Transactions 事务模块:spring jdbc事务管理。支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。jar包:spring-tx。

1.4.5 Web

  • 模块
    • Web 模块:提供了基本的 Web 开发集成特性,建立于spring-context之上,通过servlet或listener来初始化IOC容器。例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。jar包:spring-web。
    • Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
    • Struts 模块:包含支持类内的 Spring 应用程序,集成了经典的 Struts Web 层。
    • Portlet 模块:提供了在 Portlet 环境中使用 MVC实现,类似 Web-Servlet 模块的功能。
  • 相关 jar 包
    • spring-web:提供了基本的面向 web 的功能,如多文件上传、使用 Servlet 监听器的 Ioc 容器的初始化。一个面向 web 的应用层上下文。
    • spring-webmvc:包括 MVC 和 REST web 服务实现。实现web mvc。
    • spring-websocket:与前端的全双工通信协议
    • spring-webmvc-portlet:提供在 Protlet 环境的 MVC 实现和spring-webmvc功能的镜像。
    • spring-webflux:Spring 5.0提供的,用于取代传统java servlet,非阻塞式Reactive Web框架,异步,非阻塞,事件驱动的服务

1.4.6 Test

  • 相关 jar 包
    • spring-test,以 Junit 和 TestNG 来支持 spring 组件的单元测试和集成测试。

1.5 核心思想

  • Spring最核心的两个技术思想是:IoC 和 AOP

1.5.1 IOC

  • IoC 即 Inversion of Control ,意为控制反转。指把创建对象过程交给 Spring 进行管理
  • Spring 最认同的技术是控制反转的依赖注入(DI)模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。

1.5.2 AOP

  • Aspect Oriented Programming的简写,译为“面向切面编程”。
  • AOP 用来封装多个类的公共行为,将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP 还解决一些系统层面上的问题,比如日志、事务、权限等。

1.6 Spring Framework特点

  • 非侵入式:使用 Spring Framework 开发应用程序时,Spring 对应用程序本身的结构影响非常小。对领域模型可以做到零污染;对功能性组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构,反而能将组件结构进一步简化。这就使得基于 Spring Framework 开发应用程序时结构清晰、简洁优雅。
  • 控制反转:IoC——Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。
  • 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功能。
  • 容器:Spring IoC 是一个容器,因为它包含并且管理组件对象的生命周期。组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发效率。
  • 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用 XML 和 Java 注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭建超大型复杂应用系统。
  • 一站式:在 IoC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且 Spring 旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在 Spring Framework 的基础上全部使用 Spring 来实现。

2. HelloWorld

2.1 环境

  • Spring5
  • Maven3.6.2
  • java8

2.2 入门程序

  • pom.xml
代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>spring-turbo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>spring-turbo-01-hello</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.15</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>
  • Hello
代码语言:javascript复制
package com.example.bean;

public class Hello {
    public void sayHello(){
        System.out.println("hello spring ~");
    }
}
  • 1-1-hello.xml
代码语言:javascript复制
<?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="hello" class="com.example.bean.Hello"></bean>

</beans>
  • HelloTest
代码语言:javascript复制
import com.example.bean.Hello;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloTest {
    @Test
    public void testHello(){
        ApplicationContext app = new ClassPathXmlApplicationContext("1-1-hello.xml");
        Hello h = (Hello) app.getBean("hello");
        h.sayHello();
    }
}

2.3 Spring创建对象原理

2.3.1

  • dom4j解析beans.xml文件,从中获取class属性值,类的全类名
代码语言:javascript复制
<bean id="hello" class="com.example.bean.HelloWorld"></bean>
  • 将上面的信息读取进入程序对象的ID,一个是对象的类的全路径名

2.3.2 反射技术实例化对象

  • 通过反射机制调用构造方法创建对象
  • 获得类的字节码
代码语言:javascript复制
Class clazz = Class.forName("com.example.bean.HelloWorld");
  • 通过字节码实例化对象
代码语言:javascript复制
// Object obj  = clazz.newInstance();
Object object = clazz.getDeclaredConstructor().newInstance();

2.3.3 Spring把创建好的对象存储到Map集合

  • bean对象最终存储在spring容器中,在spring源码底层就是一个map集合,存储bean的map在DefaultListableBeanFactory类中
代码语言:javascript复制
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
beanDefinitionMap.put("hello",object)
  • Spring容器加载到Bean类时 , 会把这个类的描述信息, 以包名加类名的方式存到beanDefinitionMap 中, Map<String,BeanDefinition> , 其中 String是Key , 默认是类名首字母小写 , BeanDefinition , 存的是类的定义(描述信息) , 我们通常叫BeanDefinition接口为 : bean的定义对象。

2.3.4 工厂模式返回Bean对象 getBean方法

代码语言:javascript复制
public Object getBean(String name){
    Object obj =beanDefinitionMap.get(name);
    return obj;
}

3. IoC

3.1 控制反转

  • IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更高扩展力、更优良的程序。
  • Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。
  • IoC 容器是 Spring 框架中最重要的核心组件之一,它贯穿了 Spring 从诞生到成长的整个过程。
  • 控制反转:简单的说就是,创建对象的权利,或者是控制的位置,由JAVA代码转移到spring容器,由spring的容器控制对象的创建,就是控制反转,spring创建对象时,会读取配置文件中的信息,然后使用反射给我们创建好对象之后在容器中存储起来,当我们需要某个对象时,通过id获取对象即可,不需要我们自己去new
  • 一句话:创建对象交给容器
  • 控制反转,反转的是什么
    • 将对象的创建权利交出去,交给第三方容器负责。
    • 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
  • 控制反转这种思想如何实现呢?
    • DI(Dependency Injection):依赖注入

3.2 依赖注入

  • DI(Dependency Injection):依赖注入,依赖注入实现了控制反转的思想。
  • 依赖注入:指Spring创建对象的过程中,将对象依赖属性通过配置进行注入
  • 依赖注入常见的实现方式包括两种:
    • 第一种:set注入
    • 第二种:构造注入
  • 所以结论是:IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。
  • Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)

3.3 IoC容器在Spring的实现

  • Spring 的 IoC 容器就是 IoC思想的一个落地的产品实现。IoC容器中管理的组件也叫做 bean。在创建 bean 之前,首先需要创建IoC 容器。Spring 提供了IoC 容器的两种实现方式:
    • BeanFactory:这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。
    • ApplicationContext:BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。
  • ApplicationContext的主要实现类

类型名

简介

ClassPathXmlApplicationContext

通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象

FileSystemXmlApplicationContext

通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象

ConfigurableApplicationContext

ApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。

WebApplicationContext

专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。

4. 基于XML管理Bean

4.1 新建项目

4.1.1 新建项目spring-test

4.1.2 导入依赖

  • 导入spring-context的时候,会自动导入其他依赖的jar,自动进行了依赖传递。所以,导入一个spring-context 依赖也可以
  • 依赖关系传递图
  • 为了方便测试,我们导入Junit测试依赖
代码语言:javascript复制
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.26</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

4.2 获取bean

  • 新建模块:spring-test-xml-01
  • 项目结构
  • PersonInterface
代码语言:javascript复制
package com.example.bean;

public interface PersonInterface {
    void sayHello();
}
  • Person
代码语言:javascript复制
package com.example.bean.impl;

public class Person {
    public void sayHello(){
        System.out.println("hello, i'm person~");
    }
}
  • Student
代码语言:javascript复制
package com.example.bean.impl;

import com.example.bean.PersonInterface;

public class Student extends Person implements PersonInterface {
    @Override
    public void sayHello() {
        System.out.println("hello, i'm student~");
    }
}
  • Teacher
代码语言:javascript复制
package com.example.bean.impl;

import com.example.bean.PersonInterface;

public class Teacher extends Person implements PersonInterface {
    @Override
    public void sayHello() {
        System.out.println("hello, i'm teacher~");
    }
}
  • bean.xml
代码语言:javascript复制
<?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="student1" class="com.example.bean.impl.Student" name="myStudent"></bean>
    <bean id="student2" class="com.example.bean.impl.Student"></bean>
    <!-- name 和id类似,一般不用 -->
</beans>
  • GetBeanTest (重点都在这里了)
代码语言:javascript复制
import com.example.bean.PersonInterface;
import com.example.bean.impl.Person;
import com.example.bean.impl.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Arrays;


/**
 * 获取bean
 */

public class GetBeanTest {
    @Test
    public void testGetBean(){
        ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
        // 方式一:根据id获取
        // 由于 id 属性指定了 bean 的唯一标识,所以根据 bean 标签的 id 属性可以精确获取到一个组件对象
        Student s1 = (Student) app.getBean("student1");
        s1.sayHello();

        // 方式二:根据类型获取
        // 当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个
        // IOC容器中配置了多个同类型的bean时, 根据类型获取时会抛出异常
//        Student s2 = app.getBean(Student.class);
//        s2.sayHello();

        // 方式三:根据id和类型
        Student s3 = app.getBean("student1", Student.class);
        s3.sayHello();

        // 如果组件类实现了接口,可以根据接口类型获取bean, 前提是bean唯一
        // 同理:如果组件类继承了父类,可以根据父类类型获取bean, 前提是bean唯一
        // 如果一个接口有多个实现类,这些实现类都配置了 bean,根据接口类不可以可以获取bean, 因为bean不唯一
//        PersonInterface p1 = app.getBean(PersonInterface.class);
//        Person p2 = app.getBean(Person.class);
//        p1.sayHello();
//        p2.sayHello();

        // 结论
        // 根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。
        // java中,instanceof运算符用于判断前面的对象是否是后面的类,或其子类、实现类的实例。如果是返回true,否则返回false。
        // 也就是说:用instanceof关键字做判断时, instanceof 操作符的左右操作必须有继承或实现关系

        String[] personInterfaces = app.getBeanNamesForType(PersonInterface.class);
        System.out.println(Arrays.toString(personInterfaces)); // [student1, student2]
    }
}

4.3 依赖注入之setter注入

  • property标签:通过组件类的setXxx()方法给组件对象设置属性
  • name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关)
  • value属性:指定属性值

  • 新建模块:spring-test-xml-02
  • 项目结构
  • Student
代码语言:javascript复制
package com.example.bean;

public class Student {
    private String name;
    private int sid;
    private int grade;

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

    public void setSid(int sid) {
        this.sid = sid;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "Student{"  
                "name='"   name   '''  
                ", sid="   sid  
                ", grade="   grade  
                '}';
    }
}
  • bean.xml
代码语言:javascript复制
<?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">

    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <bean id="student" class="com.example.bean.Student">
        <property name="name" value="张三"></property>
        <property name="sid" value="10001"></property>
        <property name="grade" value="1"></property>
    </bean>
</beans>
  • SetBeanTest
代码语言:javascript复制
import com.example.bean.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


/**
 * 依赖注入之setter注入
 */

public class SetBeanTest {
    @Test
    public void testGetBean(){
        ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
        Student s = (Student) app.getBean("student");
        System.out.println(s);
    }
}

4.4 依赖注入之构造器注入

  • Student中添加无参和有参构造函数
代码语言:javascript复制
package com.example.bean;

public class Student {
    private String name;
    private int sid;
    private int grade;

    public Student(){
    }

    public Student(String name, int sid, int grade){
        this.name = name;
        this.sid = sid;
        this.grade = grade;
        System.out.println("有参构造函数执行了, name=" name ", sid="   sid   ", grade=" grade);
    }

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

    public void setSid(int sid) {
        this.sid = sid;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "Student{"  
                "name='"   name   '''  
                ", sid="   sid  
                ", grade="   grade  
                '}';
    }
}
  • bean.xml
代码语言:javascript复制
<?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">

    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <bean id="student" class="com.example.bean.Student">
        <property name="name" value="张三"></property>
        <property name="sid" value="10001"></property>
        <property name="grade" value="1"></property>
    </bean>

    <!-- constructor-arg标签还有两个属性可以进一步描述构造器参数 -->
    <!-- index属性:指定参数所在位置的索引(从0开始)-->
    <!-- name属性:指定参数名 -->
    <bean id="student2" class="com.example.bean.Student">
        <constructor-arg name="name" value="李四"></constructor-arg>
        <constructor-arg name="sid" value="10002"></constructor-arg>
        <constructor-arg name="grade" value="1"></constructor-arg>
    </bean>
</beans>
  • SetBeanTest
代码语言:javascript复制
import com.example.bean.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


/**
 * 依赖注入之setter注入
 * 依赖注入之构造器注入
 */

public class SetBeanTest {
    @Test
    public void testGetBean(){
        ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
        // 调用无参构造
        Student s = (Student) app.getBean("student");
        System.out.println(s);
        // 调用有参构造
        Student s2 = (Student) app.getBean("student2");
        System.out.println(s2);
    }
}

4.5 特殊值处理

4.5.1 字面量赋值

  • 字面量
    • int a = 10; a是一个变量
    • 带引号的'a':代表a这个字母本身,是一个字面量。所以字面量没有引申含义,就是我们看到的这个数据本身。
代码语言:javascript复制
<!-- 使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 -->
<property name="name" value="张三"/>

4.5.2 null值

代码语言:javascript复制
<property name="grade">
    <null />
</property>
  • 以上写法,为grade所赋的值是字符串null

4.5.3 特殊符号

大于号、小于号在XML文档中用来定义标签的开始,不能随便使用

  • 解决方案一:使用XML实体来代替

&lt;

<

小于号

&gt;

>

大于号

&amp;

&

&apos;

单引号

&quot;

"

双引号

  • 解决方案二:CDATA节
    • CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据
    • XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析
    • 所以CDATA节中写什么符号都随意

4.5.4 实现

  • Student(sid、grade类型改为Integer,如果不改,把值设置为null时会报错)
代码语言:javascript复制
package com.example.bean;

public class Student {
    private String name;
    private Integer sid;
    private Integer grade;

    public Student(){
    }

    public Student(String name, Integer sid, Integer grade){
        this.name = name;
        this.sid = sid;
        this.grade = grade;
        System.out.println("有参构造函数执行了, name=" name ", sid="   sid   ", grade=" grade);
    }

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

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public void setGrade(Integer grade) {
        this.grade = grade;
    }

    @Override
    public String toString() {
        return "Student{"  
                "name='"   name   '''  
                ", sid="   sid  
                ", grade="   grade  
                '}';
    }
}
  • bean.xml
代码语言:javascript复制
<?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">

    <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
    <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
    <!-- value属性:指定属性值 -->
    <bean id="student" class="com.example.bean.Student">
        <property name="name" value="张三"></property>
        <property name="sid" value="10001"></property>
        <property name="grade" value="1"></property>
    </bean>

    <!-- constructor-arg标签还有两个属性可以进一步描述构造器参数 -->
    <!-- index属性:指定参数所在位置的索引(从0开始)-->
    <!-- name属性:指定参数名 -->
    <bean id="student2" class="com.example.bean.Student">
        <constructor-arg name="name" value="李四"></constructor-arg>
        <constructor-arg name="sid" value="10002"></constructor-arg>
        <constructor-arg name="grade" value="2"></constructor-arg>
    </bean>

    <!-- 属性值设置为null -->
    <bean id="student3" class="com.example.bean.Student">
        <property name="name" value="王五"></property>
        <property name="sid" value="10003"></property>
        <property name="grade">
            <null/>
        </property>
     </bean>

    <!-- 属性值设置为特殊字符 xml实体 -->
    <bean id="student4" class="com.example.bean.Student">
        <property name="name" value="&quot;&lt;&apos;赵&amp;六&apos;&gt;&quot;"></property>
        <property name="sid" value="10004"></property>
        <property name="grade" value="4"></property>
    </bean>

    <!-- 属性值设置为特殊字符 CDATA -->
    <bean id="student5" class="com.example.bean.Student">
        <property name="name">
            <value><![CDATA['<爱丽丝>']]></value>
        </property>
        <property name="sid" value="10005"></property>
        <property name="grade" value="5"></property>
    </bean>

</beans>
  • SetBeanTest
代码语言:javascript复制
import com.example.bean.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


/**
 * 依赖注入之setter注入
 * 依赖注入之构造器注入
 * 特殊值处理
 */

public class SetBeanTest {
    @Test
    public void testGetBean(){
        ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
        // 调用无参构造
        Student s = (Student) app.getBean("student");
        System.out.println(s);
        // 调用有参构造
        Student s2 = (Student) app.getBean("student2");
        System.out.println(s2);
        // 属性设置为null
        Student s3 = (Student) app.getBean("student3");
        System.out.println(s3);
        // 属性设置为特殊字符-xml
        Student s4 = (Student) app.getBean("student4");
        System.out.println(s4);
        // 属性设置为特殊字符-CDATA
        Student s5 = (Student) app.getBean("student5");
        System.out.println(s5);
    }
}

4.6 为对象类型属性赋值

  • 方法一:引用外部bean
    • ref属性:引用IOC容器中某个bean的id,将所对应的bean为属性赋值
  • 方法二:内部bean
    • 在一个bean中再声明一个bean就是内部bean
    • 内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性
  • 方式三:级联属性赋值
  • 新建模块
  • Student
代码语言:javascript复制
package com.example.bean;

public class Student {
    private String name;
    private Integer sid;
    private Integer grade;

    private Clazz clazz;

    public Student(){
    }

    public Student(String name, Integer sid, Integer grade){
        this.name = name;
        this.sid = sid;
        this.grade = grade;
        System.out.println("有参构造函数执行了, name=" name ", sid="   sid   ", grade=" grade);
    }

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

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public void setGrade(Integer grade) {
        this.grade = grade;
    }

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }

    public String getName() {
        return name;
    }

    public Integer getSid() {
        return sid;
    }

    public Integer getGrade() {
        return grade;
    }

    public Clazz getClazz() {
        return clazz;
    }

    @Override
    public String toString() {
        return "Student{"  
                "name='"   name   '''  
                ", sid="   sid  
                ", grade="   grade  
                ", clazz="   clazz  
                '}';
    }
}
  • Clazz
代码语言:javascript复制
package com.example.bean;

public class Clazz {
    private Integer clazzId;

    private String clazzName;

    public Integer getClazzId() {
        return clazzId;
    }

    public void setClazzId(Integer clazzId) {
        this.clazzId = clazzId;
    }

    public String getClazzName() {
        return clazzName;
    }

    public void setClazzName(String clazzName) {
        this.clazzName = clazzName;
    }

    @Override
    public String toString() {
        return "Clazz{"  
                "clazzId="   clazzId  
                ", clazzName='"   clazzName   '''  
                '}';
    }

    public Clazz() {
    }

    public Clazz(Integer clazzId, String clazzName) {
        this.clazzId = clazzId;
        this.clazzName = clazzName;
    }
}
  • bean.xml
代码语言:javascript复制
<?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="clazz" class="com.example.bean.Clazz">
        <property name="clazzId" value="1111"></property>
        <property name="clazzName" value="财源滚滚班"></property>
    </bean>

    <bean id="student" class="com.example.bean.Student">
        <property name="name" value="张三"></property>
        <property name="sid" value="10001"></property>
        <property name="grade" value="1"></property>
        <!-- ref属性:引用IOC容器中某个bean的id,将所对应的bean为属性赋值 -->
        <property name="clazz" ref="clazz"></property>
    </bean>

    <!--
    如果错把ref属性写成了value属性,会抛出异常
    Caused by: java.lang.IllegalStateException: Cannot convert value of type
    'java.lang.String' to required type 'com.example.bean.Clazz' for property 'clazz': no matching editors or conversion strategy found
    意思是不能把String类型转换成我们要的Clazz类型,说明我们使用value属性时,Spring只把这个属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到bean来赋值
    -->


    <!-- 方式二:内部bean -->
    <bean id="student2" class="com.example.bean.Student">
        <property name="sid" value="10002"></property>
        <property name="name" value="李四"></property>
        <property name="grade" value="2"></property>
        <property name="clazz">
            <!-- 在一个bean中再声明一个bean就是内部bean -->
            <!-- 内部bean只能用于给属性赋值,不能在外部通过IOC容器获取,因此可以省略id属性 -->
            <bean id="clazzInner" class="com.example.bean.Clazz">
                <property name="clazzId" value="2222"></property>
                <property name="clazzName" value="远大前程班"></property>
            </bean>
        </property>
    </bean>

    <!-- 方式三:级联属性赋值 -->
    <bean id="student3" class="com.example.bean.Student">
        <property name="sid" value="10003"></property>
        <property name="name" value="王五"></property>
        <property name="grade" value="3"></property>
        <property name="clazz" ref="clazz"></property>
        <!-- 级联属性赋值 有一个注意点,该方式必须有get方法,否则会报属性找不到 -->
        <property name="clazz.clazzId" value="3333"></property>
        <property name="clazz.clazzName" value="最强王者班"></property>
    </bean>

</beans
  • SetBeanObjectTest
代码语言:javascript复制
import com.example.bean.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


/**
 * 为对象类型属性赋值
 */

public class SetBeanObjectTest {
    @Test
    public void testGetBean(){
        ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
        Student s = (Student) app.getBean("student");
        System.out.println(s);

        Student s2 = app.getBean("student2",Student.class);
        System.out.println(s2);

        Student s3 = app.getBean("student3",Student.class); // 同一个对象 s.clazz对象的属性值被修改
        System.out.println(s3);
    }
}

4.7 为集合类型属性赋值

  • 为集合、List集合、set集合类型属性赋值
  • 为Map集合类型属性赋值
  • 引用集合类型的bean
    • 使用util:list、util:map标签必须引入相应的命名空间
代码语言:javascript复制
<?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:util="http://www.springframework.org/schema/util"
       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">
</beans>
  • Student
代码语言:javascript复制
package com.example.bean;

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

public class Student {
    private String name;
    private Integer sid;
    private Integer grade;
    private Clazz clazz;
    private String[] hobbies;
    private List<Clazz> hobbies2;
    private Set<String> hobbies3;
    private Map<String, Clazz> clazzMap;
    private Properties properties;

    public String[] getHobbies() {
        return hobbies;
    }

    public void setHobbies(String[] hobbies) {
        this.hobbies = hobbies;
    }

    public List<Clazz> getHobbies2() {
        return hobbies2;
    }

    public void setHobbies2(List<Clazz> hobbies2) {
        this.hobbies2 = hobbies2;
    }

    public Set<String> getHobbies3() {
        return hobbies3;
    }

    public void setHobbies3(Set<String> hobbies3) {
        this.hobbies3 = hobbies3;
    }

    public Map<String, Clazz> getClazzMap() {
        return clazzMap;
    }

    public void setClazzMap(Map<String, Clazz> clazzMap) {
        this.clazzMap = clazzMap;
    }
    
    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public Student(){
    }

    public Student(String name, Integer sid, Integer grade){
        this.name = name;
        this.sid = sid;
        this.grade = grade;
        System.out.println("有参构造函数执行了, name=" name ", sid="   sid   ", grade=" grade);
    }

    public String getName() {
        return name;
    }

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

    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public Integer getGrade() {
        return grade;
    }

    public void setGrade(Integer grade) {
        this.grade = grade;
    }

    public Clazz getClazz() {
        return clazz;
    }

    public void setClazz(Clazz clazz) {
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "Student{"  
                "name='"   name   '''  
                ", sid="   sid  
                ", grade="   grade  
                ", clazz="   clazz  
                ", hobbies="   Arrays.toString(hobbies)  
                ", hobbies2="   hobbies2  
                ", hobbies3="   hobbies3  
                ", clazzMap="   clazzMap  
                ", properties="   properties  
                '}';
    }
}
  • utilBean.xml
代码语言:javascript复制
<?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:util="http://www.springframework.org/schema/util"
       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 Set Properties类型属性赋值 -->
    <bean id="student1" class="com.example.bean.Student">
        <property name="name" value="张三"></property>
        <property name="hobbies">
            <array>
                <value>抽烟</value>
                <value>喝酒</value>
                <value>烫头</value>
            </array>
        </property>

        <property name="hobbies2">
            <list>
                <ref bean="clazz1"></ref>
                <ref bean="clazz2"></ref>
            </list>
        </property>

        <property name="hobbies3">
            <set>
                <value>抽烟3</value>
                <value>喝酒3</value>
                <value>烫头3</value>
            </set>
        </property>
        
        <property name="properties">
            <props>
                <prop key="1">ABC</prop>
                <prop key="2">DEF</prop>
            </props>
        </property>
    </bean>

    <!-- --------------------------------------------------------------------------- -->
    <!-- 为Map集合类型属性赋值 -->
    <bean id="clazz1" class="com.example.bean.Clazz">
        <property name="clazzId" value="10001"></property>
        <property name="clazzName" value="java"></property>
    </bean>
    <bean id="clazz2" class="com.example.bean.Clazz">
        <property name="clazzId" value="10002"></property>
        <property name="clazzName" value="python"></property>
    </bean>

    <bean id="student2" class="com.example.bean.Student">
        <property name="name" value="李四"></property>
        <property name="clazzMap">
            <map>
                <entry>
                    <key><value>c1</value></key>
                    <ref bean="clazz1"></ref>
                </entry>
                <entry>
                    <key><value>c2</value></key>
                    <ref bean="clazz2"></ref>
                </entry>
            </map>
        </property>
    </bean>

    <!-- --------------------------------------------------------------------------- -->
    <!-- util -->
    <util:list id="clazzs">
        <ref bean="clazz1"></ref>
        <ref bean="clazz2"></ref>
    </util:list>
    <util:map id="clazzMap">
        <entry>
            <key><value>10010</value></key>
            <ref bean="clazz1"></ref>
        </entry>
        <entry>
            <key><value>10086</value></key>
            <ref bean="clazz2"></ref>
        </entry>
    </util:map>

    <bean id="student3" class="com.example.bean.Student">
        <property name="name" value="王五"></property>
        <property name="hobbies2" ref="clazzs"></property>
        <property name="clazzMap" ref="clazzMap"></property>
    </bean>

</beans>
  • SetBeanUtilTest
代码语言:javascript复制
import com.example.bean.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


/**
 * 为对象类型属性赋值
 */

public class SetBeanUtilTest {
    @Test
    public void testGetBean(){
        ApplicationContext app = new ClassPathXmlApplicationContext("utilBean.xml");
        // 集合 list set
        Student s = (Student) app.getBean("student1");
        System.out.println(s);
        // map
        Student s2 = app.getBean("student2",Student.class);
        System.out.println(s2);
        // util
        Student s3 = app.getBean("student3",Student.class);
        System.out.println(s3);
    }
}

4.8 p命名空间、c命名空间

4.8.1 p命名空间

  • 新增Person、pBean.xml、PBeanTest文件
  • Person
代码语言:javascript复制
package com.example.bean;

public class Person {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{"  
                "name='"   name   '''  
                ", age="   age  
                '}';
    }
}
  • pBean.xml(引入p命名空间)
代码语言:javascript复制
<?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="person" class="com.example.bean.Person" p:name="张三" p:age="18"></bean>
</beans>
  • PBeanTest
代码语言:javascript复制
import com.example.bean.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class PBeanTest {
    @Test
    public void test(){
        ApplicationContext app = new ClassPathXmlApplicationContext("pBean.xml");
        Person person = app.getBean(Person.class);
        System.out.println(person); // Person{name='张三', age=18}
    }
}

4.8.2 c命名空间

  • person增加构造函数
代码语言:javascript复制
public Person() {
}

public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
}
  • pBean.xml配置
代码语言:javascript复制
<!-- 引入c命名空间 -->
xmlns:c="http://www.springframework.org/schema/c"

<!--c名称空间,就是对constructor-arg的简化-->
<bean id="person2" class="com.example.bean.Person" c:name="李四" c:age="20" ></bean>
  • 测试
代码语言:javascript复制
import com.example.bean.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class PBeanTest {
    @Test
    public void test(){
        ApplicationContext app = new ClassPathXmlApplicationContext("pBean.xml");
        Person person = app.getBean("person", Person.class);
        System.out.println(person);

        Person person2 = app.getBean("person2", Person.class);
        System.out.println(person2);
    }
}

4.9 引入外部属性文件

context:property-placeholder

4.9.1 加载配置文件

xml文件中配置

  • 方法一:<context:property-placeholder location="jdbc.properties,jdbc.properties"/>
    • 如果配置文件多的话,每个都需要配置
  • 方法二:<context:property-placeholder location="*.properties"/>
    • *.properties代表所有以properties结尾的文件都会被加载,可以解决方法一的问题,但是不标准
  • 方法三:<context:property-placeholder location="classpath:*.properties" />
    • 标准的写法,classpath:代表的是从根路径下开始查找,但是只能查询当前项目的根路径
  • 方法四:<context:property-placeholder location="classpath*:*.properties" />
    • 不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的properties配置文件

不加载系统属性:system-properties-mode="NEVER"

<context:property-placeholder location="classpath:jdbc.properties" system-properties-mode="NEVER"/>

4.9.2 实现

  • 加入依赖
代码语言:javascript复制
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.16</version>
</dependency>

<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>
  • 创建外部属性文件:jdbc.properties
代码语言:javascript复制
jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc.driver=com.mysql.cj.jdbc.Driver
  • 配置jdbcBean.xml
代码语言:javascript复制
<!-- 引入context 名称空间 -->
<?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">
    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!-- 配置bean -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="driverClass" value="${jdbc.driver}"></property>
    </bean>

</beans>

注意:在使用context:property-placeholder元素加载外包配置文件功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束。

  • 测试
代码语言:javascript复制
import com.alibaba.druid.pool.DruidDataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.ResourceBundle;

public class JdbcTest {
    @Test
    public void test() throws SQLException, PropertyVetoException {
        // TODO 系统属性
        System.out.println("系统属性");
        Map<String, String> env = System.getenv();
        System.out.println(env);
        System.out.println("----------------------");

        // TODO 读取配置文件
        ResourceBundle rb = ResourceBundle.getBundle("jdbc");
        String driver = rb.getString("jdbc.driver");
        String url = rb.getString("jdbc.url");
        String userName = rb.getString("jdbc.user");
        String password = rb.getString("jdbc.password");

        // 创建数据源对象 设置连接参数
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(userName);
        dataSource.setPassword(password);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
        System.out.println("-------------------------------");

        // TODO xml文件
        ApplicationContext app = new ClassPathXmlApplicationContext("jdbcBean.xml");
        // DruidDataSource
        DruidDataSource druidDataSource = app.getBean(DruidDataSource.class);
        Connection connection2 = dataSource.getConnection();
        System.out.println(connection2);
        connection2.close();
        System.out.println("-----------------------------------");
        // c3p0
        ComboPooledDataSource comboPooledDataSource = app.getBean(ComboPooledDataSource.class);
        Connection connection3 = comboPooledDataSource.getConnection();
        System.out.println(connection3);
        connection3.close();

        // TODO 手动创建c3p0数据源
        ComboPooledDataSource comboPooledDataSource1 = new ComboPooledDataSource();
        comboPooledDataSource1.setDriverClass("com.mysql.jdbc.Driver");
        comboPooledDataSource1.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai");
        comboPooledDataSource1.setUser("root");
        comboPooledDataSource1.setPassword("123456");
        Connection connection4 = comboPooledDataSource1.getConnection();
        System.out.println(connection4);
        connection4.close();
        System.out.println("-----------------------------------");

        // TODO 手动创建druid数据源
        DruidDataSource druidDataSource1 = new DruidDataSource();
        druidDataSource1.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource1.setUrl("jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai");
        druidDataSource1.setUsername("root");
        druidDataSource1.setPassword("123456");
        Connection connection5 = druidDataSource1.getConnection();
        System.out.println(connection5);
        connection5.close();
    }
}

4.10 bean的作用域

4.10.1 bean的作用域

  • 在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参加下表:

取值

含义

创建对象的时机

singleton(默认)

在IOC容器中,这个bean的对象始终为单实例

IOC容器初始化时

prototype

这个bean在IOC容器中有多个实例

获取bean时

  • 如果是在WebApplicationContext环境下还会有另外几个作用域(但不常用):

取值

含义

request

在一个请求范围内有效

session

在一个会话范围内有效

  • scope属性
    • 取值singleton(默认值),bean在IOC容器中只有一个实例,IOC容器初始化时创建对象
    • 取值prototype,bean在IOC容器中可以有多个实例,getBean()时创建对象

4.10.2 实现

  • Student
代码语言:javascript复制
package com.example.bean;

public class Student {
    private String name;

    public Student() {
        System.out.println("Student无参构造被执行了....");
    }

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student{"  
                "name='"   name   '''  
                '}';
    }
}
  • Teacher
代码语言:javascript复制
package com.example.bean;

public class Teacher {
    private String name;

    public Teacher() {
        System.out.println("Teacher无参构造被执行了....");
    }

    public Teacher(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Teacher{"  
                "name='"   name   '''  
                '}';
    }
}
  • scopeBean.xml
代码语言:javascript复制
<?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="teacher" class="com.example.bean.Teacher" scope="singleton"></bean>
    <bean id="student" class="com.example.bean.Student" scope="prototype"></bean>
</beans>
  • ScopeTest
代码语言:javascript复制
import com.example.bean.Student;
import com.example.bean.Teacher;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ScopeTest {

    @Test
    public void test(){
        ApplicationContext app = new ClassPathXmlApplicationContext("scopeBean.xml");
        System.out.println("------------------");

        Teacher teacher = app.getBean("teacher", Teacher.class);
        System.out.println(teacher);
        System.out.println("------------------");

        Student student = app.getBean("student", Student.class);
        System.out.println(student);
    }
}
  • 测试结果
代码语言:javascript复制
Teacher无参构造被执行了.... // 单例模式,bean在IOC容器中只有一个实例,IOC容器初始化时创建对象
------------------
Teacher{name='null'}
------------------
Student无参构造被执行了.... // bean在IOC容器中可以有多个实例,getBean()时创建对象
Student{name='null'}

4.11 bean生命周期

4.11.1 bean生命周期

  • bean对象创建(调用无参构造器)
  • 给bean对象设置属性
  • bean的后置处理器(初始化之前)
  • bean对象初始化(需在配置bean时指定初始化方法)
  • bean的后置处理器(初始化之后)
  • bean对象就绪可以使用
  • bean对象销毁(需在配置bean时指定销毁方法)
  • IOC容器关闭

4.11.2 bean的后置处理器

bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行

4.11.3 实现

  • 结构
  • Person
代码语言:javascript复制
package com.example.bean;
public class Person {

    private Integer id;

    private String username;

    public Person() {
        System.out.println("生命周期Person:1、创建对象");
    }

    public Person(Integer id, String username) {
        this.id = id;
        this.username = username;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        System.out.println("生命周期Person:2、依赖注入");
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void initMethod(){
        System.out.println("生命周期Person:4、初始化");
    }

    public void destroyMethod(){
        System.out.println("生命周期Person:7、销毁");
    }

    @Override
    public String toString() {
        return "Person{"  
                "id="   id  
                ", username='"   username  
                '}';
    }
}
  • User
代码语言:javascript复制
package com.example.bean;
public class User {

    private Integer id;

    private String username;

    public User() {
        System.out.println("生命周期:1、创建对象");
    }

    public User(Integer id, String username) {
        this.id = id;
        this.username = username;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        System.out.println("生命周期:2、依赖注入");
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void initMethod(){
        System.out.println("生命周期:4、初始化");
    }

    public void destroyMethod(){
        System.out.println("生命周期:7、销毁");
    }

    @Override
    public String toString() {
        return "User{"  
                "id="   id  
                ", username='"   username  
                '}';
    }
}
  • MyBeanProcessor
代码语言:javascript复制
package com.example.process;

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

public class MyBeanProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("生命周期:3、bean的后置处理器(初始化之前)"   beanName   " = "   bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("生命周期:5、bean的后置处理器(初始化之后)"   beanName   " = "   bean);
        return bean;
    }
}
  • bean.xml
代码语言:javascript复制
<?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属性指定初始化方法 -->
    <!-- 使用destroy-method属性指定销毁方法 -->
    <bean class="com.example.bean.User" scope="singleton" init-method="initMethod" destroy-method="destroyMethod">
        <property name="id" value="1001"></property>
        <property name="username" value="admin"></property>
    </bean>
    
    
    <bean class="com.example.bean.Person" scope="prototype" init-method="initMethod" destroy-method="destroyMethod">
        <property name="id" value="10011"></property>
        <property name="username" value="张三"></property>
    </bean>

    <!-- bean的后置处理器要放入IOC容器才能生效 -->
    <bean id="myBeanProcessor" class="com.example.process.MyBeanProcessor"/>
</beans>
  • BeanLifeTest
代码语言:javascript复制
import com.example.bean.Person;
import com.example.bean.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeanLifeTest {
    @Test
    public void testLife(){
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        System.out.println("---------------------");
        User bean = ac.getBean(User.class);
        System.out.println("生命周期:6、通过IOC容器获取bean并使用");

        System.out.println("---------------------");
        Person person = ac.getBean(Person.class);
        System.out.println("生命周期Person:6、通过IOC容器获取bean并使用");

        System.out.println("---------------------");
        ac.close();
    }
}
  • 结果
代码语言:javascript复制
生命周期:1、创建对象
生命周期:2、依赖注入
生命周期:3、bean的后置处理器(初始化之前)com.example.bean.User#0 = User{id=1001, username='admin}
生命周期:4、初始化
生命周期:5、bean的后置处理器(初始化之后)com.example.bean.User#0 = User{id=1001, username='admin}
---------------------
生命周期:6、通过IOC容器获取bean并使用
---------------------
生命周期Person:1、创建对象
生命周期Person:2、依赖注入
生命周期:3、bean的后置处理器(初始化之前)com.example.bean.Person#0 = Person{id=10011, username='张三}
生命周期Person:4、初始化
生命周期:5、bean的后置处理器(初始化之后)com.example.bean.Person#0 = Person{id=10011, username='张三}
生命周期Person:6、通过IOC容器获取bean并使用
---------------------
生命周期:7、销毁

4.12 import resource标签

  • 引入其他xml文件
代码语言:javascript复制
<!-- import -->
<import resource="bean.xml"/>

0 人点赞