Spring基础教程

文章目录


一、概述

Spring官网:https://spring.io/

(一)Spring家族体系

  Spring框架自2003年由Rod Johnson设计并实现以来,经历了多个重大版本的发展和演进,已经形成了一个庞大的家族式技术生态圈。目前,Spring已经是Java EE领域最流行的开发框架,在全球各大企业中都得到了广泛应用。

①简介

  Spring是一个生态体系(也可以说是技术体系),由许多开源框架和组件组成,是一个统称,其中包含了Spring FrameworkSpring BootSpring Cloud等等。
七大核心技术体系
在这里插入图片描述
分别是微服务架构、响应式编程、云原生、Web 应用、Serverless架构、事件驱动以及批处理。
  这些技术体系各自独立但也有一定交集,例如,微服务架构往往会与基于Spring Cloud的云原生技术结合在一起使用,而微服务架构的构建过程也需要依赖于能够提供RESTful风格的Web应用程序等。

②Spring与SpringFramework

  Spring是一个生态体系,其中包含了Spring FrameworkSpring BootSpring Cloud等等,而Spring Framework是整个spring生态的基石,是一个一站式的轻量级的java开发框架,核心技术为:依赖注入、事务管理、WEB应用、数据访问等。
  SpringSpringFramework并非同一概念,前者是一种生态体系,而后者是该生态体系的基石框架,我们一般常说的Spring框架指的就是Spring Framework,故而,本文对Spring的介绍实质上就是SpringFramework的基本使用。

(二)SpringFramework

  早在2002年的11月份,Rod Johnson发表了一本名为《Expert One-on-One J2EE Design and Development》的书。这本书中就包含了Spring框架代码,最初这个框架叫做Interface 21 framework,后来Yann提议命名为Spring(春天),寓意 :Spring代表了一个新的开始,结束了传统J2EE开发的寒冬。
  所有我们现在能看到的Spring家族技术体系都是在Spring Framework基础上逐步演进而来的,SpringFrameworkSpringSpringMVCSpringBootSpringCloudSpringDataSpringSecurity等的基础框架。

①整体架构

在这里插入图片描述
组成Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

功能模块功能介绍
WebJavaWeb模块,提供了相关的Web功能。
Core Container核心容器,包含了spring-core,spring-beans,spring-context,spring-context-support,spring-expression这五个模块,是其他模块建立的基础。
AOP提供了面向切面编程,提供了比如日志记录、权限控制、性能统计等通用功能和业务逻辑分离的技术,并且能动态把这些功能添加到需要的代码中。
Aspects提供与AspectJ的集成,是一个功能强大且成熟的面向切面编程框架。
InStrumentation提供了类工具的支持和类加载器的实现。
Messaging为集成messaging api和消息协议提供支持。同样也提供了一些映射消息到方法的注解,类似spring mvc注解。
Testingspring-test模块使用JUnit或者TestNG为Spring各个组件提供单元测试与集成测试。
Data Access/Integration提供了对数据访问/集成功能的支持,包含JDBC, ORM,OXM, JMS 和 Transaction 模块。
SpringMVC提供了面向Web应用程序的集成功能。
  • Data Access/Integration:提供数据访问/集成等功能。
    • JDBC:提供了一个JDBC样例模板,消除冗长JDBC编码和必须的事务控制。
    • ORM:提供了与"对象-关系"映射(在本专栏MyBatis一文中提过)框架集成的API,包括JPAJDOHibernateMyBatis等。
    • OXM:提供了Object/XML映射的抽象层实现,如JAXBCastorXMLBeanJiBXXStream。将java对象映射成为XML数据,或者将XML数据映射成java对象。
    • JMSjava消息服务,提供了一套“消息生产者、消息消费者”模板,JMS用于两个应用程序之间,或者分布式系统中发送消息,进行异步通信。
    • Transaction:支持编程和声明式事务管理。
  • Web:包含Web应用开发时,用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类、StrutsJSF集成类、文件上传的支持类、Filter类和大量工具辅助类。
    • Web:提供了基本web集成特性,例如:多文件上传、使用Servlet监听器的IoC容器初始化以及web应用上下文。
    • servlet:提供了SpringMVC Web框架实现,SpringMVC提供了基于注解的请求资源注入、更简单的数据绑定、数据验证等以及一套非常简易的JSP标签。
    • WebSocket:提供了简单的接口,用户只需要实现接口就可以快速搭建websocket server,从而实现双向通讯。
    • Portlet:提供了Portlet环境中MVC实现。
  • Core ContainerSpringFramework核心容器,是SpringFramework框架的基础,所有模块都构建于核心模块之上。
    • Beans模块:提供了框架的基础部分,包含访问配置文件、创建、管理bean以及进行控制反转和依赖注入等相关操作的支持。
    • Core核心模块Spring的其他组件要都要使用到这个包里的类,是其他组件的基本核心,封装了Spring框架的底层部分,包括资源访问、类型转换及一些常用工具类。
    • Context上下文模块:建立在CoreBeans模块的基础之上,提供了一种类似于JNDI注册器的框架式的对象访问方法,通过上下文可以获得容器中的Bean ApplicationContext接口是Context模块的关键。
    • SpEL(表达式语言模块):提供了强大的表达式语言支持,用于在运行时查询和操纵对象,支持访问和修改属性值,方法调用,等功能。

②核心jar包

  Spring4中官方给出的结构图:
在这里插入图片描述
图中将Spring分为了五个部分,分别是coreaopdata accesswebtest,图中每个圆角矩形都对应一个jar,如果在maven中配置,所有这些jar的groupId都是org.springframework,每个jar有一个不同的artifactId,另外,instrumentation有两个jar,还有一个spring-context-support图中没有列出,所以spring4的jar包一共是20个。
  下面介绍这五个部分的jar包之间的依赖关系(groupId都是springframework,以下均为artifactId),使用的是Maven工具和Maven Helper插件。

1.Data Access

  Data Access(数据访问)部分包含5个模块:

  1. spring-jdbcjdbc支持。
  2. spring-tx:事务控制。
  3. spring-orm:对象关系映射,集成orm框架。
  4. spring-oxm:对象XML映射。
  5. spring-jmsjava消息服务。
所含jar包
spring-jdbcspring-beans——>spring-core
spring-core——>spring-jcl
spring-tx
spring-txspring-core
spring-beans
spring-ormspring-core
spring-beans
spring-jdbc
spring-tx
spring-oxmspring-core
spring-beans
spring-jmsspring-core
spring-beans
spring-messaging——>spring-beans
spring-messaging——>spring-core
spring-tx

在这里插入图片描述

2.Web
  1. spring-web:基础web功能,如文件上传。
  2. spring-webmvcmvc实现。
  3. spring-webmvc-portlet:基于portlet的mvc实现。
  4. spring-websocket:为web应用提供的高效通信工具。
  5. spring-messaging:用于构建基于消息的应用程序。
所含jar包
spring-webmicrometer-obsercation——>micrometer-commons
spring-beans——>spring-core
spring-core——>spring-jcl
spring-webmvcspring-beans
spring-aop——>spring-beans
spring-aop——>spring-core
spring-context——>spring-aop
spring-context——>spring-beans
spring-context——>spring-core
spring-core
spring-web
spring-expression——>spring-core
spring-websocketspring-context
spring-core
spring-web
spring-webmvc-portletspring-beans
spring-context
spring-core
spring-web
spring-webmvc

在这里插入图片描述

3.Aop
  1. spring-aop:面向切面编程。
  2. spring-aspects:集成AspectJ
  3. spring-instrument:提供一些类级的工具支持和ClassLoader级的实现,用于服务器。
  4. spring-instrument-tomcat:针对Tomcatinstrument实现
所含jar包
spring-aopspring-core——>spring-jcl
spring-beans——>spring-core
spring-aspectsaspectjweaver
spring-instrument
spring-instrument-tomcat

在这里插入图片描述

4.Test
  1. spring-testspring测试,提供junitmock测试功能。
所含jar包
spring-testspring-core——>spring-jcl
5.core
  1. spring-core:包含Spring框架基本的核心工具类,Spring其他组件都要用到这个包中的类,依赖注入IoCDI的最基本实现。

  2. spring-beans:所有应用都要用到的,它包含访问配置文件、创建和管理Bean以及进行 Inversion of Control(IoC)或者Dependency Injection(DI)操作相关的所有类。

  3. spring-context:Spring提供在基础IoC功能上的扩展服务,此外还提供许多企业级服务的支持,如邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存以及各种视图层框架的封装等。

  4. spring-expression:定义了Spring的表达式语言。需要注意的是,在使用Spring开发时,除了Spring自带的JAR包以外,还需要一个第三方JARcommons.logging处理日志信息。

所含jar包
spring-corespring-jcl
spring-beansspring-core
spring-contextspring-aop——>spring-beans
spring-aop——>spring-core
spring-beans
spring-core
spring-expressions
spring-expressionsspring-core

③SpringFramework特性

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

④简单的SpringFramework程序

  要想使用SpringFramework中的IOCAOP功能,只需要使用core部分的四个jar包即可。
pom.xml

<?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>org.example</groupId>
    <artifactId>SpringDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>19</maven.compiler.source>
        <maven.compiler.target>19</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>6.0.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>6.0.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>6.0.4</version>
        </dependency>
    </dependencies>

</project>

Beans.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 id="HelloWorld" class="com.example.HelloWorld">
        <property name="message" value="Hello World!"/>
    </bean>
</beans>

HelloWorld.java

package com.example;

public class HelloWorld {
    private String message;
    public void setMessage(String message) {
        this.message = message;
    }
    public void getMessage(){
        System.out.println("message : "+message);
    }
}

MyTest.java

import com.example.HelloWorld;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context=new ClassPathXmlApplicationContext("Beans.xml");
        HelloWorld helloWorld=(HelloWorld) context.getBean("HelloWorld");
        helloWorld.getMessage();
    }
}

在这里插入图片描述

二、IOC模块

(一)三层架构思想

  三层架构源于后端开发的一种分层思想,通常意义上的三层架构将整个业务应用划分为:界面层(User Interface Layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data Access Layer)。
  分层的目的就是为了"高内聚,低耦合"的思想。

  • 表示层:又称控制层(Controller),主要用于与前端进行交互,接受用户的请求,以及数据的返回,为客户端提供应用程序的访问。
  • 业务逻辑层:又称服务层(Service),主要负责对数据层的操作。也就是说把一些数据层的操作进行组合。
  • 数据访问层:又称持久化层(DaorepositoryMapper),用于与数据库进行交互,执行SQL语句。

在后端代码中,除了三层架构对应的类与包外,还包括但不限于以下部分:

  • properties/yml文件:全局变量一般以资源文件的形式存放在resource目录下,文件名一般为application.properties或者application.yml。如,包含数据库连接信息的jdbc.properties配置文件。
  • config:项目设置类,用于指定当前SpringBoot项目的配置。
  • common:基础信息类,规定API返回的基础信息,如规定返回格式为{操作时间,错误信息,数据对象}
  • entity:实体类,对应数据库中的表,便于读取和写入,一般地,数据库中的一张表对应一个实体类,一个列名(字段)对应实体类的一个属性,一行对应该类的一个对象。
  • dto:数据传输类,是为了迎合业务需求而创建的类,实体类用于与数据库交互,而dto用于作为参数在不同层级间传输,或者传输到前端, dto`中的属性可能由多个表列名(字段)对应。
  • util:工具类,由程序员编写,用于数据处理。
  • exception:异常处理类,由程序员编写,用于对异常进行处理。

(二)IOC思想概述

①基本概念

参考文章:https://blog.csdn.net/qq_36537546/article/details/90599137
  IOCInversion Of Control,控制倒转),是spring的核心,贯穿始终,所谓IOC ,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。所有的类都会在spring容器中登记,告诉spring你是个什么,你需要什么,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

  • 控制:谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的。
  • 反转:程序本身不再创建对象 , 而变成被动的接收对象。

②案例分析

以下案例参考:https://www.bilibili.com/video/BV1WE411d7Dv

1.不基于IOC思想的案例实现

Dao层
UserDao.java接口

public interface UserDao {
   public void getUser();
}

UserDao.java实现类

public class UserDaoImpl implements UserDao {
   @Override
   public void getUser() {
       System.out.println("获取用户数据");
  }
}

Service层
UserService.java接口

public interface UserService {
   public void getUser();
}

UserService.java实现类

public class UserServiceImpl implements UserService {
	private UserDao userDao = new UserDaoImpl();

	@Override
	public void getUser() {
		userDao.getUser();
	}

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

测试代码
  当需要获取User数据时,就需要先创建UserService实现类对象,通过对象来调用UserDao实现类的方法。

@Test
public void test(){
   UserService service = new UserServiceImpl();
   service.getUser();
}

分析
  以上代码的耦合度是很高的,假如想要实现在MySQL中的查询User功能,则需新建一个UserDao实现类UserMySQLDao,并修改UserServiceImpl中对象的调用,每次变动都需要修改大量代码,当有大量需求时,会严重影响开发效率。

2.基于IOC思想的案例实现

创建UserDao的新功能实现类UserDaoMySqlImpl

public class UserDaoMySqlImpl implements UserDao {
   @Override
   public void getUser() {
       System.out.println("MySql获取用户数据");
  }
}

在上述案例中创建Spring配置文件beans.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 id="UserDaoMySqlImpl " class="com.kuang.dao.impl.UserDaoMySqlImpl"/>

   <bean id="ServiceImpl" class="com.kuang.service.impl.UserServiceImpl">
       <!--将UserDaoMySqlImpl对象赋值给ServiceImpl对象的userDao属性-->
       <property name="userDao" ref="UserDaoMySqlImpl "/>
   </bean>

</beans>

测试代码

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   //使用ClassPathXmlApplicationContext获取配置文件内容
   UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
   //获取UserServiceImpl对象
   serviceImpl.getUser();
}

  在IOC思想下,程序的流程,从上文中传统的由程序本身创建对象并使用,变成了由Spring创建对象,程序被动接收,程序员只需要实现新的功能模块,而不同模块之间的协同,完全由Spring来控制。
  总之,在IOC思想下,组件不负责创建自己依赖的组件,而是让程序员来创建依赖组件,最后通过Spring将依赖组件自动注入原组件。

(三)IOC容器与Bean

  在前文中,我们提到过Spring能完成组件自动注入的功能,从而实现控制反转的思想,我们将被管理的对象称之为Bean(意为咖啡豆,咖啡的制作原料是咖啡豆,而java编程语言的最小组成是对象,所以我们在Spring中采用Bean这个名称),而将Spring中管理对象之间依赖关系的管理者,称之为IOC容器。

前言:工厂模式

参考文章:https://blog.csdn.net/a745233700/article/details/120253639
  软件设计模式有23种,按照功能和使用场景可以分为三大类:

  1. 创建型模式:处理的是对象的创建过程(通过各种方式创建对象,使对象创建和管理变得简单)
  2. 结构型模式:处理的是对象/类的组合。
  3. 行为型模式:处理类和对象间的交互方式和任务分布。

  工厂设计模式(Factory Pattern),是一种很常见的设计模式,属于创建型模式的,主要作用就是来创建对象,其将目的将创建对象的具体过程屏蔽隔离起来,从而达到更高的灵活性,工厂模式可以分为三类:

  1. 简单工厂模式(Simple Factory)
  2. 工厂方法模式(Factory Method)
  3. 抽象工厂模式(Abstract Factory)
a.简单工厂模式

  简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的业务逻辑分离,降低系统的耦合度,使得两个修改起来相对容易些,当以后实现改变时,只需要修改工厂类即可。
组成

  1. 工厂Factory:被应用程序调用以创建具体产品的对象,含有和具体业务逻辑有关的代码。
  2. 抽象产品AbstractProduct:是具体产品继承的父类或实现的接口,在Java中一般有抽象类或者接口来实现。
  3. 具体产品Product:工厂角色所创建的对象就是此角色的实例。

手机公共接口:Phone.java

public interface Phone {
    String getPhoneBrand();
}

华为手机实现类:HuaWeiPhone.java

public class HuaWeiPhone implements Phone {
    @Override
    public String getPhoneBrand(){
        return "HuaWei";
    }
}

苹果手机实现类:MacPhone.java

public class MacPhone implements Phone {
    @Override
    public String getPhoneBrand(){
        return "Mac";
    }
}

手机工厂类:PhoneFactory.java

public class PhoneFactory {
    public Phone getPhone(String brand){
        switch (brand){
            case "HuaWei":
                return new HuaWeiPhone();
            case "Mac":
                return new MacPhone();
            default:
                return null;
        }
    }
}

测试类:MyTest.java

import org.junit.Test;

public class MyTest {
    @Test
    public void test(){
        //创建工程对象
        PhoneFactory phoneFactory=new PhoneFactory();
        //通过工厂来创建特定对象
        Phone MyPhone=phoneFactory.getPhone("Mac");
        System.out.println("手机的牌子是:"+MyPhone.getPhoneBrand());
    }
}

优点
  简单工厂模式提供专门的工厂类用于创建对象,实现了对象创建和使用的职责分离,客户端不需知道所创建的具体产品类的类名以及创建过程(否则,则可能需针对HuaWeiMac各写一个实现类,且,客户端需要手动创建所需的手机对象。当有新的牌子出现时,客户端需要手动创建新的对象,而引入工厂之后,只需要将新牌子的实现类加入到工厂当中,客户端只需要提供参数就可获取到相应牌子的手机对象。),只需知道具体产品类所对应的参数即可,通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点
  不符合"开闭原则"(一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节,且,当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。),每次添加新产品就需要修改工厂类。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护,并且工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。

b.工厂方法模式

  工厂方法模式将工厂抽象化,并定义一个创建对象的接口。每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。
组成

  1. 抽象工厂AbstractFactory: 工厂方法模式的核心,是具体工厂角色必须实现的接口或者必须继承的父类,在Java中它由抽象类或者接口来实现。
  2. 具体工厂Factory:被应用程序调用以创建具体产品的对象,含有和具体业务逻辑有关的代码。
  3. 抽象产品AbstractProduct:是具体产品继承的父类或实现的接口,在Java中一般有抽象类或者接口来实现。
  4. 具体产品Product:具体工厂角色所创建的对象就是此角色的实例。

手机的公共接口:Phone.java

public interface Phone {
    String getPhoneBrand();
}

华为手机实现类:HuaWeiPhone.java

public class HuaWeiPhone implements Phone {
    @Override
    public String getPhoneBrand(){
        return "HuaWei";
    }
}

苹果手机实现类:MacPhone.java

public class MacPhone implements Phone {
    @Override
    public String getPhoneBrand(){
        return "Mac";
    }
}

手机工厂公共接口:PhoneFactory.java

public interface PhoneFactory {
    Phone getPhone();
}

华为手机工厂实现类:HuaWeiPhoneFactory.java

public class HuaWeiPhoneFactory implements PhoneFactory{
    @Override
    public HuaWeiPhone getPhone(){
        return new HuaWeiPhone();
    }
}

苹果手机工厂实现类:MacPhoneFactory.java

public class MacPhoneFactory implements PhoneFactory{
    @Override
    public MacPhone getPhone(){
        return new MacPhone();
    }
}

测试类:MyTest.java

import org.junit.Test;

public class MyTest {
    @Test
    public void test(){
        //1.获取所需工厂(工厂此时相当于是一个可调用的方法)
        MacPhoneFactory macPhoneFactory=new MacPhoneFactory();
        //2.通过工厂获取对象
        MacPhone macPhone= macPhoneFactory.getPhone();
        System.out.println("手机的牌子为:"+macPhone.getPhoneBrand());
    }
}

优点
  在使用时,用于只需知道产品对应的具体工厂,关注具体的创建过程,甚至不需要知道具体产品类的类名,当我们选择哪个具体工厂时,就已经决定了实际创建的产品是哪个了,且,符合“开闭原则”了,扩展时不必去修改原来的代码。
缺点
  每增加一个产品都需要增加一个具体产品类和实现工厂类,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

c.抽象工厂模式

   在工厂方法模式中,我们使用一个工厂创建一个产品,一个具体工厂对应一个具体产品,但有时候我们需要一个工厂能够提供多个产品对象,而不是单一的对象,这个时候我们就需要使用抽象工厂模式。
两个概念

  • 产品等级结构:产品等级结构指的是产品的继承结构,例如一个空调抽象类,它有海尔空调、格力空调、美的空调等一系列的子类,那么这个空调抽象类和他的子类就构成了一个产品等级结构。
  • 产品族:产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。比如,海尔工厂生产海尔空调、海尔冰箱,那么海尔空调则位于空调产品族中。

什么是抽象工厂模式?
  抽象工厂模式主要用于创建相关对象的家族。当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象;并且通过隔离具体类的生成,使得客户端不需要明确指定具体生成类;所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
  但该模式的缺点在于添加新的行为时比较麻烦,如果需要添加一个新产品族对象时,需要更改接口及其下所有子类,这必然会带来很大的麻烦。
组成

  1. 抽象工厂AbstractFactory:定义了一个接口,这个接口包含了一组方法用来生产产品,所有的具体工厂都必须实现此接口。
  2. 抽象工厂AbstractFactory:定义了一个接口,这个接口包含了一组方法用来生产产品,所有的具体工厂都必须实现此接口。
  3. 抽象产品AbstractProduct:这是一个产品家族,每一个具体工厂都能够生产一整组产品。
  4. 具体产品Product:抽象产品的具体实现。

手机公共接口:Phone.java

public interface Phone {
    String getPhoneBrand();
}

华为手机实现类:HuaWeiPhone.java

public class HuaWeiPhone implements Phone {
    @Override
    public String getPhoneBrand(){
        return "HuaWei";
    }
}

苹果手机实现类:MacPhone.java

public class MacPhone implements Phone {
    @Override
    public String getPhoneBrand(){
        return "Mac";
    }
}

手机壳公共接口:PhoneCase.java

public interface PhoneCase {
    String getPhoneCase();
}

华为手机壳实现类:HuaWeiPhoneCase.java

public class HuaWeiPhoneCase implements PhoneCase{
    @Override
    public String getPhoneCase(){
        return "华为手机壳";
    }
}

苹果手机壳实现类:MacPhoneCase.java

public class MacPhoneCase implements PhoneCase{
    @Override
    public String getPhoneCase(){
        return "Mac手机壳";
    }
}

公共工厂接口:AbstractFactory.java

public interface AbstractFactory {
    //获取手机壳对象
    public PhoneCase getPhoneCase();
    //获取手机对象
    public Phone getPhone();
}

华为工厂实现类:HuaWeiFactory.java

public class HuaWeiFactory implements AbstractFactory{
    @Override
    public HuaWeiPhoneCase getPhoneCase(){
        return new HuaWeiPhoneCase();
    }
    @Override
    public HuaWeiPhone getPhone(){
        return new HuaWeiPhone();
    }
}

苹果工厂实现类:MacFactory.java

public class MacFactory implements AbstractFactory{
    @Override
    public MacPhone getPhone(){
        return new MacPhone();
    }
    @Override
    public MacPhoneCase getPhoneCase(){
        return new MacPhoneCase();
    }
}

测试类:MyTest.java

import org.junit.Test;

public class MyTest {
    @Test
    public void test(){
        //1.创建工厂对象
        MacFactory macFactory=new MacFactory();
        HuaWeiFactory huaWeiFactory=new HuaWeiFactory();
        //2.通过工厂对象可获取任意产品对象
        MacPhone macPhone= macFactory.getPhone();
        HuaWeiPhoneCase huaWeiPhoneCase=huaWeiFactory.getPhoneCase();
        System.out.println("手机牌子是:"+macPhone.getPhoneBrand()+",手机壳牌子是:"+huaWeiPhoneCase.getPhoneCase());
    }
}

工厂方法模式与抽象工厂模式的区别在于:

  1. 工厂方法只有一个抽象产品类和一个抽象工厂类,但可以派生出多个具体产品类和具体工厂类,每个具体工厂类只能创建一个具体产品类的实例。
  2. 抽象工厂模式拥有多个抽象产品类(产品族)和一个抽象工厂类,每个抽象产品类可以派生出多个具体产品类;抽象工厂类也可以派生出多个具体工厂类,同时每个具体工厂类可以创建多个具体产品类的实例。
d.BeanFactory与FactoryBean

  工厂模式的实现是Spring管理Bean对象的基础,也是Spring框架解耦和重要的扩展点,其中,BeanFactory与FactoryBeanSpring当中最重要的两个工厂模式的实现。
  在Spring当中,BeanFactoryIOC容器设计方式的顶层接口,也是一种工厂方法模式的典型案例,它的实现类DefaultListableBeanFactorySimpleJndiBeanFactoryStaticListableBeanFactory等等,通过getBean重载方法,为我们创建不同的Bean对象,这样开发过程中,我们可以将需要创建的对象通过配置等方式交给Bean工厂去完成,使用时直接获取便可,从而大大降低耦合度,并增强可扩展性,提高了代码的可维护性,符合设计模式的原则。
  FactoryBeanSpring当中另一个典型的工厂方法模式的实现,它和BeanFactory一样都是用于创建对象,但是,BeanFactory相当于是Spring的一个大工厂,创建着Spring框架运行过程中所需要的Bean,而FactoryBean是一个定制化工厂,其会存在于BeanFactory创建对象的过程中,通过getObject()方法来创建并返回对象,且,当有需要时,会通过FactoryBean去自定制个性化的Bean,从而Spring框架提高扩展能力。

1. 三大核心接口

在这里插入图片描述
  Spring当中IOC容器有三大核心接口,分别是BeanFactoryApplicationContextBeanDefinition,前两者用于IOC容器的实现,是IOC核心容器接口,而BeanDefinition接口则用于描述Bean对象。
  事实上,BeanFactorySpring内部使用的接口,面向Spring本身,不提供给开发人员使用,而ApplicationContextBeanFactory的子接口,提供了更多高级功能。

1.1 BeanFactory
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String name) throws BeansException;

    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    Object getBean(String name, Object... args) throws BeansException;

    <T> T getBean(Class<T> requiredType) throws BeansException;

    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

    boolean containsBean(String name);

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

    String[] getAliases(String name);
}

  Spring框架给我们提供BeanFactory功能,它是IOC容器的最基本形式,给具体的IOC容器的实现提供了规范,是最顶层的接口。
  BeanFactory可用来解决Bean之间的依赖问题,达到了松耦合的效果,且,BeanFactory会在Bean的生命周期的各个阶段中对Bean进行各种管理,并且Spring将这些阶段通过各种接口暴露给我们,让我们可以对Bean进行各种处理,我们只要让Bean实现对应的接口,那么Spring就会在Bean的生命周期调用我们实现的接口来处理该Bean
功能

  • IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象的依赖。
  • BeanFactory定义了Spring管理Bean的通用方法,是负责生产和管理Springbean的一个工厂,也是Spring当中一个典型的工厂方法模式的案例。
  • 多种实现:如DefaultListableBeanFactoryXmlBeanFactoryApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。
方法声明说明
String FACTORY_BEAN_PREFIX = “&”;在获取Bean时,若在Bean的name前加上&,则此时获取的是FactoryBean本身对象,否则获取的是由工厂生成的Bean的代理对象。
Object getBean(String name) throws BeansException;根据Bean的名称在IOC容器中获取Bean的实例对象。
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;根据Bean的名字和Class类型来得到Bean实例,增加了类型安全验证机制。
Object getBean(String name, Object… args) throws BeansException;根据Bean的名称和参数获取Bean实例。
<T> T getBean(Class<T> requiredType) throws BeansException;根据Class类型来获取Bean实例。
<T> T getBean(Class<T> requiredType, Object… args) throws BeansException;根据Class类型和参数获取Bean实例。
boolean containsBean(String name);查看IOC容器中是否包含名称为name的Bean。
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;判断名称为name的Bean是否是单例模式。
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;判断名称为name的Bean是否是Prototype类型。
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;判断名称为name的Bean是否匹配typeToMatch泛型。
Class getType(String name) throws NoSuchBeanDefinitionException;获取名称为name的Bean实例的Class对象。
String[] getAliases(String name);获取名称为name的Bean的所有别名。

在这里插入图片描述

  BeanFactory有3个重要的子接口:ListableBeanFactoryHierarchicalBeanFactoryAutowireCapableBeanFactory。这三个类最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。

  • ListableBeanFactory:表示这些Bean是可列表化的。
  • HierarchicalBeanFactory:表示的是这些Bean是有继承关系的,也就是每个Bean有可能有父Bean
  • AutowireCapableBeanFactory:定义Bean的自动装配规则。

  事实上,每个接口都有它使用的场合,它主要是为了区分在Spring内部在操作过程中对象的传递和转化过程时,对对象的数据访问所做的限制。

1.2 ApplicationContext

参考文章:https://blog.csdn.net/qq_41907991/article/details/104890350
在这里插入图片描述

  ApplicationContext,称为Spring上下文,是SpringBeanFactory基础容器之上,提供的另一个IoC容器实现,也是一个核心IOC容器,在ApplicationContext中通过配置的方式实现了BeanFactory的许多功能。
功能:是springBeanFactory之外的另一个核心IOC容器接口,允许容器通过应用程序上下文环境创建、获取、管理BeanBeanFactory),还有国际化(MessageSource)、加载资源文件(ResourceLoader)、事件发布(ApplicationEventPublisher)、获取容器当前运行的环境(EnvironmentCapable)等功能。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context;

import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable;

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
    @Nullable
    String getId();

    String getApplicationName();

    String getDisplayName();

    long getStartupDate();

    @Nullable
    ApplicationContext getParent();

    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
1.2.1 国际化

1.2.1.1 什么是国际化?
  应用程序运行时,可根据客户端操作系統的国家/地区、语言的不同而显示不同的界面,比如客户端OS的语言环境为大陆的简体中文,程序就显示为简体中文,客户端OS的语言环境为美国——英语,程序就显示美式英语。
  对于有国际化要求的应用系统,我们不能简单地采用硬编码的方式编写代码,而是为每种语言提供一套相应的资源文件,并以规范化命名的方式保存在特定的目录中,由系统自动根据客户端语言选择适合的资源文件。

1.2.1.2 JAVA国际化
  国际化信息也称为本地化信息,一般需要两个条件才可以确定一个特定类型的本地化信息,它们分别是“语言类型”和“国家/地区的类型”。如中文本地化信息既有中国大陆地区的中文,又有中国台湾、中国香港地区的中文,还有新加坡地区的中文。国际化代码:

ar_sa 阿拉伯语(沙特阿拉伯)
ar_iq 阿拉伯语(伊拉克)
eu 巴斯克语
bg 保加利亚语
zh_tw 中文(中国台湾)
zh_cn 中文(中华人民共和国)
zh_hk 中文(中国香港特别行政区)
zh_sg 中文(新加坡)
hr 克罗地亚语
en 英语
en_us 英语(美国)
en_gb 英语(英国)
en_au 英语(澳大利亚)
en_ca 英语(加拿大)

  JAVA通过java.util.Locale类表示一个本地化对象,它允许通过语言参数和国家/地区参数创建一个确定的本地化对象:

Locale locale=new Locale("zh","cn");//中文,中国
Locale locale2=new Locale("en","us");//英文,美国
Locale locale3=new Locale("zh");//中文--不指定国家
Locale locale4=Locale.CHINA;//中文,中国
Locale locale5=Locale.CHINESE;//中文

在持有一个Locale对象后,我们需要将同一个文字或者数字根据不同的地区/语言格式化成不同的表现形式,所以这里我们还需要一个格式化的操作,JDK给我们提供以下几个常见的类用于国际化格式化:
a.NumberFormat:处理数字、百分数、货币等,如:

public static void main(String[] args) {
    // 1.通过语言跟地区确定一个Locale对象
    // 中国,中文
    Locale chinaLocale = new Locale("zh", "cn");
    // 美国,英文
    Locale usLocale = new Locale("en", "us");
    // 获取货币格式化对象
    NumberFormat chinaCurrencyFormat = NumberFormat.getCurrencyInstance(chinaLocale);
    NumberFormat usLocaleCurrencyFormat = NumberFormat.getCurrencyInstance(usLocale);
    // 中文,中国下的货币表现形式
    String chinaCurrency = chinaCurrencyFormat.format(99.9);  // 输出 ¥99.90
    // 美国,英文下的货币表现形式
    String usCurrency = usLocaleCurrencyFormat.format(99.9); // 输出 $99.90
    System.out.println(chinaCurrency);
    System.out.println(usCurrency);
}

b.DateFormat:通过DateFormat.getDateInstance(int style,Locale locale)方法按本地化的方式对日期进行格式化操作。该方法第一个入参为时间样式,第二个入参为本地化对象

public static void main(String[] args) {
    // 1.通过语言跟地区确定一个Locale对象
    // 中国,中文
    Locale chinaLocale = new Locale("zh", "cn");
    // 美国,英文
    Locale usLocale = new Locale("en", "us");
    DateFormat chinaDateFormat = DateFormat.getDateInstance(DateFormat.YEAR_FIELD,chinaLocale);
    DateFormat usDateFormat = DateFormat.getDateInstance(DateFormat.YEAR_FIELD,usLocale);
    System.out.println(chinaDateFormat.format(new Date())); // 输出 2020年1月15日
    System.out.println(usDateFormat.format(new Date()));    // 输出 January 15, 2020
}

c.MessageFormat:在NumberFormatDateFormat的基础上提供了强大的占位符字符串的格式化功能,它支持时间、货币、数字以及对象属性的格式化操作。

public static void main(String[] args) {
    // 1.通过语言跟地区确定一个Locale对象
    // 中国,中文
    Locale chinaLocale = new Locale("zh", "cn");
    String str1 = "{0},你好!你于{1}在农业银行存入{2}。";
    MessageFormat messageFormat = new MessageFormat(str1,chinaLocale);
    Object[] o = {"小红", new Date(), 99.99};
    System.out.println(messageFormat.format(o));
    // 输出:小红,你好!你于20-1-15 下午4:05在农业银行存入99.99。
}

  012代表的是占位符的索引,从0开始计数,datenumber为格式化的类型,而longcurrency为格式化样式。

  • FormatType:格式化类型,取值如下:
    • number:调用NumberFormat进行格式化。
    • date:调用DateFormat进行格式化。
    • time:调用DateFormat进行格式化。
    • choice:调用ChoiceFormat进行格式化
  • FormatStyle:设置使用的格式化样式。
1.3 BeanDefinition

2.Bean

2.1 普通Bean
2.2 FactoryBean

在这里插入图片描述
  FactoryBean是一种特殊的Bean,它在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,是一个可以创建或修饰其他对象的工厂 bean,可创建除自身以外其他对象。
  一般情况下实例化一个Bean对象时,Spring通过反射机制利用<bean>class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。
  而实现了FactoryBean接口后,可在getObject()方法中灵活配置,来定制实例化Bean的逻辑,使得从BeanFactory及其实现类的getBean()方法中获取到的Bean对象,实际上是FactoryBeangetObject()方法创建并返回的Bean对象,而不是FactoryBean本身。

public interface FactoryBean<T> {
	
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    
	//从Factory中获取Bean
	@Nullable
	T getObject() throws Exception;

	// 从BeanFactory中获取类型
	@Nullable
	Class<?> getObjectType();

	//判断是否是单例
	default boolean isSingleton() {
		return true;
	}
}
方法声明说明
String OBJECT_TYPE_ATTRIBUTE = “factoryBeanObjectType”;声明实现接口后属于FactoryBean类型。
T getObject() throws Exception;返回Bean对象。如果一个类实现了FactoryBean接口,则该类就变成了工厂,此时根据类名所获取的实际上是该工厂调用getObject()返回的对象,而非工厂自身的对象,若想要获取工厂自身的对象,则需要加上&前缀。
Class<?> getObjectType();返回的Bean对象的类型。
default boolean isSingleton()判断是否是单例。

案例
汽车类:Car.java

public class Car {
    private String Brand;
    private Double Price;
    public Car() {
    }
    public Car(String brand, Double price) {
        Brand = brand;
        Price = price;
    }
}

汽车工厂类:CarFactory.java

import org.springframework.beans.factory.FactoryBean;

public class CarFactory implements FactoryBean<Car> {
    @Override
    public Car getObject() throws Exception {
        return new Car("宝马",32351.5);
    }

    @Override
    public Class<?> getObjectType() {
        return Car.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

配置文件:Beans.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 id="CarFactory" class="CarFactory"/>
</beans>

测试类:MyTest.java

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

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("UserBeans.xml");
        //获取Bean对象
        System.out.println(IOC.getBean("CarFactory"));
        //获取工厂对象
        System.out.println(IOC.getBean("&CarFactory"));
    }
}

在这里插入图片描述
使用断点调试:
在这里插入图片描述
可见,此时BeanFactory.getBean()方法最终返回的是xxxFactoryBean.getObject()方法中创建并返回的Bean对象,而非工厂本身。

BeanFactoryFactoryBean的对比
  BeanFactorySpringIOC容器的核心公共接口,其提供方法支持外部程序对所管理的bean进行访问,在程序启动时根据传入的参数产生各种类型的bean,并添加到IOC容器(实现BeanFactory接口的类) 的ingletonObject属性中。常见的DefaultListableBeanFactoryXmlBeanFactoryApplicationContext都是实现了BeanFactoryIOC容器。
  FactoryBean是一个bean,但它又不仅仅是个bean。它是一个可以 创建 或 修饰 其他对象的工厂bean,这跟设计模式中的工厂模式或者装饰设模式很相似,它可以创建除自身以外的其他对象,其最大的应用场景是用在AOP中,AOP 代理对象通过java反射机制在运行时创建代理对象,并根据业务需求将相应的方法编织到代理对象的目标方法中,Spring中的ProxyFactoryBean就是这个功能。

2.3 Bean的作用域

  Bean的作用域是指BeanSpring整个框架中的某种行为模式,是创建的Bean对象相对于其他Bean对象的请求可见范围。比如singleton单例作用域,就表示Bean在整个Spring中只有一份,它是全局共享的,当其他人修改了这个值之后,另一个人读取到的就是被修改的值。
五种作用域

范围说明
singleton(单例作用域)将每个Spring IoC容器的单个 bean 定义范围限定为单个对象实例。
prototype:原型作用域(多例作用域)将单个 bean 定义的作用域限定为任意数量的对象实例。
request(请求作用域)每个 HTTP 请求都有一个自己的 bean 实例,仅在含有Web应用的Spring的上下文ApplicationContext中有效。
session(会话作用域)每个session会话都有一个自己的 bean 实例,仅在含有Web应用的Spring的上下文ApplicationContext中有效。
application(上下文作用域)每个ServletContext都有一个自己的 bean 实例,仅在含有Web应用的Spring的上下文ApplicationContext中有效。
websocketHTTP WebSocket作用域)每个WebSocket都有一个自己的 bean 实例,仅在含有Web应用的Spring的上下文ApplicationContext中有效。

其中,singletonprototype属于基本作用域,后三者应用在Web应用当中,在SpringMVC当中再进行介绍。

2.3.1 singleton

  单例作用域,当将Beanscope设置为singletonSpringIOC容器将仅生成和管理一个Bean实例,且当你是用idname获取Bean实例时,IOC容器会返回共享的Bean实例。

<bean id="Bean名称" class="全类名" scope="singleton"/>

例:
配置文件:Beans.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 id="MacPhone" class="MacPhone" scope="singleton"/>
</beans>

测试类:MyTest.java

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

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("UserBeans.xml");
        //获取单例对象
        MacPhone macPhone1=(MacPhone) IOC.getBean("MacPhone");
        MacPhone macPhone2=(MacPhone) IOC.getBean("MacPhone");
        System.out.println("macPhone1的引用为:"+macPhone1);
        System.out.println("macPhone2的引用为:"+macPhone2);
    }
}

在这里插入图片描述

2.3.2 prototype

  原型作用域(多例作用域),指每次向Spring容器请求获取Bean都返回一个全新的Bean

<bean id="Bean名称" class="全类名" scope="prototype"/>

例:
配置文件:Beans.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 id="MacPhone" class="MacPhone" scope="prototype"/>
</beans>

测试类:MyTest.java

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

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("UserBeans.xml");
        //获取单例对象
        MacPhone macPhone1=(MacPhone) IOC.getBean("MacPhone");
        MacPhone macPhone2=(MacPhone) IOC.getBean("MacPhone");
        System.out.println("macPhone1的引用为:"+macPhone1);
        System.out.println("macPhone2的引用为:"+macPhone2);
    }
}

在这里插入图片描述

2.4 Bean的生命周期

  由前文可知,Bean就是SpringIOC容器实例化、组装和管理的对象。Bean对象和普通的Java对象类似,但是Spring不再自己去new对象了,而是由IOC容器去帮助我们实例化对象并且管理它,Bean的生命周期完全由容器控制。

对于普通的JAVA对象,其生命周期为:

  1. 实例化。
  2. 不再使用时由垃圾回收机制回收。

对于Bean对象,其基本生命周期为:

  1. 实例化(Instantiation)。
  2. 属性赋值(Populate)。
  3. 初始化(Initialization)。
  4. 销毁(Destruction)。

注:前文提到过,BeanFactory管理的Bean是在使用到Bean的时候才会实例化BeanApplicantContext管理的Bean在容器初始化的时候就回完成Bean实例化,前者是提供给Spring内部使用,后者提供给开发人员使用,故此处探讨的是后者。

2.4.1 基本生命周期

  在基本生命周期中,只探讨:实例化Bean对象->Bean对象属性赋值 -> 初始化Bean对象-> 销毁Bean对象,这四个步骤,其中,实例化和属性赋值都是Spring帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。

a.实例化Bean对象

  Spring会根据XML配置和@Autowire注解注入来获取Bean的定义信息(BeanDefinition),得到一份需要实例化的Bean列表,然后才能开始实例化。事实上,Spring提供了BeanDefinitionReader接口,用于获取Bean的定义信息,它有AbstractBeanDefinitionReaderGroovyBeanDefinitionReaderPropertiesBeanDedefintionReaderXmlBeanDefinitionReader四个实现类用于获取信息,其中,XmlBeanDefinitionReader就是负责机械XML配置文件当中的Bean,并执行相关的方法,将XML中的Bean配置解析成BeanDefinition对象。

  在得到包含Bean定义信息的BeanDefinition对象后,并不能直接反射创建对象,因为此时还有很多信息是不全的,比如:附属Bean的配置信息、占位符没替换、自定义的逻辑没处理等等,所以,在实例化Bean之前还有很多需要处理,这就是BeanFactoryPostProcessor接口的功能(事实上,在实例化Bean之前,完成了各种复杂配置、自定义配置的处理等,放在下一节写)。

  当客户向容器请求一个尚未初始化的Bean时,容器就会调用doCreateBean()方法进行实例化,实际上就是通过反射的方式创建出一个Bean对象,且,过程非常复杂,且整个过程中Spring留下了大量可扩展点。
AbstractAutowireCapableBeanFactory类AbstractBeanFactory的子类,实现了AutowireCapableBeanFactory接口,用于提供Bean创建(构造函数创建)、属性装配(population),注入(autowiring)的功能。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
	...
// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
	
   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   ...
   if (instanceWrapper == null) {
       //实例化阶段
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
       //属性赋值阶段
      populateBean(beanName, mbd, instanceWrapper);
       //初始化阶段
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
	...
   }

	...
}

进入org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance方法,一直进入,找到org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate方法,就能看出,Spring采用反射机制实例化Bean对象:

@Override
	public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
		// Don't override the class with CGLIB if no overrides.
		if (!bd.hasMethodOverrides()) {
			Constructor<?> constructorToUse;
			synchronized (bd.constructorArgumentLock) {
				constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
				if (constructorToUse == null) {
					final Class<?> clazz = bd.getBeanClass();
					if (clazz.isInterface()) {
						throw new BeanInstantiationException(clazz, "Specified class is an interface");
					}
					try {
						if (System.getSecurityManager() != null) {
							constructorToUse = AccessController.doPrivileged(
									(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
						}
						else {
							constructorToUse = clazz.getDeclaredConstructor();
						}
						bd.resolvedConstructorOrFactoryMethod = constructorToUse;
					}
					catch (Throwable ex) {
						throw new BeanInstantiationException(clazz, "No default constructor found", ex);
					}
				}
			}
			return BeanUtils.instantiateClass(constructorToUse);
		}
		else {
			// Must generate CGLIB subclass.
			return instantiateWithMethodInjection(bd, beanName, owner);
		}
	}
b.Bean对象属性赋值
// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (instanceWrapper == null) {
       // 实例化阶段!
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
       // 属性赋值阶段!
      populateBean(beanName, mbd, instanceWrapper);
       // 初始化阶段!
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   ...
}

  Bean实例创建出来后,接着就是给这个Bean对象进行属性填充(populateBean方法),包括自定义属性赋值和容器对象属性赋值(invokeAwareMethods方法,在initializeBean 方法中调用,就会检查 Aware相关接口的实现和配置,执行了这些逻辑之后,才调用了invokeInitMethods执行初始化逻辑),由于第一步的时候已经转换成BeanDefinition对象了,直接取值即可。

	private void invokeAwareMethods(String beanName, Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}
c.初始化Bean对象
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
	...
// 忽略了无关代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
	
   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   ...
   if (instanceWrapper == null) {
       //实例化阶段
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
       //属性赋值阶段
      populateBean(beanName, mbd, instanceWrapper);
       //初始化阶段
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
	...
   }

	...
}

初始化时机:对象实例化完成,且,一些属性也完成赋值,那么就会调用Bean的初始化方法。对于单实例Bean来说,在Spring容器创建完成后,Spring容器会自动调用Bean的初始化方法initializeBean;对于多实例bean来说,在每次获取Bean对象的时候,调用bean的初始化方法initializeBean

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
	...
    protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
        this.invokeAwareMethods(beanName, bean);
        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        }

        try {
            this.invokeInitMethods(beanName, wrappedBean, mbd);
        } catch (Throwable var6) {
            throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, var6.getMessage(), var6);
        }

        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }
    ...
}

其中,调用了invokeAwareMethods()方法,而在真正的初始化方法invokeInitMethods) 之前、之后,各有一个前置和后置处理器,留给开发者拓展使用。
对于invokeAwareMethods()方法:

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
      throws Throwable {

   boolean isInitializingBean = (bean instanceof InitializingBean);
   if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
      if (logger.isTraceEnabled()) {
         logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
      }
      if (System.getSecurityManager() != null) {
         try {
            AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
               ((InitializingBean) bean).afterPropertiesSet();
               return null;
            }, getAccessControlContext());
         }
         catch (PrivilegedActionException pae) {
            throw pae.getException();
         }
      }
      else {
         ((InitializingBean) bean).afterPropertiesSet();
      }
   }

   if (mbd != null && bean.getClass() != NullBean.class) {
      String initMethodName = mbd.getInitMethodName();
      if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
         invokeCustomInitMethod(beanName, bean, mbd);
      }
   }
}

  SpringBean提供了两种初始化的方式,实现InitializingBean接口(也就是要实现该接口中的afterPropertiesSet方法),或者在配置文件或@Bean注解中通过init-method来指定,两种方式可以同时使用。这两种方式可以同时使用,同时使用先调用afterPropertiesSet方法,后执行init-method指定的方法。

原理分析
  Spring中提供了一个InitializingBean接口,对应生命周期的初始化阶段,该接口为Bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在Bean的属性赋值后都会执行该方法。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.beans.factory;

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

案例
配置文件:Beans.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 id="carBean" class="Car"/>
    
</beans>

汽车类:Car.java

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

public class Car implements InitializingBean {
    public Car(){
        System.out.println("执行Car构造器");
    }
    @Override
    public void afterPropertiesSet(){
        System.out.println("执行初始化方法");
    }
}

测试类:MyTest.java

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

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("UserBeans.xml");
        //获取Car对象
        System.out.println(IOC.getBean("carBean"));
    }
}

在这里插入图片描述
(可见,是先实例化再初始化)

d.销毁Bean对象

  在Spring当中,对于单实例Bean对象,会在容器关闭时销毁,而多实例Bean对象,容器不会管理Bean,只是负责创建。

2.4.2 扩展生命周期

流程图:
在这里插入图片描述

https://blog.csdn.net/kykangyuky/article/details/123114227

3.IOC工作流程

bean创建过程大体分为五个步骤:加载→解析→实例化→初始化→获取

1.Tomcat启动,IoC容器初始化,加载扫描包下的Class文件。

2.BeanDefinitionReader读取配置文件中的bean定义信息加载解析为BeanDefinition。

3.通过反射将BeanDefinition实例化为Bean对象。

4.然后进行初始化Bean对象中的属性信息和方法。

  (1) 设置Bean的属性

  (2) 检查Aware相关接口并设置相关依赖

  (3) 检查BeanPostProcessor接口postProcessBeforeInitialization方法进行前置处理

  (4) 检查Bean在Spring配置文件中配置的init-method属性并自动调用其配置的初始化方法

  (5) 检查BeanPostProcessor接口postProcessAfterInitialization方法并进行后置处理

5.最后获取到完整的Bean对象。

(四)Bean的管理:基于XML

①配置文件

  Spring配置文件是指导Spring工厂进行Bean生产、依赖关系注入的图纸。例:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:tx="http://www.springframework.org/schema/tx"
	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
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
	
	<!-- 注解包扫描 -->
	<context:component-scan base-package="com.test.fx.service"></context:component-scan>
	<!-- 引入DB配置文件 -->
	<context:property-placeholder location="classpath:oracleDriver.properties"/>	
	<!-- 配置数据源 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="${driver}"></property>
		<property name="url" value="${url}"></property>
		<property name="username" value="${name}"></property>
		<property name="password" value="${password}"></property>
	</bean>
	<!-- 创建SqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<property name="mapperLocations" value="classpath:com/baizhi/fx/dao/*DaoImpl.xml"></property>
	</bean>
	<!-- 扫描Mapper文件并生成dao对象 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
		<property name="basePackage" value="com.test.fx.dao"></property>
	</bean>
	<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 事务管理器注解 -->	
	<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
1.XML结构体
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" <!-- 默认bean命名空间 -->
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <!-- xml约束命名空间定义 -->
xmlns:aop="http://www.springframework.org/schema/aop"<!-- AOP命名空间的scheme约束 -->
xmlns:context="http://www.springframework.org/schema/context" <!-- context命名空间的scheme约束 -->

xsi:schemaLocation=" <!-- 上面各个scheme的location信息(网络地址) -->
     http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.0.xsd ">

</beans>
2. bean标签

  常见的Bean标签:

<!-- 一个最基本的bean标签 -->
<bean id="bean名称" class="bean的类全名(类全限定路径)" scope="作用域">
属性说明
idbean的名称,一般为类名的驼峰命名。
classBean的完全限定名,就是类的全路径名。
scopebean的作用域,可选值有singleton(单例)、prototype(多例,又叫原型)、request、session、global session。
lazy-init表明了Bean是否为延迟加载,false为立即加载,表示在spring启动时,立刻进行实例化。为true时将不会在ApplicationContext启动时提前被实例化,而是第一次向容器通过getBean索取bean时实例化才会加载。
init-method在Bean实例被创建后、初始化时指定需要执行的方法。
destroy-method用于容器在销毁bean之前调用的方法。
abstract是否为抽象Bean,Spring对抽象Bean不产生实例,主要用于继承。
parent父Bean的名称,会继承父Bean的属性。
factory-bean指定创建该bean时使用的工厂类名。
depends-on依赖对象,这个Bean在初始化时依赖的对象,这个对象会在这个Bean初始化之前创建。
dependency-check依赖检查它用来确保Bean组件通过JavaBean描述的所以依赖关系都得到满足。在与自动装配功能一起使用时,它特别有用。可选值:none(不进行依赖检查),objects(只做对象间依赖的检查),simple(只做原始类型和String类型依赖的检查),all(对所有类型的依赖进行检查。它包括了前面的objects和simple)
autowire自动装配,它定义了Bean的自动装载方式。 可选值:no(不使用自动装配功能),byName(通过Bean的属性名实现自动装配),byType(通过Bean的类型实现自动装配),constructor(构造函数的参数的自动组装),autodetect通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式
2.1 lazy-init属性

  如果一个设置了立即加载的Bean1,引用了一个延时加载的Bean2,那么Bean1在容器启动时被实例化,而Bean2由于被Bean1引用,所以也被实例化,这种情况也符合延时加载的Bean在第一次调用时才被实例化的规则。
  事实上,可在容器层次<beans>来控制延时初始化:

<beans default-lazy-init="true"><!-- no beans will be eagerly pre-instantiated... --></beans>

  如果一个Beanscope属性为scope="pototype"时,即使设置了lazy-init=“false”,容器启动时不实例化Bean,而是调用getBean()方法实例化的

2.2 init-method属性

  此属性用于替代的是InitializingBean接口。InitializingBean接口为bean提供了初始化方法的方式,它只有afterPropertiesSet方法,凡是继承该接口的类,在初始化Bean的时候会执行该方法。此接口在前文中已详细写过,故不再赘述。

2.2 destroy-method属性

  此属性用于替代的是DisposableBean接口中的destroy()方法,但当Bean的作用域scope = "prototype"时,该属性将失效,因为在Spring中,如果一个Bean为多例的,那么该Bean被创建后,Spring将不再去管理了。

2.3 abstract和parent属性

  abstract属性可将Bean声明为抽象Bean,而parent属性则用于指定继承的Bean对象。
例,多个用户性别、年龄相同,就可在创建Bean时将相同的属性抽取出来:

<beans>
    <bean id="abstractBean" abstract="true">
        <property name="Sex" value="Male"/>
        <property name="Age" value="18"/>
    </bean>
    <!--其他Bean所对应的实体类中也有相对应的属性-->
    <bean id="mikeBean" class="Mike" parent="abstractBean">
        <property name="Id" value="001"/>
        <property name="Hobby" value="Swimming"/>
    </bean>
    <bean id="johnBean" class="John" parent="abstractBean">
        <property name="Id" value="002"/>
        <property name="Hobby" value="Basketball"/>
    </bean>
</beans>
2.4 factory-bean和factory-method属性

  这两个属性可以同时出现,也可以只指定factory-method
a.同时使用factory-beanfactory-method
汽车工厂类:CarFactory.java

public class CarFactory {
   public Car createCar(){
       Car car = new Car();
       return car;
   }
}

配置文件:Beans.xml

<!--创建汽车工厂Bean-->
<bean id="carFactory" class="com.factory.demo.CarFactory"/>
<!--指定汽车工厂Bean和创建car1的Bean对象所用方法CarFactory()-->
<bean id="car1" factory-bean="carFactory" factory-method="createCar">
</bean>

注意,此时不能指定静态方法,因为static方法可直接通过类调用而无需实例化。
b.只使用factory-method
汽车工厂类:CarFactory.java

public class CarFactory {
   // 静态方法
   public static Car createStaticCar(){
       Car car = new Car();
       return car;
   }
}

配置文件:Beans.xml

<!-- 指定该bean的class类型为该bean的工厂类 -->
<bean id="car2" class="com.bean.demo.CarFactory" factory-method="createStaticCar"></bean>

这里该Beanclass类型为产生该Bean的工厂类,factory-method指向该工厂类的静态方法。

c.factory-method方法有参数
  如果有参数,那么我们可以通过constructor-arg进行配置,原理跟普通的构造方法注入是一样的。
汽车工厂类:CarFactory.java

public class CarFactory {
   // 静态方法
   public static Car createStaticCar(String brand){
       Car car = new Car();
       car.setBrand(brand);
       return car;
   }
}

配置文件:Beans.xml

<bean id="car2" class="com.bean.demo.CarFactory" factory-method="createStaticCar">
	<constructor-arg name="brand" value="GTR" />
</bean>
2.5 ref属性

  当Bean的属性是其他Bean时,就可使用ref属性,它有localbean两个基本属性。

  • local:如果一个Bean与被参考引用的Bean在同一个xml文件中而且被引用参考的Bean是用id来命名的,那么就可以使用reflocal属性。
  • bean:允许指向的Bean可以在同一个xml,也可以不在同一个xml中。Bean属性的值可以与被参考引用的Beanid属性相同,也可以与被参考引用的bean的属性不相同。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/db_ssm" />
    <property name="username" value="hc" />
    <property name="password" value="123456" />
</bean>

<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource">
        <ref local="dataSource"/>
        <!-- 
			<ref bean="dataSource">
		-->
    </property>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
2.6 tx标签
<!-- 配置事务管理器 -->
<bean id="事务管理bean id" class="类的全限定路径">
	<property name="数据源属性名称" ref="引用的数据源实例名称" />
</bean>

<!-- 配置一个事务通知 -->
<tx:advice id="事务通知名称" transaction-manager="事务管理器实例名称">
    <tx:attributes>
        <tx:method name="方法名" read-only="是否只读" propagation="事务类型"/>
        <!-- 其他所有方法,以默认事务运行 -->
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>

<!-- 配置事务切入点 -->
<aop:config>
	<aop:pointcut id="事务切入点id" expression="事务切入点表达式" />
    <aop:advisor advice-ref="事务通知名称" ponitcut-ref="事务切入点id" />
</aop:config>

例:

<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${driver}"></property>
    <property name="url" value="${url}"></property>
    <property name="username" value="${name}"></property>
    <property name="password" value="${password}"></property>
</bean>

<!-- 配置事务管理器 -->
<bean id="transactionManager" 		
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置事务通知 -->
<!-- 使用<tx:advice>元素声明事务通知,需要指定id属性,以便AOP把通知和切入点关联起来 -->
<!-- 还需要指定transaction-manager属性,其值为Bean配置文件中事务管理器的id属性值 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 在<tx:advice>元素下声明<tx:attributes>元素,用于指定事务属性 -->
    <!-- 在<tx:attributes>元素下可以使用多个<tx:method>元素指定不同方法的不同事务属性 -->
	<tx:attributes>
		<!-- 根据方法名指定事务的属性  -->
		<tx:method name="BookShopXmlService" propagation="REQUIRED"/>
        <!--方法名以get开头的事务属性 -->
		<tx:method name="get*" read-only="true"/>
		<tx:method name="find*" read-only="true"/>
		<tx:method name="*"/>
	</tx:attributes>
</tx:advice>

<!-- 配置事务切入点,以及把事务切入点和事务属性关联起来 -->
<!-- 在<aop:config>元素下,使用<aop:advisor>元素声明一个增强器,将事务通知和切入点关联起来 -->
<!-- 使用 advice-ref属性指定事务通知,用pointcut-ref属性指定切入点 -->
<aop:config>
	<aop:pointcut expression="execution(* com.sqp.spring.service.*.*(..))"
		id="txPointcut"/>
	<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
2.7 context标签

a.<context:annotaion-config>注解装配
  注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置<context:annotation-config/>元素。

<!-- 使用@Autowired注解,必须事先在Spring容器中声明AutowiredAnnotationBeanPostProcessor的Bean: -->
<bean class="org.springframework.beans.factory.annotation.
             AutowiredAnnotationBeanPostProcessor "/>

<!-- 使用 @Required注解,就必须声明RequiredAnnotationBeanPostProcessor的Bean -->
<bean class="org.springframework.beans.factory.annotation.
             RequiredAnnotationBeanPostProcessor"/>

<!-- 类似地,使用@Resource、@PostConstruct、@PreDestroy等注解就必须声明 CommonAnnotationBeanPostProcessor;使用@PersistenceContext注解,就必须声明 PersistenceAnnotationBeanPostProcessor的Bean。 -->

<!-- 这样的声明未免太不优雅,而Spring为我们提供了一种极为方便注册这些BeanPostProcessor的方式,即使用<context:annotation- config/>隐式地向 Spring容器注册AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor以及PersistenceAnnotationBeanPostProcessor这4个BeanPostProcessor。 -->
<context:annotation-config/> 

b.<context:component-scan>组件扫描
  组件扫描可以在类路径底下寻找标注了@Component@Service@Controller@Repository注解的类,并把这些类纳入进Spring容器中管理。

<!-- base-package 指定了要扫描的包路径 -->
    <context:component-scan base-package="org.example.Controller">
        <!--<context:exclude-filter type="" expression=""/>
                context:exclude-filter,排除标签:用于指出当前扫描过程中要排除掉组件.
                type:排除时所依据的原则,常用选项:annotation(注解类型)、assignable(类类型).
                    当type为annotation时,若要排除控制层,则expression="import org.springframework.stereotype.Controller;",即指出注解的全类名.
                    当type为assignable时,则expression需要指定目标类的全类名,如,"org.example.Controller.UserController".
            -->
        <!--<context:include-filter type="" expression=""/>
                注意,在使用之前需要在父级标签中指定参数:<context:component-scan base-package="org.example.Controller" use-default-filters="false">,
            因为use-default-filters默认为true,含义为默认在扫描所有标签的基础上...,对于exclude而言即为在扫描所有标签的情况下排除...,自然无影响,而对于include而言,
            就是使得其失效(因为default-filters中是所有,其包含了include-filter).
                context:include-filter,指定标签:用于指出当前扫描过程中要扫描组件的范围.
        -->

这个配置隐式注册了多个对注解进行解析处理的处理器,包括<context:annotation-config/>该配置注册的处理器,也就是说写了<context:component-scan base-package="" />配置,就不用写<context:annotation-config/>配置了。另外此标签还有一个属性是use-default-filter,默认为true这就意味着会扫描指定包下的全部的标有@Component@Controller@Service等等的类,并注册成Bean
  <context:component-scan>标签还有两个子标签,用于在包内自由选择扫描的组件:

<context:exclude-filter> 指定的不扫描
<context:include-filter> 指定的扫描
<!-- 例如,我们只想扫描指定包下面的Controller -->
<!-- 此处如果省略了use-default属性,不仅会把指定包下的Controller扫描出来,还会把带有@Service等注解的其他组件扫描出来 -->
<context:component-scan base-package="com.sparta.trans" use-default-filters="false">
 <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

c.<context:property-placeholder>配置文件处理
  用来处理用一个proerties文件里面的内容来替换Spring配置文件中的${}内容,例如:

<!-- 引入DB配置文件 -->
<context:property-placeholder location="classpath:oracleDriver.properties"/>	
<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${driver}"></property>
    <property name="url" value="${url}"></property>
    <property name="username" value="${name}"></property>
    <property name="password" value="${password}"></property>
</bean>

常用属性

属性说明
location表示引入配置文件的位置,多个之间用逗号分隔。
file-encoding指定文件编码,如GBK、UTF-8等。
ignore-resource-not-found如果属性文件找不到,是否忽略,默认false,即不忽略,找不到将抛出异常
ignore-unresolvable是否忽略解析不到的属性,如果不忽略,找不到将抛出异常。
properties-ref配置类的引用,如果在xml中声明了properties bean,那么将引用这个bean的id。
local-override是否本地覆盖模式,即如果true,那么properties-ref的属性将覆盖location加载的属性。
system-properties-mode系统属性模式:ENVIRONMENT(默认,将使用Spring 3.1提供的PropertySourcesPlaceholderConfigurer,其他情况使用Spring 3.1之前的PropertyPlaceholderConfigurer如果是本地覆盖模式:那么查找顺序是:properties-ref、location、environment,否则正好反过来;)、OVERRIDE(PropertyPlaceholderConfigurer使用,因为在spring 3.1之前版本是没有Enviroment的,所以OVERRIDE是spring 3.1之前版本的Environment如果是本地覆盖模式:那么查找顺序是:properties-ref、location、System.getProperty(),System.getenv(),否则正好反过来;)、NEVER(只查找properties-ref、location;)。

②获取Bean对象的常用方式

  在基于XML配置方式中,获取Bean对象的常用方式有三种:按Class对象来获取、按id来获取、通过Class对象和id获取。

1.根据Class对象获取

配置文件:Beans.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 id="personBean" class="Pojo.Person">
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
        <property name="sex" value=""/>
    </bean>
</beans>

测试类:MyTest.java

import Pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取Person对象
        System.out.println(IOC.getBean(Person.class));
    }
}

通过类型来获取Bean,这种方式只适合获取单例的对象,如果有多个,则会报 NoUniqueBeanDefinitionException。这是因为当有多个相同类型Bean对象的时候,只通过类型获取就会有多个匹配,我们的程序不知道你到底需要获取那个对象,最终就会报错。

2.根据id获取

配置文件:Beans.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 id="personBean" class="Pojo.Person">
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
        <property name="sex" value=""/>
    </bean>
</beans>

测试类:MyTest.java

import Pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取Person对象
        Person person=(Person) IOC.getBean("personBean");
        System.out.println(person);
    }
}

此时获取到的是Object对象(可见前文中源码解析部分),这种获取方式不会对对象有单例的那种限制,因为我们是通过id来获取的Bean

3.根据Class对象和id获取

配置文件:Beans.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 id="personBean" class="Pojo.Person">
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
        <property name="sex" value=""/>
    </bean>
</beans>

测试类:MyTest.java

import Pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取Person对象
        Person person=(Person) IOC.getBean("personBean", Person.class);
        System.out.println(person);
    }
}

③依赖注入

  依赖注入(DIDependency Injection),组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。事实上,IOCDI是同一个概念的不容角度描述,在Spring框架当中,前者重点强调,由Spring来负责控制对象的生命周期和对象间的关系,后者则重点强调,在系统运行中,动态的向某个对象提供它所需要的其他对象。

  • 依赖Bean对象的创建依赖于IOC容器,指Bean对象创建时所依赖的资源。
  • 注入:指Bean对象创建时资源的注入由容器来完成。
1.构造器注入

Person实体类:Person.java

package Pojo;

public class Person {
    private String Name;
    private Integer Age;
    private String Sex;
    private Person Father;

    public Person() {
    }

    public Person(String name, Integer age, String sex, Person father) {
        Name = name;
        Age = age;
        Sex = sex;
        Father = father;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public Integer getAge() {
        return Age;
    }

    public void setAge(Integer age) {
        Age = age;
    }

    public String getSex() {
        return Sex;
    }

    public void setSex(String sex) {
        Sex = sex;
    }

    public Person getFather() {
        return Father;
    }

    public void setFather(Person father) {
        Father = father;
    }

    @Override
    public String toString() {
        return "Person{" +
                "Name='" + Name + '\'' +
                ", Age=" + Age +
                ", Sex='" + Sex + '\'' +
                ", Father=" + Father +
                '}';
    }
}

属性列表

private String Name;
private Integer Age;
private String Sex;
private Person Father;
1.1 无参构造注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="personBean" class="Pojo.Person" scope="prototype"/>
</beans>
1.2 有参构造注入
1.2.1 使用index指定

  使用index指定的是构造器的参数索引,从0开始。

<?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="personBean" class="Pojo.Person" scope="prototype">
        <constructor-arg index="0" value="张三"/>
        <constructor-arg index="1" value="40"/>
        <constructor-arg index="2" value=""/>
        <constructor-arg index="3" ref="personFatherBean"/>
    </bean>
    <!--注册father属性对应的Bean-->
    <bean id="personFatherBean" class="Pojo.Person" scope="prototype">
        <property name="name" value="张三"/>
        <property name="sex" value=""/>
        <property name="age" value="40"/>
    </bean>
</beans>
1.2.2 使用name指定

  使用name指定的是构造器的参数名。

<?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="personBean" class="Pojo.Person" scope="prototype">
        <constructor-arg name="name" value="张三"/>
        <constructor-arg name="age" value="40"/>
        <constructor-arg name="sex" value=""/>
        <constructor-arg name="father" ref="personFatherBean"/>
    </bean>
    <!--注册father属性对应的Bean-->
    <bean id="personFatherBean" class="Pojo.Person" scope="prototype">
        <property name="name" value="张三"/>
        <property name="sex" value=""/>
        <property name="age" value="40"/>
    </bean>
</beans>
1.2.3 使用type指定
<?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="personBean" class="Pojo.Person" scope="prototype">
        <constructor-arg type="java.lang.String" value="张三"/>
        <constructor-arg type="java.lang.Integer" value="18"/>
        <constructor-arg type="java.lang.String" value=""/>
        <constructor-arg type="Pojo.Person" ref="personFatherBean"/>
    </bean>
    <!--注册father属性对应的Bean-->
    <bean id="personFatherBean" class="Pojo.Person" scope="prototype">
        <property name="name" value="张三"/>
        <property name="sex" value=""/>
        <property name="age" value="40"/>
    </bean>
</beans>

注:建议同时使用typename进行指定,因为在赋值时都是value="值"的形式,这样便于区分。

2.set注入

  在上述的构造器方法构造当中,并不能处理null值的问题,而set注入本质是先空参构造,再调用实体类当中的set方法,故可解决此问题。

<?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="personBean" class="Pojo.Person" scope="prototype">
        <constructor-arg type="java.lang.String" value="张三"/>
        <constructor-arg type="java.lang.Integer" value="18"/>
        <constructor-arg type="java.lang.String" value=""/>
        <constructor-arg type="Pojo.Person" ref="personFatherBean"/>
    </bean>
    <bean id="personFatherBean" class="Pojo.Person" scope="prototype">
        <property name="name" value="张三"/>
        <property name="sex" value=""/>
        <property name="age" value="40"/>
        <!--
            若使用构造器构造来处理null对象:
                <constructor-arg name="name" value="张三"/>
                <constructor-arg name="age" value="40"/>
                <constructor-arg name="sex" value="男"/>
                <constructor-arg name="father" value=""/>
                则会报错:Cannot convert value of type 'java.lang.String' to required type 'Pojo.Person',即,实体类类型不能由String对象转换而来(基本数据类型及其包装类可转换).
            而若去掉<constructor-arg name="father" value=""/>,即:
                <constructor-arg name="name" value="张三"/>
                <constructor-arg name="age" value="40"/>
                <constructor-arg name="sex" value="男"/>
                则会报错,找不到对应的构造方法,但是property并不会出现这些错误,因为它的本质是先无参构造,再使用内置的set方法.
        -->
    </bean>
</beans>

事实上,还可以写为:

<!--基本数据类型及其包装类可由空字符串直接转换-->
<property name="name" value=""/>
<!--实体类对象的写法-->
<property name="father">
	<null/>
</property>
3.级联注入

  前文中对于实体类属性,使用的是ref的方式进行注入,在MyBatis中,直接通过"对象.属性"来为引用类型的属性进行赋值,但Spring中并不可行,因为此时该对象就是null,自然不能为其赋值,此时,可以定义内部Bean来为实体类属性赋值。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="Student" class="org.example.pojo.Student">
        <property name="SId" value="1001"/>
        <property name="SName" value="张三"/>
        <property name="clazz">
            <bean id="clazzInner" class="org.example.pojo.Clazz">
                <property name="clazzId" value="403"/>
                <property name="clazzName" value="四年三班"/>
            </bean>
            <!--内部bean只能在该标签内部使用,程序当中亦无法通过IOC容器获取-->
        </property>
    </bean>
</beans>
4.集合注入
4.1数组注入

Person.java实体类参数列表

package Pojo;

import java.lang.reflect.Array;

public class Person {
    private String Name;
    private Integer Age;
    private String Sex;
    private Array Hobbies;
    ...
}

配置文件:Beans.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 id="personBean" class="Pojo.Person">
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
        <property name="sex" value=""/>
        <property name="hobbies">
            <array>
                <value>游泳</value>
                <value>跑步</value>
                <value>唱歌</value>
            </array>
        </property>
    </bean>
</beans>
4.2 列表注入

Person.java实体类参数列表

package Pojo;

import java.lang.reflect.Array;

public class Person {
    private String Name;
    private Integer Age;
    private String Sex;
    private List<String>Hobbies;
    ...
}

配置文件:Beans.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 id="personBean" class="Pojo.Person">
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
        <property name="sex" value=""/>
        <property name="hobbies">
            <list>
                <value>游泳</value>
                <value>跑步</value>
                <value>唱歌</value>
            </list>
        </property>
    </bean>
</beans>
4.3 集合注入

Person.java实体类参数列表

package Pojo;

import java.util.Map;

public class Person {
    private String Name;
    private Integer Age;
    private String Sex;
    private Map<String,String>BankCard;
    ...
}

配置文件:Beans.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 id="personBean" class="Pojo.Person">
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
        <property name="sex" value=""/>
        <property name="bankCard">
            <map>
                <entry key="中国农业银行" value="123456654321"/>
                <entry key="中国建设银行" value="789654456789"/>
            </map>
        </property>
    </bean>
</beans>
5. 配置文件注入

  在(四)2.7当中写过配置文件的引入方式,在这里只举例,关于其他属性,还请看上文。
需要先注册解析器,使用标签:

<context:property-placeholder location="配置文件路径"/>

location表示引入配置文件的路径,多个之间用逗号分隔。
例,在Spring当中引入数据库连接参数配置文件(事实上,数据源本身就是一个对象,也可通过Spring来进行管理)
a.引入依赖:pom.xml

<!--MySQL驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.31</version>
</dependency>
<!--数据源-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.15</version>
</dependency>

b.数据库连接参数配置文件:jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/dataBase?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456

在数据源Bean中使用${键名}的方式可获取属性文件中的数据库连接参数。
c.配置文件:Beans.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: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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!--注册解析器-->
    <context:property-placeholder location="jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

d.测试类:MyTest.java

import com.alibaba.druid.pool.DruidDataSource;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取数据库源对象
        DruidDataSource dataSource=IOC.getBean(DruidDataSource.class);
        System.out.println(dataSource);
    }
}

在这里插入图片描述

6. 属性值包含特殊符号

  小于号等其他符号在XML文档中有特殊含义,故而不能作为字符串正常传入,有两种解决方案:

<!--解决方案一:使用XML实体代替,如,"&lt;"代表"<","&gt;"代表">"-->
<property name="expression" value="a &lt: b"/>
<!--解决方案二:使用CDATA节代替-->
<!--CDATA代表当前是纯文本数据,XML解析器看到CDATA就知道这是纯文本从而不会当做XML标签或属性来解析-->
<value><![CDATA[a<b]]></value>
<!--注意,CDATA不能写在属性中,只能写在标签中-->
7.自动装配

  自动装配就是指Spring容器在不使用<constructor-arg><property>标签的情况下,可以自动装配(autowire)相互协作的Bean之间的关联关系,将一个Bean注入其他BeanProperty 中,即,相当于扩展了ref的功能。
自动装配需要配置<bean>元素的autowire属性,其有五个值:

说明
no默认值,表示不使用自动装配,此时Bean依赖需要通过ref进行配置。
byName根据属性名自动装配,此时将检查容器并根据名字查找与属性完全一致的Bean,并将其与属性自动装配,。
byType若容器中存在一个与指定属性类型相同的Bean,则将与该属性自动装配,若存在多个该类型的Bean,则将抛出异常,并指出不能使用byType的方式进行自动装配,若没有找到相匹配的Bean,则什么都不发生,属性也不会被设置,这一情况可通过设置dependency-check="objects"让Spring抛出异常。
constructor类似于 byType,根据构造方法参数的数据类型,进行 byType 模式的自动装配。(类中构造函数的参数必须在配置文件中有相同的类型,否则将抛出异常)

注:自动装配只能对注册了Bean的实体类类型起作用,而简单的数据类型,如,inbooleanString 等,不起作用。

事实上,可在头文件中,一次性将IOC容器的所有Bean统一设置成自动装配:

<beans xmlns="http://www.springframework.org/schema/beans"
	....
	default-autowire="byName">
7.1 byName

  将属性的属性名作为Beanid来寻找相应的BeanIOC中不允许Beanid重复)。
实体类:Person.java

package Pojo;

public class Person {
    private String Name;
    private Integer Age;
    private String Sex;
    private Person Father;
    ...
}

配置文件:Beans.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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <bean id="personBean" class="Pojo.Person" autowire="byName">
        <property name="name" value="张三"/>
        <property name="sex" value=""/>
        <property name="age" value="18"/>
    </bean>
    <!--不能大写成Father,原因看下面的过程-->
    <bean id="father" class="Pojo.Person">
        <property name="name" value="张三父亲"/>
        <property name="sex" value=""/>
        <property name="age" value="40"/>
    </bean>
</beans>

测试类:MyTest.java

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

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取Bean对象
        System.out.println(IOC.getBean("personBean"));
    }
}

在这里插入图片描述
实际过程:

  1. 查找personBean其类中所有的set方法名,找到setFather后,获得将set去掉并且首字母小写的字符串,即father
  2. Spring容器中寻找是否有此字符串名称id的对象。
  3. 如果有,就取出注入;如果没有,就报空指针异常。
7.2 byType

  使用autowire byType首先需要保证同一类型的对象,在Spring容器中唯一。如果不唯一,会报不唯一的异常。

NoUniqueBeanDefinitionException

实体类:Person.java

package Pojo;

public class Person {
    private String Name;
    private Integer Age;
    private String Sex;
    private Phone phone;
    ...
}

实体类:Phone.java

package Pojo;

public class Phone {
    String Brand;
    ...
}

配置文件:Beans.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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <bean id="personBean" class="Pojo.Person" autowire="byType">
        <property name="name" value="张三"/>
        <property name="sex" value=""/>
        <property name="age" value="18"/>
    </bean>
    <bean id="phoneBean" class="Pojo.Phone">
        <property name="brand" value="Mac"/>
    </bean>
</beans>

测试类:MyTest.java

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

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取Bean对象
        System.out.println(IOC.getBean("personBean"));
    }
}

在这里插入图片描述

(五)Bean的管理:基于注解

  Spring本身就是一个轻代码而重配置的框架,配置比较繁重,影响开发效率,所以就在Spring中加入了注解,使用注解代替原来的xml配置文件,这样就可以简化配置,提升开发效率。
常用注解

注解说明
@Component使用在类上用于实例化Bean。
@Controller使用在web层的类上实例化Bean。
@Service使用在service层类上用于实例化Bean。
@Repository使用在dao层类上用于实例化Bean。
@Autowired根据属性类型进行自动装配。
@Qualifier根据Bean名称进行自动装配。
@Resource相当于@Autowired+@Qualifier
@Value用于字面量的自动装配。
@Bean标注将该方法的返回值存储到Spring容器。
@Scope标注Bean的作用域。
@PostConstruct使用在方法上标注该方法是Bean的初始化方法。
@PreDestroy使用在方法上标注该方法是Bean的销毁方法。
@Configuration用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解。
@ComponentScan用于指定Spring 在初始化容器时要扫描的包。
@PropertySource用于加载.properties 文件中的配置。
@Import用于导入其他配置类。

  需要注意的是,要想使用注解功能,就需要在配置文件当中开启注解扫描:

    <!--扫描组件,用于Spring扫描注解-->
    <context:component-scan base-package="org.example.Controller">
        <!--<context:exclude-filter type="" expression=""/>
                context:exclude-filter,排除标签:用于指出当前扫描过程中要排除掉组件.
                type:排除时所依据的原则,常用选项:annotation(注解类型)、assignable(类类型).
                    当type为annotation时,若要排除控制层,则expression="import org.springframework.stereotype.Controller;",即指出注解的全类名.
                    当type为assignable时,则expression需要指定目标类的全类名,如,"org.example.Controller.UserController".
            -->
        <!--<context:include-filter type="" expression=""/>
                注意,在使用之前需要在父级标签中指定参数:<context:component-scan base-package="org.example.Controller" use-default-filters="false">,
            因为use-default-filters默认为true,含义为默认在扫描所有标签的基础上...,对于exclude而言即为在扫描所有标签的情况下排除...,自然无影响,而对于include而言,
            就是使得其失效(因为default-filters中是所有,其包含了include-filter).
                context:include-filter,指定标签:用于指出当前扫描过程中要扫描组件的范围.
        -->

①基于注解注册Bean

1.@Component

  @Component用于定义Spring管理的Bean,被标注后的类会被自动识别为Bean并交给Spring进行管理。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
    String value() default "";
}

@Component只有一个属性,相当于<bean>标签中id的作用。
配置文件:Beans.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: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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!--组件扫描,扫描含有注解的类-->
    <context:component-scan base-package="Pojo"/>
</beans>

实体类:Cat.java

package Pojo;

import org.springframework.stereotype.Component;

@Component("cat")
public class Cat {
    public void say(){
        System.out.println("I'm a cat!");
    }
}

测试类:MyTest.java

import Pojo.Cat;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取Bean对象
        Cat cat=(Cat) IOC.getBean("cat");
        cat.say();
    }
}
2.@Repository、@Service与@Controller

  在Javaweb开发中,提供3@Component注解衍生注解(功能与@component一样,对于Spring而言三者并无区别,主要是便于开发人员区分)分别是:

注解说明
@Controller用于标注控制层,使在web层的类注册为Bean。
@Service用于标注服务层,使在service层的类注册为Bean。
@Repository用于标注数据访问层(DAO层),使用在dao层的类注册为Bean。

@Controller

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Service

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

@Repository

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.stereotype;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

可见,本质就是@Component标签的别称,故不再赘述。

②基于注解的自动装配

  为实现基于注解的自动装配,Spring提供了以下四个注解:

注解说明
@Autowired根据属性类型进行自动装配。
@Qualifier根据Bean名称进行自动装配。
@Resource相当于@Autowired+@Qualifier。
@Value用于字面量的自动装配。
1.@Autowired

  @Autowired用于根据属性类型进行自动装配,它等同于前文基于XML配置当中的default-autowire=byType,可以标注在属性上、方法上、注解上和构造器上,在一个类上标注@Service或者@Controller@Component@Repository注解之后,Spring的组件扫描会发现并将其初始化为Spring应用上下文中的Bean,当需要使用这个Bean时,可加上Autowired注解,之后会根据无参构造函数来创建相应的Bean对象,以玩成自动装配。
例:
手机类:Phone.java

package Pojo;

import org.springframework.stereotype.Component;

@Component("phoneBean")
public class Phone {
    public Phone() {
        System.out.println("Phone无参构造");
    }

    public void sayPhone(){
        System.out.println("I have a Phone!");
    }
}

实体类:Person.java

package Pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Person {
    private String Name;
    private Integer Age;
    private String Sex;
    @Autowired
    private Phone phone;
    ...
}

测试类:MyTest.java

import Pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
    }
}

在这里插入图片描述
可见,虽然并未获取Person对象,但还是被Spring通过无参构造完成了初始化。

  @Autowired只有一个required属性:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.beans.factory.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

@Autowired注解的required参数默认是true,表示开启自动装配,有些时候我们不想使用自动装配功能,可以将该参数设置成false,此时即为null值。

1.1属性

  @Autowired标注在属性上后,Spring会自动寻找已注册的、与该属性类型相同的类进行注入。
配置文件:Beans.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: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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!--组件扫描,扫描含有注解的类-->
    <context:component-scan base-package="Pojo"/>
</beans>

实体类:Phone.java

package Pojo;

import org.springframework.stereotype.Component;

@Component("phone")
public class Phone {
    public void sayPhone(){
        System.out.println("I have a Phone!");
    }
}

实体类:Person.java

package Pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("person")
public class Person {
    private String Name;
    private Integer Age;
    private String Sex;
    @Autowired
    private Phone phone;
    ...
}

测试类:MyTest.java

import Pojo.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取Bean对象
        Person person=(Person) IOC.getBean("person");
        person.getPhone().sayPhone();
    }
}

在这里插入图片描述

1.2 方法

  可以在JavaBean中的方法(一般写在setter方法上)上使用@Autowired注解,当Spring遇到一个使用@Autowiredsetter方法上,它会在方法中执行byType自动装配。
配置文件:Beans.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: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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!--组件扫描,扫描含有注解的类-->
    <context:component-scan base-package="Pojo"/>
</beans>

文本编辑器实体类:TextEditor.java

package Pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("textEditorBean")
public class TextEditor {
    //文本编辑器类
    //包含一个拼写检查程序对象
    private SpellChecker spellChecker;
    @Autowired
    public void setSpellChecker(SpellChecker spellChecker) {
        this.spellChecker = spellChecker;
        System.out.println("调用了setSpellChecker方法");
    }

    public SpellChecker getSpellChecker() {
        return spellChecker;
    }
    public void checkSpelling(){
        spellChecker.checkSpelling();
    }
}

拼写检查程序实体类:SpellChecker.java

package Pojo;

import org.springframework.stereotype.Component;

@Component("spellCheckerBean")
public class SpellChecker {
    //拼写检查程序
    public SpellChecker() {
        System.out.println("调用了SpellChecker无参构造");
    }
    public void checkSpelling(){
        System.out.println("检查拼写...");
    }
}

测试类:MyTest.java

import Pojo.TextEditor;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取文本编辑器对象,此处已经调用了SpellChecker无参构造和setSpellChecker方法,完成了自动装配.
        TextEditor editor=(TextEditor) IOC.getBean("textEditorBean");
        editor.checkSpelling();
    }
}

在这里插入图片描述

1.3 构造器

  可以在JavaBean中的构造器方法上使用@Autowired注解,Spring会自动寻找已注册的、与该参数类型相同的类对象进行注入。
配置文件:Beans.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: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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!--组件扫描,扫描含有注解的类-->
    <context:component-scan base-package="Pojo"/>
</beans>

文本编辑器实体类:TextEditor.java

package Pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("textEditorBean")
public class TextEditor {
    //文本编辑器类
    //包含一个拼写检查程序对象
    private SpellChecker spellChecker;

    public TextEditor() {
        System.out.println("执行TextEditor无参构造");
    }
    @Autowired
    public TextEditor(SpellChecker spellChecker) {
        this.spellChecker = spellChecker;
        System.out.println("执行TextEditor有参构造");
    }
    public void checkSpelling(){
        spellChecker.checkSpelling();
    }
}

拼写检查程序实体类:SpellChecker.java

package Pojo;

import org.springframework.stereotype.Component;

@Component("spellCheckerBean")
public class SpellChecker {
    //拼写检查程序
    public SpellChecker() {
        System.out.println("调用了SpellChecker无参构造");
    }
    public void checkSpelling(){
        System.out.println("检查拼写...");
    }
}

测试类:MyTest.java

import Pojo.TextEditor;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        //获取文本编辑器对象,此处已经调用了SpellChecker无参构造和setSpellChecker方法,完成了自动装配.
        TextEditor editor=(TextEditor) IOC.getBean("textEditorBean");
        editor.checkSpelling();
    }
}

在这里插入图片描述
可见,此时在初始化Bean对象时执行的是有参构造函数,若将@Autowired注解去掉,则会执行无参构造函数,且,会在checkSpelling方法中报出空指针异常。

1.4 集合

  @Autowired注解可用在集合属性上,前提是集合泛型的子类对象/实现类对象都需要已经交给ioc管理。之后ioc容器会初始化将这些被管理的实现类对象装入集合,注入到当前类中,注入map对象,key是注入类的namevalue是注入类对象,按照那么可以在map中获取到目标对象进行业务操作,注入set/list使用时候就需要按照字节码对象进行判断使用,这样有利于统一管理某个业务实现对象。
手机接口:Phone.java

package Pojo;

public interface Phone {
    void getBrand();
}

华为手机实现类:HuaweiPhone.java

package Pojo;

import org.springframework.stereotype.Component;

@Component("huaweiPhone")
public class HuaweiPhone implements Phone{
    @Override
    public void getBrand() {
        System.out.println("Huawei!");
    }
}

苹果手机实现类:ApplePhone.java

package Pojo;

import org.springframework.stereotype.Component;

@Component("applePhone")
public class ApplePhone implements Phone{
    @Override
    public void getBrand() {
        System.out.println("Apple!");
    }
}

手机商店类:PhoneStore.java

package Pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Component("phoneStore")
public class PhoneStore {
    @Autowired
    private List<Phone>phoneList;
    @Autowired
    private Set<Phone>phoneSet;
    @Autowired
    private Map<String,Phone>phoneMap;

    public PhoneStore() {
    }

    public List<Phone> getPhoneList() {
        return phoneList;
    }

    public void setPhoneList(List<Phone> phoneList) {
        this.phoneList = phoneList;
    }

    public Set<Phone> getPhoneSet() {
        return phoneSet;
    }

    public void setPhoneSet(Set<Phone> phoneSet) {
        this.phoneSet = phoneSet;
    }

    public Map<String, Phone> getPhoneMap() {
        return phoneMap;
    }

    public void setPhoneMap(Map<String, Phone> phoneMap) {
        this.phoneMap = phoneMap;
    }
}

测试类:MyTest.java

import Pojo.PhoneStore;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        PhoneStore phoneStore=(PhoneStore) IOC.getBean("phoneStore");
        System.out.println(phoneStore.getPhoneList());
        System.out.println(phoneStore.getPhoneSet());
        System.out.println(phoneStore.getPhoneMap());
    }
}

在这里插入图片描述

2.@Qualifier

  @Autowired默认是根据类型进行注入的,因此如果有多个类型一样的Bean候选者,则需要限定其中一个候选者,否则将抛出异常。
  而@Qualifier注解一般作为Autowired的修饰使用,使得能根据当前注入到IOC中的Bean的唯一标识(id)来获取Bean并进行注入,这种组合使用使得注入更加精确、更加多元(可注册多个同类型不同idBean供选择),且不用担心是否能够匹配成功的问题。
  @Qualifier只有一个value参数,用于指定Beanid值。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.beans.factory.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
    String value() default "";
}
2.1 类

  @Qualifier标注在类上时,可用于指定该类所注册Bean的唯一标识id,但需要提前先将该类注册为Bean(相当于修改<bean>id值)。
配置文件:Beans.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: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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!--组件扫描,扫描含有注解的类-->
    <context:component-scan base-package="Pojo"/>
</beans>

实体类:TextEditor.java

package Pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("textEditorBean")
public class TextEditor {
    //文本编辑器类
    //包含一个拼写检查程序对象
    private SpellChecker spellChecker;

    public TextEditor() {
        System.out.println("执行TextEditor无参构造");
    }
    @Autowired
    public TextEditor(SpellChecker spellChecker) {
        this.spellChecker = spellChecker;
        System.out.println("执行TextEditor有参构造");
    }
    public void checkSpelling(){
        spellChecker.checkSpelling();
    }
}

此时,在使用到该Bean的地方就可以直接根据唯一标识(id)从IOC容器中获取。

2.2 属性

  当使用在属性上的时候,需要和@Autowired配合使用,否则,无法实现自动装配,之后,就可以直接通过Bean的唯一标识(id)进行自动装配了。
配置文件:Beans.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: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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!--组件扫描,扫描含有注解的类-->
    <context:component-scan base-package="Pojo"/>
</beans>

实体类:TextEditor.java

package Pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("textEditorBean")
public class TextEditor {
    //文本编辑器类
    //包含一个拼写检查程序对象
    @Autowired
    @Qualifier("spellCheckerBean")
    private SpellChecker spellChecker;

    public TextEditor() {
        System.out.println("执行TextEditor无参构造");
    }
    public TextEditor(SpellChecker spellChecker) {
        this.spellChecker = spellChecker;
        System.out.println("执行TextEditor有参构造");
    }
    public void checkSpelling(){
        spellChecker.checkSpelling();
    }
}
2.3 集合

  当使用在集合上时,@Qualifier起到了一个筛选的作用,只有加了@Qualifier注解的、且符合继承关系的Bean才会被收集注入到本集合。

//@Qualifier在方法上时,遇见自动装配中有@Qualifier不会被过滤
@Qualifier
@Bean
public User user1() {
  return new User("zhangsan",18) ;
}
 
@Bean
public User user2() {
  return new User("lisi",17);
}
 
 
//只有user1会被自动注入到users,user2因为没有@Qualifier注解而导致被过滤掉了
@Resource
@Qualifier
private List<User> users = Collections.emptyList() ;
3.@Resource

  @Resource注解是Java自己的注解,在使用@Resource注解之前需要先导入依赖:

    <dependencies>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>

  @Resource@Autowired都可以用来装配Bean,都可以用于字段或setter方法,@Autowired默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false
  @Resource默认按名称装配,当找不到与名称匹配的bean时才按照类型进行装配,相当于是@Autowired+@Qualifier。名称可以通过name属性指定,如果没有指定name属性,当注解写在字段上时,默认取字段名,当注解写在setter方法上时,默认取属性名进行装配。
  其相当于@Autowired+@Qualifier,故而此处不再举例子,看上文@Qualifier即可。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package javax.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Resources.class)
public @interface Resource {
    String name() default "";

    String lookup() default "";

    Class<?> type() default Object.class;

    AuthenticationType authenticationType() default Resource.AuthenticationType.CONTAINER;

    boolean shareable() default true;

    String mappedName() default "";

    String description() default "";

    public static enum AuthenticationType {
        CONTAINER,
        APPLICATION;

        private AuthenticationType() {
        }
    }
}

  @Resource有两个属性是比较重要的,分别是nametypeSpring@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。默认按name进行注入。

4.@Value

  @Value用于注入属性,包括普通属性、外部文件、表达式计算结果、URL等。

package org.springframework.beans.factory.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
    String value();
}

只有value属性,用于指定注入量的值或表达式。

4.1普通属性

  @Value可以注入一些字段的普通属性,并且会自动进行类型转换。

@Repository
public class ConnectionPool {
    @Value("jdbc:mysql://localhost:3306/test")
    private String url;
    @Value("com.mysql.jdbc.Driver")
    private String driveName;
    @Value("Scott")
    private String userName;
    @Value("10")
    private int no;
}
4.2配置文件

  @Value可以注入外部配置文件中的内容,需要先使用@PropertySource注解引入外部配置文件,且,@PropertySource注解有个value属性,字符串数组类型,可以用来指定多个配置文件的路径,再使用${}来获取属性值:

package Pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component("ConnectionPool")
@PropertySource({"classpath:jdbc.properties"})
public class ConnectionPool {
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Value("${jdbc.maxConnection}")
    private Integer maxConnection;
}

可在@Value注解当中设置默认值,如:

    @Value("${jdbc.username:root}")
    private String username;

表示当配置文件中不含有jdbc.username时,将username属性赋值为root

4.3其他属性

  在注入表达式、URL、操作系统属性与Bean对象属性时需要使用#{}来进行注入。
SpEl表达式注入
  SpEl(Spring Expression Language ),是Spring的表达式语言。

    @Value("#{T(java.lang.Runtime).getRuntime().availableProcessors() * 2}")
    private int maxCons;

URL注入

    @Value("https://www.baidu.com/")
    private URL homePage;

注入后会被识别为URL地址。

Bean对象属性

//OtherBean.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

//其他bean,自定义名称为 myBeans
@Component("myBeans")
public class OtherBean {

    @Value("OtherBean的NAME属性")
    private String name;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
//ValueController.java
import cn.wideth.PdaAndIpadApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest()
@ContextConfiguration(classes = PdaAndIpadApplication.class)
public class ValueController {

    @Value("#{myBeans.name}")
    private String fromAnotherBean;

    @Test
    public void getValue(){

        System.out.println(fromAnotherBean);
    }
}

③Bean的配置

  Spring提供了@Configuration@Bean@Profile@Scope@Lazy@DependsOn@Primary标签用于配置Bean

1.@Configuration

  @Configuration用于声明一个类为配置类,用于取代bean.xml配置文件注册bean对象,并声明这是一个配置类,相当于<Beans>标签。
  @Configuration注解常常一起搭配使用的注解有:

  • @Bean:等价于Spring中的bean标签用于注册bean对象,内部有一些初始化、销毁的属性。
  • @Scope:用于声明该bean的作用域。
  • @Lazy:用于标记该bean是否开启懒加载。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;

    boolean enforceUniqueMethods() default true;
}
2.@Bean

  @Bean是用于方法上的注解,作用与<bean>类似,用于声明本方法会产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中,其常常与@Component@Configuration配合使用,起到取代配置文件的作用。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}

ElementType.METHOD ElementType.ANNOTATION_TYPE可见,@Bean可用于方法、注解上。

属性说明
valuename属性的别名,是默认属性。
name此Bean的一个或多个别名,当未指定时,默认用方法名作为Bean的名称。
initMethodbean的初始化之前的执行方法。
destroyMethod在bean销毁时会自动执行该方法.

例:

//MyBean.java
public class MyBean {
 
    public MyBean(){
        System.out.println("MyBean Initializing");
    }
 
    public void init(){
        System.out.println("Bean 初始化方法被调用");
    }
 
    public void destroy(){
        System.out.println("Bean 销毁方法被调用");
    }
}
//AppConfig.java
@Configuration
public class AppConfig {
    @Bean(name="myBean",initMethod = "init", destroyMethod = "destroy")
    public MyBean myBean(){
        return new MyBean();
    }
}
//测试类:MyTest.java
import Pojo.MyBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        MyBean myBean=(MyBean) IOC.getBean("myBean");
    }
}

例:

//MyBean.java
package Pojo;

public class MyBean {

    public MyBean(){
        System.out.println("Bean 构造方法被调用");
    }

    public void init(){
        System.out.println("Bean 初始化方法被调用");
    }

    public void destroy(){
        System.out.println("Bean 销毁方法被调用");
    }
}
//AppConfig.java
package Pojo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean(name="myBean",initMethod = "init", destroyMethod = "destroy")
    public MyBean myBean(){
        return new MyBean();
    }
}
//MyTest.java
import Pojo.MyBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        MyBean myBean=(MyBean) IOC.getBean("myBean");
        myBean.destroy();
    }
}

在这里插入图片描述

3.@Scope
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
    @AliasFor("scopeName")
    String value() default "";

    @AliasFor("value")
    String scopeName() default "";

    ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}

  @Scope用于配置Bean实例的作用域对象,有如下几种作用域:

  • singleton(默认):单实例模式。
  • prototype:多实例模式。
  • request:同一次请求模式,指每一次HTTP请求都会产生一个新的Bean,同时该Bean仅在当前HTTP请求中有效。
  • session:同一会话模式,每一次HTTP请求都会产生一个新的Bean,同时该Bean仅在当前HTTP session内有效。
//MyBean.java
package Pojo;

public class MyBean {

    public MyBean(){
        System.out.println("Bean 构造方法被调用");
    }

    public void init(){
        System.out.println("Bean 初始化方法被调用");
    }

    public void destroy(){
        System.out.println("Bean 销毁方法被调用");
    }
}
//AppConfig.java
package Pojo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class AppConfig {
    @Bean(name="myBean",initMethod = "init", destroyMethod = "destroy")
    @Scope(value="prototype")
    public MyBean myBean(){
        return new MyBean();
    }
}
//MyTest.java
import Pojo.MyBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        //获取IOC容器
        ApplicationContext IOC=new ClassPathXmlApplicationContext("Beans.xml");
        MyBean myBean1=(MyBean) IOC.getBean("myBean");
        MyBean myBean2=(MyBean) IOC.getBean("myBean");
        System.out.println(myBean1);
        System.out.println(myBean2);
    }
}

在这里插入图片描述

4.@Lazy
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {
    boolean value() default true;
}

  Spring在应用程序上下文启动时去创建所有的单例Bean对象,而@Lazy注解可以延迟加载Bean对象,即在使用时才去初始化。
  故而,@Lazy注解, 一是可以减少SpringIOC容器启动时的加载时间, 二是可以解决Bean的循环依赖问题

三、AOP模块

前言

  在面向切面编程当中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。
  以获取某个活动的数据、根据条件发放奖励为例,在实现这两种功能时,都需要实现以下功能:
请添加图片描述
其中,在正常的逻辑编写当中,红框内的代码是冗余的,需要在所有需要使用到该功能的地方进行代码的复制粘贴,故而此时提出一种公共方法,使得每个接口都能调用这个接口,减少代码的冗余,且当红框内的代码需要修改时,只需修改该公共方法接口即可,降低了代码的耦合度。
请添加图片描述
  虽然情况得到了缓解,但是每个接口都需要调用这个方法,于是就有了切面的概念,只需要将方法注入到接口调用的某个地方(切点)即可。

请添加图片描述
此时,接口就只需要关注具体的业务,而不需要关注其他非该接口关注的逻辑处理。

(一)简介

  AOP(Aspect Oriented Programming),意为面向切面编程,以预编译方式和运行期间动态代理方式实现在不修改源代码的情况下将程序统一添加额外功能。其是OOP(Object Oriented Programming,面向对象编程)的一种延伸,前者是纵向(继承)编程,而AOP是横向(前面)编程。利用AOP,可将业务逻辑的各个部分进行隔离,从而降低各部分之间的耦合度。
  优点在于,对于一个切面(对象与对象之间、方法与方法之间、模块与模块之间都是一个个切面)中的内容,其会有一个统一的接口来与其他切面进行连接,故而只需关注该切面的具体业务。

四、JDBC与事务

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值