Spring看这两篇就够了(上篇)

Spring看这两篇就够了(上篇)

一、Spring初识

1.什么是框架(framework)

​ 框架就是一些类和接口的集合,通过这些类和接口协调来完成一系列的程序实现。JAVA框架可以分为三层:表示层,业务层和物理层。框架又叫做开发中的半成品,它不能提供整个WEB应用程序的所有东西,但是有了框架,我们就可以集中精力进行业务逻辑的开发而不用去关心它的技术实现以及一些辅助的业务逻辑。大家熟知的Structs和Spring就是表示层和业务层框架的强力代表。(说的太官方了)

通俗点讲就是:

​ 框架就是某些个人或者组织定义了一系列的类或者接口,提前定义好了一些实现,用户可以在这些类和接口的基础之上,使用这些类来迅速的形成某个领域或者某个行业的解决方案,简化开发的过程,提高开发的效率。就好比:你要盖一座房子,先把柱子,房梁等先建设好,然后只需要向房子中填充就可以了,可以按照自己的需求进行设计,其实我们做的项目、系统都是类似的方式,如果所有的代码全部都需要自己实现,那么这个工程就太庞大了,所以可以先创建出一些基础的模板框架,开发人员只需要按照自己的需求向架子中填充内容,完成自己独特需求即可,这就是框架存在的意义。其实我们之前定义的简单的工具类这些东西也是类似的原理,只不过比较单一简单而已,因此,在现在的很多项目系统开发的过程中都是利用框架进行开发。

2.架构发展历程

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

image-20231218204410671

  • 单一应用架构

​ 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

  • 垂直应用架构

​ 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

  • 分布式服务架构

​ 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

  • 流动计算架构

​ 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

Java主流架构技术演变之路

1、Servlet+JSP+JavaBean

image-20231218204547134

2、MVC三层架构

image-20231218204610554

​ 3、使用EJB进行应用的开发,但是EJB是重量级框架(在使用的时候,过多的接口和依赖,侵入性强),在使用上比较麻烦

​ 4、Struts1/Struts2+Hibernate+Spring(SSH)

​ 5、SpringMVC+Mybatis+Spring(SSM)

​ 6、SpringBoot开发,约定大于配置

3.Spring介绍

官网地址:https://spring.io/projects/spring-framework#overview

spring5中文手册:https://github.com/DocsHome/spring-docs/blob/master/SUMMARY.md

压缩包下载地址:https://repo.spring.io/list/libs-snapshot-local/org/springframework/spring/

源码地址:https://github.com/spring-projects/spring-framework

Spring makes it easy to create Java enterprise applications. It provides everything you need to embrace the Java language in an enterprise environment, with support for Groovy and Kotlin as alternative languages on the JVM, and with the flexibility to create many kinds of architectures depending on an application’s needs. As of Spring Framework 5.1, Spring requires JDK 8+ (Java SE 8+) and provides out-of-the-box support for JDK 11 LTS. Java SE 8 update 60 is suggested as the minimum patch release for Java 8, but it is generally recommended to use a recent patch release.

Spring supports a wide range of application scenarios. In a large enterprise, applications often exist for a long time and have to run on a JDK and application server whose upgrade cycle is beyond developer control. Others may run as a single jar with the server embedded, possibly in a cloud environment. Yet others may be standalone applications (such as batch or integration workloads) that do not need a server.

翻译:

Spring Framework 是一个轻量级的解决方案,可以一站式构建企业级应用。然而,Spring 是模块化的,允许你使用的你需要的部分,而不必把其余带进来。你可以在任何框架之上去使用IOC容器,但你也可以只使用 [Hibernate 集成代码](https://waylau.gitbooks.io/spring-framework-4-reference/IV. Data Access/15.3. Hibernate.md) 或 [JDBC 抽象层](https://waylau.gitbooks.io/spring-framework-4-reference/IV. Data Access/14.1. Introduction to Spring Framework JDBC.md)。Spring Framework 支持声明式事务管理,通过 RMI 或 Web 服务远程访问你的逻辑,并支持多种选择持久化你的数据。它提供了一个全功能的 [MVC 框架](https://waylau.gitbooks.io/spring-framework-4-reference/V. The Web/17.1. Introduction to Spring Web MVC framework.md),使您能够将 AOP 透明地集成到您的软件。

Spring 的设计是非侵入性的,也就是说你的领域逻辑代码一般对框架本身无依赖性。在你的集成层(如数据访问层),在数据访问技术和 Spring 的库一些依赖将存在。然而,它应该很容易从你的剩余代码中q分离这些依赖。

通俗点讲就是:

​ Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。

Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。

Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)

spring并不是一门单一的技术,它实际上是一个大家族,打开spring官网可以看到其包含的所有技术。

Spring发展到今天已经形成了一种开发的生态圈,Spring提供了若干个项目,每个项目用于完成特定的功能。

image-20231218205538253

额外需要重点关注Spring FrameworkSpringBootSpringCloud:

image-20231218205814342

  • Spring Framework:Spring框架,是Spring中最早最核心的技术,也是所有其他技术的基础。
  • SpringBoot:Spring是来简化开发,而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发。
  • SpringCloud:这个是用来做分布式之微服务架构的相关开发。

除了上面的这三个技术外,还有很多其他的技术,也比较流行,如SpringData,SpringSecurity等,这些都可以被应用在我们的项目中。我们今天所学习的Spring其实指的是Spring Framework

4.了解Spring发展史

image-20231218205942827

Spring发展史

  • IBM(IT公司-国际商业机器公司)在1997年提出了EJB思想,早期的JAVAEE开发大都基于该思想。
  • Rod Johnson(Java和J2EE开发领域的专家)在2002年出版的Expert One-on-One J2EE Design and Development,书中有阐述在开发中使用EJB该如何做。
  • Rod Johnson在2004年出版的Expert One-on-One J2EE Development without EJB,书中提出了比EJB思想更高效的实现方案,并且在同年将方案进行了具体的落地实现,这个实现就是Spring1.0。
  • 随着时间推移,版本不断更新维护,目前最新的是Spring5
    • Spring1.0是纯配置文件开发
    • Spring2.0为了简化开发引入了注解开发,此时是配置文件加注解的开发方式
    • Spring3.0已经可以进行纯注解开发,使开发效率大幅提升,我们的课程会以注解开发为主
    • Spring4.0根据JDK的版本升级对个别API进行了调整
    • Spring5.0已经全面支持JDK8,现在Spring最新的是5系列所以建议大家把JDK安装成1.8版

5.Spring系统架构

  • Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基。
  • Spring Framework的发展也经历了很多版本的变更,每个版本都有相应的调整

image-20231218210321015

Spring Framework的5版本目前没有最新的架构图,而最新的是4版本,所以接下来主要研究的是4的架构图

image-20231218210345676

(1)核心层

  • Core Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块

(2)AOP层

  • AOP:面向切面编程,它依赖核心层容器,目的是在不改变原有代码的前提下对其进行功能增强
  • Aspects:AOP是思想,Aspects是对AOP思想的具体实现

(3)数据层

  • Data Access:数据访问,Spring全家桶中有对数据访问的具体实现技术
  • Data Integration:数据集成,Spring支持整合其他的数据层解决方案,比如Mybatis
  • Transactions:事务,Spring中事务管理是Spring AOP的一个具体实现

(4)Web层

  • 这一层的内容将是SpringMVC框架

(5)Test层

  • Spring主要整合了Junit来完成单元测试和集成测试、

6.使用Spring的优点

​ 1、Spring通过DI、AOP和消除样板式代码来简化企业级Java开发

​ 2、Spring框架之外还存在一个构建在核心框架之上的庞大生态圈,它将Spring扩展到不同的领域,如Web服务、REST、移动开发以及NoSQL

​ 3、低侵入式设计,代码的污染极低

​ 4、独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺

​ 5、Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦

​ 6、Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式处理,从而提供了更好的复用

​ 7、Spring的ORM和DAO提供了与第三方持久层框架的的良好整合,并简化了底层的数据库访问

​ 8、Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部

​ 9、任何一个语言或者任何一个框架想要立于不败之地,那么很重要的就是它的生态。

7.Spring核心概念

1.IOC(Inversion of Control)控制反转

使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。

2.AOP(Aspect Oriented Programming)面向切面编程

是一种软件开发范式,它致力于通过横切关注点(cross-cutting concerns)的模块化来增强代码的可维护性和可重用性。横切关注点是那些影响应用程序的多个模块的功能,例如日志、事务管理、安全性等。

AOP 的核心思想是将这些横切关注点从主要业务逻辑中分离出来,将它们封装为独立的模块,然后通过切面(Aspect)将这些模块注入到应用程序的执行流程中。在 AOP 中,切面可以看作是一个特殊的类,它定义了横切关注点的行为,这些行为会在程序执行的特定点(切点)被执行。

3.DI(Dependency Injection)依赖注入

在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入,业务层要用数据层的类对象,以前是自己new的,现在自己不new了,靠别人[外部其实指的就是IOC容器]来给注入进来,这种思想就是依赖注入

4.Bean(Java Bean)

容器中所存放的一个个对象就叫Bean或Bean对象

5.容器

Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部",容器内存放Bean对象

二、IOC入门案例

1.新建一个Maven项目SSMDemo

image-20231219101926585

2.引入依赖

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

3.添加案例中需要的类

创建UserService,USerServiceImpl,UserDao和UserDaoImpl四个类

public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("user dao save ...");
    }
}

public interface UserDao {
      public void save();
}

public class UserServiceImpl implements UserService {
    public void save() {
        System.out.println("user service save ...");
    }
}

public interface UserService {
    public void save();
}

4.添加spring配置文件applicationContext.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="userDao" class="com.ts.dao.impl.UserDaoImpl"/>
    <bean id="bookService" class="com.ts.service.impl.UserServiceImpl"/>
</beans>

5.获取IOC容器

使用Spring提供的接口完成IOC容器的创建,创建Main类,编写main方法

public class Main {
    public static void main(String[] args) {
        //获取IOC容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
}

7.从容器中获取对象进行方法调用

public class Main {
    public static void main(String[] args) {
        //获取IOC容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) context.getBean("userDao");
        UserService userService = (UserService) context.getBean("userService");
//        userDao.save();
        userService.save();
    }
}

8.运行程序

测试结果为:

image-20231219110029610

Spring的IOC入门案例已经完成,但是在UserServiceImpl的类中依然存在USerDaoImpl对象的new操作,它们之间的耦合度还是比较高,这块该如何解决,就需要用到下面的DI:依赖注入

三、DI入门案例

1.去除代码中的new

在UserServiceImpl类中,删除业务层中使用new的方式创建的dao对象

public class UserServiceImpl implements UserService {
	private UserDao userDao ;
	public void save() {
		System.out.println("user service save ...");
		userDao.save();
	}
}

2.为属性提供setter方法

在UserServiceImpl类中,为USer Dao提供setter方法

public class UserServiceImpl implements UserService {
    private UserDao userDao ;
        public void save() {
            System.out.println("user service save ...");
            userDao.save();
        }

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

3.修改配置完成注入

  <bean id="userService" class="com.ts.service.impl.UserServiceImpl">
        <!--配置service与dao的关系-->
        <!--property标签表示配置当前bean的属性
        		name属性表示配置哪一个具体的属性
        		ref属性表示参照哪一个bean
		-->
        <property name="userDao" ref="userDao"/>
    </bean>

注意:配置中的两个userDao的含义是不一样的

  • name="userDao"中userDao的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前面加set找对应的setUserDao()方法进行对象注入
  • ref="userDao"中userDao的作用是让Spring能在IOC容器中找到id为userDao的Bean对象给userService进行注入
  • 综上所述,对应关系如下:

image-20231219122536949

四、IOC配置详解

1.容器概述

ApplicationContext是Spring IoC容器实现的代表,它负责实例化,配置和组装Bean。容器通过读取配置元数据获取有关实例化、配置和组装哪些对象的说明 。配置元数据可以使用XML、Java注解或Java代码来呈现。它允许你处理应用程序的对象与其他对象之间的互相依赖关系。

2.bean基础配置

2.1.bean基础配置(id与class)

bean标签的功能、使用方式以及id和class属性的作用

类别描述
名称bean
类型标签
所属beans标签
功能定义Spring核心容器管理的对象
格式<beans><bean></bean> </beans>
属性列表id:bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id值唯一
class:bean的类型,即配置的bean的全路径类名
范例<bean id=“userDao” class=“com.ts.dao.impl.UserDaoImpl”/>
2.2.bean的name属性
类别描述
名称name
所属bean标签
功能定义bean的别名,可定义多个,使用都好、分号、空格分隔
范例<bean id=“userDao” name=“userDao2,userDao3 userDao4” class=“com.ts.dao.impl.UserDaoImpl”/>

1.配置别名

打开spring的配置文件applicationContext.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="userDao" name="userDao2,userDao3 userDao4" class="com.ts.dao.impl.UserDaoImpl"/>
    <bean id="userService" class="com.ts.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

2.根据名称容器中获取bean对象

public class Main {
    public static void main(String[] args) {
        //获取IOC容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) context.getBean("userDao2");
        UserService userService = (UserService) context.getBean("userService");
//        userDao.save();
        userService.save();
    }
}

3.运行结果

image-20231219131000323

2.3 bean作用范围scope配置
类别描述
名称name
所属bean标签
功能定义bean的作用范围,可选择单例singleton 多例prototype
范例scope="prototype|singleton "

配置bean为非单例

在Spring配置文件中,配置scope属性来实现bean的非单例创建

  • 在Spring的配置文件中,修改<bean>的scope属性

    <bean id="userDao" name="userDao2,userDao3 userDao4" class="com.ts.dao.impl.UserDaoImpl" scope=""/>
    
  • 将scope设置为singleton

    <bean id="userDao" name="userDao2,userDao3 userDao4" class="com.ts.dao.impl.UserDaoImpl" scope="singleton"/>
    
  • 运行Main,打印看结果

image-20231219131715586

  • 将scope设置为prototype
<bean id="userDao" name="userDao2,userDao3 userDao4" class="com.ts.dao.impl.UserDaoImpl" scope="prototype"/>
  • 运行Main,打印看结果

image-20231219131949417

scope使用后续思考

介绍完scope属性以后,我们来思考几个问题:

  • 为什么bean默认为单例?
    • bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
    • bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
  • bean在容器中是单例的,会不会产生线程安全问题?
    • 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,
    • 因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
    • 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,
    • 因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
  • 哪些bean对象适合交给容器进行管理?
    • 表现层对象
    • 业务层对象
    • 数据层对象
    • 工具对象
  • 哪些bean对象不适合交给容器进行管理?
    • 封装实例的域对象,因为会引发线程安全问题,所以不适合。

3.bean的实例化

对象已经能交给Spring的IOC容器来创建了,但是容器是如何来创建对象的呢?

就需要研究下bean的实例化过程,在这块内容中主要解决两部分内容,分别是

  • bean是如何创建的
  • 实例化bean的三种方式,构造方法,静态工厂实例工厂

在讲解这三种创建方式之前,我们需要先确认一件事:

bean本质上就是对象,对象在new的时候会使用构造方法完成,那创建bean也是使用构造方法完成的。

基于这个知识点出发,我们来验证spring中bean的三种创建方式,

3.1构造方法实例化

增加构造函数

  public UserDaoImpl() {
        System.out.println("user dao constructor is running ....");
    }

执行程序Main运行结果如下:

image-20231219133335480

发现控制台有打印构造函数中的输出,说明Spring容器在创建对象的时候也走的是构造函数

将构造函数改成private,运行程序,依然能执行成功,说明内部走的依然是构造函数,能访问到类中的私有构造方法,显而易见Spring底层用的是反射

image-20231219133516993

image-20231219133525378

再继续将构造函数改为如下:

  private UserDaoImpl(int i) {
        System.out.println("user dao constructor is running ....");
    }

运行程序发现:

image-20231219133652562

程序会报错,说明Spring底层使用的是类的无参构造方法。

3.2静态工厂实例化

在spring的配置文件application.properties中添加以下内容:

<bean id="orderDao" class="com.ts.dao.factory.OrderDaoFactory" factory-method="getOrderDao"/>

class:工厂类的类全名

factory-mehod:具体工厂类中创建对象的方法名

3.3实例工厂
<bean id="userFactory" class="com.ts.dao.factory.OrderDaoFactory"/>
<bean id="orderDao" factory-method="getOrderDao" factory-bean="userFactory"/>

Spring为了简化这种配置方式就提供了一种叫FactoryBean的方式来简化开发。

工厂类需要实现FactoryBean接口,泛型为想要实现哪个类型的bean就是哪个

public class OrderDaoFactoryBean implements FactoryBean<OrderDao> {
    public OrderDao getObject() throws Exception {
        return new OrderDaoImpl();
    }

    public Class<?> getObjectType() {
        return OrderDao.class;
    }
}

配置文件

<bean id="orderDao" class="com.ts.dao.factory.OrderDaoFactoryBean"/>

这种方式在Spring去整合其他框架的时候会被用到,

查看源码会发现,FactoryBean接口其实会有三个方法,分别是:

T getObject() throws Exception;

Class<?> getObjectType();

default boolean isSingleton() {
		return true;
}

方法一:getObject(),被重写后,在方法中进行对象的创建并返回

方法二:getObjectType(),被重写后,主要返回的是被创建类的Class对象

方法三:没有被重写,因为它已经给了默认值,从方法名中可以看出其作用是设置对象是否为单例,默认true,从意思上来看,我们猜想默认应该是单例,如何来验证呢?

思路很简单,就是从容器中获取该对象的多个值,打印到控制台,查看是否为同一个对象。

3.4bean实例化小结

bean是如何创建的呢?

构造方法

(2)Spring的IOC实例化对象的三种方式分别是:

  • 构造方法(常用)
  • 静态工厂(了解)
  • 实例工厂(了解)
    • FactoryBean(实用)

这些方式中,重点掌握构造方法FactoryBean即可。

需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在使用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下。

五、bean的生命周期

1.什么是生命周期

从创建到消亡的完整过程,例如人从出生到死亡的整个过程就是一个生命周期。bean对象从创建到销毁的整体过程。

2.生命周期设置

添加初始化和销毁方法

在UserDaoImpl类中分别添加两个方法,方法名任意

   //表示bean初始化对应的操作
    public void init(){
        System.out.println("init...");
    }
    //表示bean销毁前对应的操作
    public void destory(){
        System.out.println("destory...");
    }

配置生命周期

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

运行程序

image-20231219151919525

从结果中可以看出,init方法执行了,但是destroy方法却未执行,这是为什么呢?

  • Spring的IOC容器是运行在JVM中
  • 运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行
  • main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了
  • 所以没有调用对应的destroy方法

想要看到destory需要将ApplicationContext更换成ClassPathXmlApplicationContext

调用其close()方法

image-20231219152110341

或者添加两个接口InitializingBeanDisposableBean并实现接口中的两个方法afterPropertiesSetdestroy

public class UserDaoImpl implements UserDao, InitializingBean, DisposableBean {
    public UserDaoImpl() {
        System.out.println("book dao constructor is running ....");
    }

    public void save() {
        System.out.println("user dao save ...");
    }

    //表示bean初始化对应的操作
    public void init(){
        System.out.println("init...");
    }
    //表示bean销毁前对应的操作
    public void destory(){
        System.out.println("destory...");
    }

    public void destroy() throws Exception {
        System.out.println("destroy()");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet()");
    }
}

image-20231219153827224

3.bean生命周期小结

(1)关于Spring中对bean生命周期控制提供了两种方式:

  • 在配置文件中的bean标签中添加init-methoddestroy-method属性
  • 类实现InitializingBeanDisposableBean接口,这种方式了解下即可。

(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:

  • 初始化容器
    • 1.创建对象(内存分配)
    • 2.执行构造方法
    • 3.执行属性注入(set操作)
    • 4.执行bean初始化方法
  • 使用bean
    • 1.执行业务操作
  • 关闭/销毁容器
    • 1.执行bean销毁方法

六、DI相关内容

1.setter注入引用类型
<bean id="userDao" class="com.ts.dao.impl.UserDaoImpl"/>
    <bean id="userService" class="com.ts.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
</bean>

image-20231219160237086

切记UserDaoImpl类中需要提供setter方法

2.setter注入基本数据类型
<bean id="userDao" class="com.ts.dao.impl.UserDaoImpl">
    <property name="databaseName" value="ts"/>
    <property name="connectionNum" value="10"/>
</bean>

**注意:**两个property注入标签的顺序可以任意。

  • 对于引用数据类型使用的是<property name="" ref=""/>
  • 对于简单数据类型使用的是<property name="" value=""/>
3.构造器注入引用类型
<bean id="userService" class="com.ts.service.impl.UserServiceImpl">
    <constructor-arg name="userDao" ref="userDao"/>
    <constructor-arg name="catDao" ref="catDao"/>
</bean>

image-20231219163341014

4.构造器注入基本类型
<bean id="userService" class="com.ts.service.impl.UserServiceImpl">
    <constructor-arg name="userDao" ref="userDao"/>
    <constructor-arg name="catDao" ref="catDao"/>
    <constructor-arg name="age" value="10"/>
    <constructor-arg name="name" value="ts"/>
</bean>

image-20231219163700267

constructor-arg 后面也可以跟上index与type防止耦合,但是并不常用

<bean id="userService" class="com.ts.service.impl.UserServiceImpl">
    <constructor-arg index="0" value="10"/>
    <constructor-arg index="1" value="ts"/>
</bean>
    <bean id="userService" class="com.ts.service.impl.UserServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="catDao" ref="catDao"/>
        <constructor-arg type="int" value="10"/>
        <constructor-arg type="java.lang.String" value="ts"/>
<!--        <constructor-arg type="java.lang.String" value="longhua"/>-->
    </bean>

具体该如何选择呢?

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

    • 简单数据类型

      <bean ...>
      	<property name="" value=""/>
      </bean>
      
    • 引用数据类型

      <bean ...>
      	<property name="" ref=""/>
      </bean>
      
  • 构造器注入

    • 简单数据类型

      <bean ...>
      	<constructor-arg name="" index="" type="" value=""/>
      </bean>
      
    • 引用数据类型

      <bean ...>
      	<constructor-arg name="" index="" type="" ref=""/>
      </bean>
      
  • 依赖注入的方式选择上

    • 建议使用setter注入
    • 第三方技术根据情况选择

七、自动配置

1.什么是自动装配

IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配

2.自动装配方式有哪些?
  • 按类型(常用)
  • 按名称
  • 按构造方法
  • 不启用自动装配

配置

<bean id="catDao" class="com.ts.dao.impl.CatDaoImpl"/>
<bean id="userDao" class="com.ts.dao.impl.UserDaoImpl"/>

<bean id="userService" class="com.ts.service.impl.UserServiceImpl" autowire="byType"/>

注意事项:

  • 需要注入属性的类中对应属性的setter方法不能省略
  • 被注入的对象必须要被Spring的IOC容器管理
  • 按照类型在Spring的IOC容器中如果找到多个对象,会报NoUniqueBeanDefinitionException

一个类型在IOC中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为:

<bean id="userService" class="com.ts.service.impl.UserServiceImpl" autowire="byName"/>

注意事项:

  • 按照名称注入中的名称指的是什么?

image-20231219172643401

set方法把set去掉,首字母变小写

两种方式介绍完后,以后用的更多的是按照类型注入。

最后对于依赖注入,需要注意一些其他的配置特征:

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

八、集合注入

1.注入数组类型数据
<property name="array">
    <array>
        <value>100</value>
        <value>200</value>
        <value>300</value>
    </array>
</property>
2. 注入List类型数据
<property name="list">
    <list>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>chuanzhihui</value>
    </list>
</property>
3. 注入Set类型数据
<property name="set">
    <set>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>boxuegu</value>
    </set>
</property>
4. 注入Map类型数据
<property name="map">
    <map>
        <entry key="country" value="china"/>
        <entry key="province" value="henan"/>
        <entry key="city" value="kaifeng"/>
    </map>
</property>
5.注入Properties类型数据
<property name="properties">
    <props>
        <prop key="country">china</prop>
        <prop key="province">henan</prop>
        <prop key="city">kaifeng</prop>
    </props>
</property>

配置完成后,运行下看结果:

image-20231219173748439

说明:

  • property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array><list><set><map><props>标签
  • List的底层也是通过数组实现的,所以<list><array>标签是可以混用
  • 集合中要添加引用类型,只需要把<value>标签改成<ref>标签,这种方式用的比较少

九、IOC/DI配置管理第三方bean

1.数据源对象管理

1.1.实现Druid管理,导入druid的依赖

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

1.2.配置第三方bean

在applicationContext.xml配置文件中添加DruidDataSource的配置

<?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="bookDao" class="com.ts.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.ts.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"></property>
    </bean>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/blog"/>
        <property name="username" value="root"/>
        <property name="password" value="29207479aaqq,.A"/>
    </bean>
</beans>

说明:

  • driverClassName:数据库驱动
  • url:数据库连接地址
  • username:数据库连接用户名
  • password:数据库连接密码
  • 数据库连接的四要素要和自己使用的数据库信息一致。

1.3.从IOC容器中获取对应的bean对象

public class Main {
    public static void main(String[] args) {
        //获取IOC容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) context.getBean("bookService");
        bookService.save();
        DataSource dataSource = (DataSource) context.getBean("dataSource");
        System.out.println(dataSource);
    }
}

1.4.运行结果

image-20231219204205903

2.1实现C3P0管理,引入pom依赖

<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>

2.2配置第三方bean

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/blog"/>
    <property name="user" value="root"/>
    <property name="password" value="29207479aaqq,.A"/>
    <property name="maxPoolSize" value="1000"/>
</bean>

注意:

  • ComboPooledDataSource的属性是通过setter方式进行注入
  • 想注入属性就需要在ComboPooledDataSource类或其上层类中有提供属性对应的setter方法
  • C3P0的四个属性和Druid的四个属性是不一样的

2.3.运行程序

image-20231219205057290

报的错为ClassNotFoundException,翻译出来是类没有发现的异常,具体的类为com.mysql.jdbc.Driver。错误的原因是缺少mysql的驱动包。

分析出错误的原因,具体的解决方案就比较简单,只需要在pom.xml把驱动包引入即可。

添加完mysql的驱动包以后,再次运行App,就可以打印出结果:

image-20231219205144609

注意:

  • 数据连接池在配置属性的时候,除了可以注入数据库连接四要素外还可以配置很多其他的属性,具体都有哪些属性用到的时候再去查,一般配置基础的四个,其他都有自己的默认值
  • Druid和C3P0在没有导入mysql驱动包的前提下,一个没报错一个报错,说明Druid在初始化的时候没有去加载驱动,而C3P0刚好相反
  • Druid程序运行虽然没有报错,但是当调用DruidDataSource的getConnection()方法获取连接的时候,也会报找不到驱动类的错误
2.加载properties文件

1.准备properties配置文件

resources下创建一个jdbc.properties文件,并添加对应的属性键值对

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/weblog
jdbc.username=root
jdbc.password=29207479aaqq,.A

2.开启context命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation=
               "http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd">

3.加载properties配置文件

<context:property-placeholder location="jdbc.properties"/>

location写方法,当用外部的配置文件的时候,location需要按如下写

 <!--方式一 -->
    <context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
    <!--方式二-->
    <context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>
    <!--方式三 -->
    <context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
    <!--方式四-->
    <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>

4.完成属性注入

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
  • 如何加载properties配置文件
<context:property-placeholder location="" system-properties-mode="NEVER"/>
  • 如何在applicationContext.xml引入properties配置文件中

${key}

十、核心容器

1.容器的创建方式
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext context = new FileSystemXmlApplicationContext("F:\\tsCode\\SSMTsDemo\\src\\main\\resources\\applicationContext.xml");
2.Bean的三种获取方式

方式一,就是目前案例中获取的方式:

BookDao bookDao = (BookDao) ctx.getBean("bookDao");

这种方式存在的问题是每次获取的时候都需要进行类型转换,有没有更简单的方式呢?

方式二:

BookDao bookDao = ctx.getBean("bookDao",BookDao.class);

这种方式可以解决类型强转问题,但是参数又多加了一个,相对来说没有简化多少。

方式三:

BookDao bookDao = ctx.getBean(BookDao.class);

这种方式就类似我们之前所学习依赖注入中的按类型注入。必须要确保IOC容器中该类型对应的bean对象只能有一个。

3.容器类层次结构

image-20231219221043109

4.BeanFactory的使用

使用BeanFactory来创建IOC容器的具体实现方式为:

public class AppForBeanFactory {
    public static void main(String[] args) {
        Resource resources = new ClassPathResource("applicationContext.xml");
        BeanFactory bf = new XmlBeanFactory(resources);
        BookDao bookDao = bf.getBean(BookDao.class);
        bookDao.save();
    }
}

为了更好的看出BeanFactoryApplicationContext之间的区别,在BookDaoImpl添加如下构造函数:

public class BookDaoImpl implements BookDao {
    public BookDaoImpl() {
        System.out.println("constructor");
    }
    public void save() {
        System.out.println("book dao save ..." );
    }
}

如果不去获取bean对象,打印会发现:

  • BeanFactory是延迟加载,只有在获取bean对象的时候才会去创建

  • ApplicationContext是立即加载,容器加载的时候就会创建bean对象

  • ApplicationContext要想成为延迟加载,只需要按照如下方式进行配置

    <?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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"  lazy-init="true"/>
    </beans>
    
  • 容器创建的两种方式

    • ClassPathXmlApplicationContext[掌握]
    • FileSystemXmlApplicationContext[知道即可]
  • 获取Bean的三种方式

    • getBean(“名称”):需要类型转换
    • getBean(“名称”,类型.class):多了一个参数
    • getBean(类型.class):容器中不能有多个该类的bean对象

    上述三种方式,各有各的优缺点,用哪个都可以。

  • 容器类层次结构

    • 只需要知晓容器的最上级的父接口为 BeanFactory即可
  • BeanFactory

    • 使用BeanFactory创建的容器是延迟加载
    • 使用ApplicationContext创建的容器是立即加载
    • 具体BeanFactory如何创建只需要了解即可。

十一、IOC/DI注解开发

要想真正简化开发,就需要用到Spring的注解开发,Spring对注解支持的版本历程:

  • 2.0版开始支持注解
  • 2.5版注解功能趋于完善
  • 3.0版支持纯注解开发

关于注解开发,会有两块内容注解开发定义bean纯注解开发

注解开发定义bean用的是2.5版提供的注解,纯注解开发用的是3.0版提供的注解。

1.注解开发定义bean

1.删除原XML配置

将配置文件中的<bean>标签删除掉

<bean id="bookDao" class="com.ts.dao.impl.BookDaoImpl"/>

2.Dao上添加注解

@Component("bookDao")
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ..." );
    }
}

注意:@Component注解不可以添加在接口上,因为接口是无法创建对象的。

一个注解就相当于一个Bean

@Component(“bookDao”)相当于给bean起一个id

注解写在哪个类上,class值得就是该类的类全名

3.配置Spring的注解包扫描

 <context:component-scan base-package="com.ts"/>

说明:

component-scan

  • component:组件,Spring将管理的bean视作自己的一个组件
  • scan:扫描

base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。

  • 包路径越多[如:com.ts.dao.impl],扫描的范围越小速度越快
  • 包路径越少[如:com.ts],扫描的范围越大速度越慢
  • 一般扫描到项目的组织名称即Maven的groupId下[如:com.ts]即可。

4.运行程序

image-20231219223957220

5.Service上添加注解

在BookServiceImpl类上也添加@Component交给Spring框架管理

@Component
public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    @Override
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

6.运行程序

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        bookDao.save();
        //按类型获取bean
        BookService bookService = ctx.getBean(BookService.class);
        System.out.println(bookService);
    }
}

打印观察结果,两个bean对象都已经打印到控制台

image-20231219224407567

说明:

  • BookServiceImpl类没有起名称,所以在App中是按照类型来获取bean对象

  • @Component注解如果不起名称,会有一个默认值就是当前类名首字母小写,所以也可以按照名称获取,如

    BookService bookService = (BookService)ctx.getBean("bookServiceImpl");
    System.out.println(bookService);
    

对于@Component注解,还衍生出了其他三个注解@Controller@Service@Repository

这三个注解和@Component注解的作用是一样的,为什么要衍生出这三个呢?

方便我们后期在编写类的时候能很好的区分出这个类是属于表现层业务层还是数据层的类。

知识点1:@Component等
名称@Component/@Controller/@Service/@Repository
类型类注解
位置类定义上方
作用设置该类为spring管理的bean
属性value(默认):定义bean的id
2.纯注解开发模式

使用注解来配置bean,但是依然有用到配置文件,在配置文件中对包进行了扫描,Spring在3.0版已经支持纯注解开发

  • Spring3.0开启了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道

1.创建配置类

创建一个配置类SpringConfig

2.标识该类为配置类

@Configuration
public class SpringConfig {
}

3.用注解替换包扫描配置

@Configuration
@ComponentScan("com.ts")
public class SpringConfig {
}

4.创建运行类并执行

创建一个新的运行类AppForAnnotation

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = (BookDao) context.getBean("bookDao");
        System.out.println(bookDao);
        BookService bookService = context.getBean(BookService.class);
        System.out.println(bookService);
    }
}

image-20231219225455350

  • @Configuration注解用于设定当前类为配置类

  • @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式

    @ComponentScan({com.ts.service","com.ts.dao"})
    
  • 读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象

    //加载配置文件初始化容器
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    //加载配置类初始化容器
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    
知识点1:@Configuration
名称@Configuration
类型类注解
位置类定义上方
作用设置该类为spring配置类
属性value(默认):定义bean的id
知识点2:@ComponentScan
名称@ComponentScan
类型类注解
位置类定义上方
作用设置spring配置类扫描路径,用于加载使用注解格式定义的bean
属性value(默认):扫描路径,此路径可以逐层向下扫描

小结::

  • 记住@Component、@Controller、@Service、@Repository这四个注解
  • applicationContext.xml中<context:component-san/>的作用是指定扫描包路径,注解为@ComponentScan
  • @Configuration标识该类为配置类,使用类替换applicationContext.xml文件
  • ClassPathXmlApplicationContext是加载XML配置文件
  • AnnotationConfigApplicationContext是加载配置类
3.注解开发bean作用范围与生命周期管理
1.Bean的作用范围

//@Scope设置bean的作用范围
@Scope(“prototype”)|(“singleton”)

知识点1:@Scope
名称@Scope
类型类注解
位置类定义上方
作用设置该类创建对象的作用范围
可用于设置创建出的bean是否为单例对象
属性value(默认):定义bean作用范围,
默认值singleton(单例),可选值prototype(非单例)
2.Bean的生命周期
知识点1:@PostConstruct
名称@PostConstruct
类型方法注解
位置方法上
作用设置该方法为初始化方法
属性
知识点2:@PreDestroy
名称@PreDestroy
类型方法注解
位置方法上
作用设置该方法为销毁方法
属性

小结

image-20231219230223981

4.注解开发依赖注入
1.准备步骤

1.新建如下结构模块

image-20231220100352456

2.代码如下:

@Configuration
@ComponentScan("com.ts")
public class SpringConfig {
}
@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save");
    }
}
public interface BookDao {
    public void save();
}
@Service
public class BookServiceImpl implements BookService {

    private BookDao bookDao;
    
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save");
        bookDao.save();
    }
}
public interface BookService {
    public void save();
}
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookService bookService = context.getBean(BookService.class);
        bookService.save();
    }

3.运行结果如下:

image-20231220100729962

出现问题的原因是,在BookServiceImpl类中添加了BookDao的属性,并提供了setter方法,但是目前是没有提供配置注入BookDao的,所以bookDao对象为Null,调用其save方法就会报控指针异常

4.BookServiceImpl类的bookDao属性上添加@Autowired注解

@Autowired
private BookDao bookDao;

注意:

  • @Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉
  • 为什么setter方法可以删除呢?
    • 自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
    • 普通反射只能获取public修饰的内容
    • 暴力反射除了获取public修饰的内容还可以获取private修改的内容
    • 所以此处无需提供setter方法

5.添加一个新类BookDao2Impl

@Repository
public class BookDao2Impl implements BookDao {
    public void save() {
        System.out.println("book2 dao save");
    }
}

在运行发现注入失败

image-20231220101055870

此时,按照类型注入就无法区分到底注入哪个对象,解决方案:按照名称注入

给两个Dao类分别起名字

@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ..." );
    }
}
@Repository("bookDao2")
public class BookDaoImpl2 implements BookDao {
    public void save() {
        System.out.println("book dao save ...2" );
    }
}

此时就可以注入成功,但是得思考个问题:

  • @Autowired是按照类型注入的,给BookDao的两个实现起了名称,它还是有两个bean对象,为什么不报错?

  • @Autowired默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配。因为变量名叫bookDao而容器中也有一个booDao,所以可以成功注入。

  • 分析下面这种情况是否能完成注入呢?

    image-20231220101445903

  • 不行,因为按照类型会找到多个bean对象,此时会按照bookDao名称去找,因为IOC容器只有名称叫bookDao1bookDao2,所以找不到,会报NoUniqueBeanDefinitionException

2.注解实现按照名称注入

当根据类型在容器中找到多个bean,注入参数的属性名又和容器中bean的名称不一致,这个时候该如何解决,就需要使用到@Qualifier来指定注入哪个名称的bean对象。

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    @Qualifier("bookDao1")
    private BookDao bookDao;
    
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

@Qualifier注解后的值就是需要注入的bean的名称。

注意:@Qualifier不能独立使用,必须和@Autowired一起使用

3.简单数据类型注入
@Value("tshappybop")
4.注解读取properties配置文件

1.resource下准备jdbc.properties文件

name=tshappyboy

2.使用注解加载properties配置文件

配置类上添加@PropertySource注解

3.使用@Value读取配置文件中的内容

@Value("${name}")

4.运行程序

image-20231220102532946

注意:

  • 如果读取的properties配置文件有多个,可以使用@PropertySource的属性来指定多个

    @PropertySource({"jdbc.properties","xxx.properties"})
    
  • @PropertySource注解属性中不支持使用通配符*,运行会报错

    @PropertySource({"*.properties"})
    
  • @PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件

    @PropertySource({"classpath:jdbc.properties"})
    
5.@Autowired
名称@Autowired
类型属性注解 或 方法注解(了解) 或 方法形参注解(了解)
位置属性定义上方 或 标准set方法上方 或 类set方法上方 或 方法形参前面
作用为引用类型属性设置值
属性required:true/false,定义该属性是否允许为null
6.@Qualifier
名称@Qualifier
类型属性注解 或 方法注解(了解)
位置属性定义上方 或 标准set方法上方 或 类set方法上方
作用为引用类型属性指定注入的beanId
属性value(默认):设置注入的beanId
7.@Value
名称@Value
类型属性注解 或 方法注解(了解)
位置属性定义上方 或 标准set方法上方 或 类set方法上方
作用为 基本数据类型 或 字符串类型 属性设置值
属性value(默认):要注入的属性值
8.@PropertySource
名称@PropertySource
类型类注解
位置类定义上方
作用加载properties文件中的属性值
属性value(默认):设置加载的properties文件对应的文件名或文件名组成的数组

十二、IOC/DI注解开发管理第三方bean

前面定义bean的时候都是在自己开发的类上面写个注解就完成了,但如果是第三方的类,这些类都是在jar包中,我们没有办法在类上面添加注解,这个时候该怎么办?

遇到上述问题,我们就需要有一种更加灵活的方式来定义bean,这种方式不能在原始代码上面书写注解,一样能定义bean,这就用到了一个全新的注解==@Bean==。

1.环境准备
  • 创建一个Maven项目

  • pom.xml添加Spring的依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
    </dependencies>
    
  • 添加一个配置类SpringConfig

    @Configuration
    public class SpringConfig {
    }
    
  • 添加BookDao、BookDaoImpl类

    public interface BookDao {
        public void save();
    }
    @Repository
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ..." );
        }
    }
    
  • 创建运行类App

    public class App {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        }
    }
    

最终创建好的项目结构如下:

image-20231220125535929

2.注解开发管理第三方bean

1.导入对应的jar包

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

2.在配置类中添加一个方法

注意该方法的返回值就是要创建的Bean对象类型

@Configuration
public class SpringConfig {
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

3.在方法上添加@Bean注解

@Bean注解的作用是将方法的返回值制作为Spring管理的一个bean对象

@Configuration
public class SpringConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/weblog");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

注意:不能使用DataSource ds = new DruidDataSource()

因为DataSource接口中没有对应的setter方法来设置属性。

4.从IOC容器中获取对象并打印

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        DataSource dataSource = context.getBean(DataSource.class);
        System.out.println(dataSource);
    }
}

至此使用@Bean来管理第三方bean的案例就已经完成。

如果有多个bean要被Spring管理,直接在配置类中多些几个方法,方法上添加@Bean注解即可。

3.引入外部配置类

新建一个JdbcConfig配置类,并把数据源配置到该类下。

public class JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/weblog");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

这样还不行,Spring不认识这个类,扫描不到,因为还需要在Spring的配置类上添加包扫描并且JdbcConfig添加上配置注解

@Configuration
public class JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/weblog");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}
@Configuration
@ComponentScan("com.ts")
public class SpringConfig {

}

运行程序可以成功

image-20231220130333248

还可以使用@Import引入,这样我们可以更加清晰的知道哪个类为配置类

1.去除JdbcConfig类上的注解

public class JdbcConfig {
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/weblog");
        ds.setUsername("root");
        ds.setPassword("root");
        return ds;
    }
}

2.在Spring配置类中引入

@Configuration
@Import({JdbcConfig.class})
public class SpringConfig {

}

运行程序

image-20231220130748392

注意:

  • 扫描注解可以移除

  • @Import参数需要的是一个数组,可以引入多个配置类。

  • @Import注解在配置类中只能写一次,下面的方式是不允许的

    @Configuration
    //@ComponentScan("com.itheima.config")
    @Import(JdbcConfig.class)
    @Import(Xxx.class)
    public class SpringConfig {
    	
    }
    

依然能获取到bean对象并打印控制台

4.@Bean
名称@Bean
类型方法注解
位置方法定义上方
作用设置该方法的返回值作为spring管理的bean
属性value(默认):定义bean的id
5.@Import
名称@Import
类型类注解
位置类定义上方
作用导入配置类
属性value(默认):定义导入的配置类类名,
当配置类有多个时使用数组格式一次性导入多个配置类
6.注解开发实现为第三方bean注入资源

基本数据类型用@Value注解,引用类型用只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象

7.注解开发总结

image-20231220131216477

十三、Spring整合Mybatis

1.环境准备

1.准备数据库表

create database spring_db character set utf8;
use spring_db;
create table tbl_account(
    id int primary key auto_increment,
    name varchar(35),
    money double
);

2.创建项目导入jar包

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
</dependencies>

3.根据表创建实体类

public class Account {
    private Integer id;
    private String name;
    private Double money;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    public Account() {
    }

    public Account(Integer id, String name, Double money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }
}

4.创建Dao接口

public interface AccountDao {
    @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
    void save(Account account);

    @Delete("delete from tbl_account where id = #{id} ")
    void delete(Integer id);

    @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
    void update(Account account);

    @Select("select * from tbl_account")
    List<Account> findAll();

    @Select("select * from tbl_account where id = #{id} ")
    Account findById(Integer id);
}

5.创建Service接口和实现类

public interface AccountService {
    void save(Account account);

    void delete(Integer id);

    void update(Account account);

    List<Account> findAll();

    Account findById(Integer id);
}

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void save(Account account) {
        accountDao.save(account);
    }

    public void update(Account account){
        accountDao.update(account);
    }

    public void delete(Integer id) {
        accountDao.delete(id);
    }

    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    public List<Account> findAll() {
        return accountDao.findAll();
    }
}

6.添加jdbc.properties文件

resources目录下添加,用于配置数据库连接四要素

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=root

useSSL:关闭MySQL的SSL连接

7.添加Mybatis核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--读取外部properties配置文件-->
    <properties resource="jdbc.properties"></properties>
    <!--别名扫描的包路径-->
    <typeAliases>
        <package name="com.ts.domain"/>
    </typeAliases>
    <!--数据源-->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>
    <!--映射文件扫描包路径-->
    <mappers>
        <package name="com.ts.mapper"></package>
    </mappers>
</configuration>

8.编写应用程序

public class Main {
    public static void main(String[] args) throws IOException {
        // 1. 创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 2. 加载SqlMapConfig.xml配置文件
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 3. 创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        // 4. 获取SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 5. 执行SqlSession对象执行查询,获取结果User
        AccountDao accountDao = sqlSession.getMapper(AccountDao.class);

        Account ac = accountDao.findById(1);
        System.out.println(ac);

        // 6. 释放资源
        sqlSession.close();
    }
}

运行后

image-20231220135905187

2.进行整合

1.导入整合需要的jar包依赖

<dependency>
    <!--Spring操作数据库需要该jar包-->
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<dependency>
    <!--
        Spring与Mybatis整合的jar包
        这个jar包mybatis在前面,是Mybatis提供的
    -->
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.0</version>
</dependency>

2.创建Spring的主配置类

//配置类注解
@Configuration
//包扫描,主要扫描的是项目中的AccountServiceImpl类
@ComponentScan("com.ts")
public class SpringConfig {
}

3.创建数据源的配置类

public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String userName;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}

4.主配置类中读properties并引入数据源配置类

@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import(JdbcConfig.class)
public class SpringConfig {
}

5.创建Mybatis配置类并配置SqlSessionFactory

public class MybatisConfig {
    //定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        //设置模型类的别名扫描
        ssfb.setTypeAliasesPackage("com.ts.domain");
        //设置数据源
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
    //定义bean,返回MapperScannerConfigurer对象
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.ts.mapper");
        return msc;
    }
}

6.主配置类中引入Mybatis配置类

@Configuration
@ComponentScan("com.ts")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}

7.编写运行类

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

        AccountService accountService = ctx.getBean(AccountService.class);

        Account ac = accountService.findById(1);
        System.out.println(ac);
    }
}

运行之后

image-20231220142939069

支持Spring与Mybatis的整合就已经完成了,其中主要用到的两个类分别是:

  • SqlSessionFactoryBean
  • MapperScannerConfigurer

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Tomcat是一款免费的Web服务器,专为Java Web应用程序而设计。它是一个开源项目,由Apache软件基金会开发和维护。Tomcat支持Java Servlet、JavaServer Pages(JSP)、Java Expression Language(EL)和Java WebSocket等技术。 Tomcat的最新版本是Tomcat 9,它是我们使用的最新Java Servlet API版本。Tomcat 9也支持HTTP/2协议和WebSocket 1.1规范。Tomcat 也有其他版本,比如Tomcat 8.5,它也是一个很常用的版本。 Tomcat的好处是它轻量级且易于使用。Tomcat虽然是一款Web服务器,但它可以作为嵌入式Web容器,使用Spring框架等等进行开发。由于Tomcat使用Java语言,因此Tomcat不仅适用于多个操作系统,如Windows、Linux和Mac OS X,而且还适用于多个开发平台,如Eclipse和NetBeans。 Tomcat也很容易配置。我们可以使用命令行界面轻松地配置服务器,在Tomcat的安装页面可以选择安装到什么目录,配置好后,我们可以通过在浏览器中输入localhost:8080,直接访问Tomcat Server并查看Tomcat欢迎页。接着,我们可以部署自己的Java Web应用程序,这些应用程序可以使用Java Servlet、JSP和其他Java Web技术。 总的来说,Tomcat是一款优秀的Java Web服务器,它可以快速安装和配置,适用于多个开发平台和操作系统,并且易于使用和集成。无论开发Java Web应用程序还是部署Java Web应用程序,Tomcat都是一个值得使用的工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值