当前位置:   article > 正文

Spring从入门到精通(一)

spring从入门到精通

1.Spring基本概述

1.1 什么是Spring

  • Spring 是分层的 Java SE/EE 应用 full-stack(一站式、全站)轻量级开源框架,以 IoC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核提供了展现层 SpringMVC持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

1.2 Spring的优势(重点理解)

  • 方便解耦,简化开发
    通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
  • AOP 编程的支持
    通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过AOP 轻松应付。
  • 声明式事务的支持
    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
  • 方便程序的测试
    可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
  • 方便集成各种优秀框架
    Spring 可以降低各种框架的使用难度,提供了对各种优秀框架( Struts、 Hibernate、 Hessian、 Quartz等)的直接支持。
  • 降低 JavaEE API 的使用难度
    Spring 对 JavaEE API(如 JDBC、 JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。
  • Java 源码是经典学习范例
    Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。

1.3 Spring架构组成

Spring架构由诸 多模块组成,可分类为:

  • 核心技术: 依赖注入,事件,资源,i18n, 验证,数据绑定,类型转换,SpEL, AOP
  • 测试: 模拟对象,TestContext框架, Spring MVC测试,WebTestClient。
  • 数据访问: 事务,DAO支持,JDBC, ORM, 封送XML。
  • Spring MVC和Spring WebFlux Web框架。
  • 集成: 远程处理,JMS, JCA, JMX, 电子邮件,任务,调度,缓存。
  • 语言: Kotlin, Groovy, 动态语言。
    在这里插入图片描述
  • Spring 依赖
GroupldArtifactld说明
org.springframeworkspring-beansBeans支持,包含Groovy
org.springframeworkspring-aop基于代理的AOP支持
org.springframeworkspring-aspects基于AspectJ的切面
org.springframeworkspring-context应用上下文运行时,包括调度和远程抽象
org.springframeworkspring-context-support支持将常见的第三方类库集成到Spring应用上下文
org.springframeworkspring-corespring-core
org.springframeworkspring-expressionSpring 表达式语言,SpEL
org.springframeworkspring-test单元测试和集成测试支持组件
org.springframeworkspring-tx事务基础组件,包括对DAO的支持及JCA的集成
org.springframeworkspring-webweb支持包,包括客户端及web远程调用
org.springframeworkspring-webmvcREST web服务及web应用的MVC实现
org.springframeworkspring-jclJakarta Commons Logging日志系统

在这里插入图片描述


2. 程序间耦合分析以及解决方案

2.1 相关概念

  • 耦合
    • 耦合指的是程序间的依赖关系
      - 类的依赖关系(eg: service依赖dao层)
      - 方法的依赖关系
  • 解耦
    • 我们不能消除程序间的依赖关系,只能尽可能的降低程序的依赖关系。这种降低程序间的依赖关系就叫做解耦。
  • 如何解耦
    • 在实际开发中,我们应该做到在编译期不依赖,在运行期依赖。
  • Bean
    • 在计算机英语中,有可重用组件(如持久层和业务层的接口和重用)的含义。
  • JavaBean
    • 其实JavaBean并不完全等于实体类,因为实体类只是可重用组件的一部分,也就是JavaBean的范围大于实体类的范围。
    • JavaBean的含义:用Java语言编写的可重用组件。

2.2 使用工厂解耦(Spring容器原理)

  • 降低解耦的思路
    • 使用反射的机制,避免使用new关键字。
    • 通过读取配置文件的方式来获取资源的全限定名。
  • 工厂
    • 就是创建我们的service和dao对象的(JavaBean)。
  • 解决步骤:
    • 需要一个配置文件来配置我们的service和dao。
    • 配置的内容:唯一标志=全限定类名(key=value)。
    • 通过读取配置文件中配置的内容,反射创建对象。
  • 配置文件的选取:
    • xml(当然后期spring框架用的肯定是xml)
    • properties(读取更简单,我们这个例程先选用这种)

(1)工厂配置文件
在这里插入图片描述
(2)工厂单例模式——解耦
在这里插入图片描述


3.IOC的概念和作用

  • IOC概念:

    • 反转了依赖关系的满足方式,由之前的自己创建依赖对象,变为由工厂推送。
      • 变主动为被动,即反转
      • bean对象保存在Spring容器里
  • IOC作用:

    • 解决了具有依赖关系的组件之间的强耦合,使得项目形态更加稳健
  • IOC实现:

    • 通过解析配置文件
    • 通过工厂用反射的方式创建对象

(1)IOC示例

  • 配置文件(配置bean标签)
    在这里插入图片描述
  • 调用Spring工厂API(ApplicationContext接口)
public class testAccountService {
 	/**
		程序中的对象都交由Spring的ApplicationContext工厂进行创建
	*/
    @Test
    public void testFactory(){
    //1.读取配置文件中所需创建的bean对象,并获得工厂对象
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("Bean.xml");
        //2. 通过id获取bean对象
        AccountService accountService= (AccountService) applicationContext.getBean("AccountService");
        //3.使用对象
        accountService.addAccount();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 基于XML的IOC开发配置文件
    在这里插入图片描述

(2)详解ApplicationContext

ApplicationContext是Spring给我们提供的核心容器,其依赖关系如下:
在这里插入图片描述
我们发现ApplicationContext继承了BeanFactory。BeanFactory才是顶级容器。

(1)这两个核心的容器有什么区别呢?

  • ApplicationContext
    ApplicationContext在构建核心容器时,创建对象采取的策略是采用立即加载的方式。 也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
  • BeanFactory
    BeanFactory它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式. 也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。

(2)ApplicationContext是一个接口,其重要实现类?
在这里插入图片描述

  • ClassPathXmlApplicationContext
    它可以加载类路径下的配置文件,要求配置文件必须在类路径下,否则加载不了(这种比较常用)。
  • FileSystemXmlApplicationContext
    它可以加载磁盘任意路径下的配置文件(必须有访问权限)。
  • AnnotationConfigApplicationContext
    它是用于读取注解创建容器的

4. Spring对bean的管理方式

4.1 Spring实例化bean的方式

(1) 无参数构造函数的实例化方式

  • 无参数构造函数实例化方式是Spring默认的bean的实例化方式
    在这里插入图片描述

(2)使用工厂中的普通方法实例化对象

  • 我们设想一个场景:我们要使用别人写好的代码,别人写好的代码通常是封装在一个jar包中的,我们要怎么创建jar包中字节码文件的对象呢?(我们无法通过修改源码的方式来提供默认构造方法)
  • 我们接下来模拟一个工厂类(它可能是存在于jar包中的),通常jar包都会暴露一个工厂类,给调用者创建对象。
    在这里插入图片描述
    在这里插入图片描述

(3)使用工厂中的静态方法实例化对象

在这里插入图片描述
在这里插入图片描述

4.2 Bean的细节

4.2.1Bean的作用域

(1)bean标签的scope属性:

  • 作用:用于指定bean的作用范围
  • 取值: 常用的就是单例的多例的
    • singleton:单例的(默认值)
    • prototype:多例的
    • request:作用于web应用的请求范围
    • session:作用于web应用的会话范围
    • global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session。

(2)单例与多例作用域的区别

  • 单例的
    • bean配置时,不指明scope则默认是单例配置
    • 当bean配置是单例时,Spring容器在加载配置文件时,就会将此bean创建并保存在容器中
      在这里插入图片描述
  • 多例的
    • 当bean配置是多例时,Spring容器在加载配置文件时,不会创建bean对象,只有在使用bean的id获取此对象时,才会创建此bean对象
      在这里插入图片描述

4.2.2Bean的生命周期

(1) 单例对象
单例对象的生命周期和容器的生命周期是一致的。 当容器创建时,对象就实例化好了。当容器还在的时候,对象也就一直存在。当容器销毁,对象也就消亡。
在这里插入图片描述

在这里插入图片描述

(2)多例对象

  • 出生: 当我们使用对象时spring框架为我们创建时(调用配置bean的id)
  • 活着: 对象只要是在使用过程中就一直活着
  • 死亡: 当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收
    在这里插入图片描述

在这里插入图片描述

5. 依赖注入

5.1 依赖注入的特点及概念

  • 依赖注入:

    • 在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
    • 依赖关系的维护就称为依赖注入
  • IOC的作用:
    降低程序间的耦合(依赖关系)

  • DI的作用:
    依赖关系的管理,依赖都交给spring来维护

  • 依赖注入的数据类型

    • 基本类型和String
    • 其他bean类型(在配置文件中或者注解配置过的bean)
    • 复杂类型/集合类型
  • 依赖注入的方式

    • 第一种:使用构造函数提供
    • 第二种:使用set方法提供
    • 第三种:使用注解提供(后面再介绍)

5.2依赖注入的方式

(1)构造函数注入

  • 使用的标签:constructor-arg
  • 标签出现的位置:bean标签的内部
  • 标签中的属性:
    • name(最常用):用于指定给构造函数中指定名称的参数赋值
    • value:用于提供基本类型和String类型的数据
    • ref:用于指定其他的bean类型数据(它指的就是在spring的ioc核心容器中出现过的bean对象)
      在这里插入图片描述
  • 优势
    在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功(因为默认空参构造
  • 弊端
    改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供

(2)set方法注入(默认的)

  • 涉及的标签:property
  • 出现的位置:bean标签的内部
  • 标签的属性
    • name:用于指定注入时所调用的set方法名称(如:setName --> name
    • value:用于提供基本类型和String类型的数据
    • ref:用于指定其他的bean类型数据。它指的就是在spring的ioc核心容器中出现过的bean对象
      在这里插入图片描述

(3)复杂类型数据注入

  • 创建实体类,并提供set方法
  • 创建配置文件
<!--    复杂集合set注入-->
    <bean id="user" class="com.qfedu.pojo.User">
<!--        数组set注入-->
        <property name="nums">
            <array>
                <value>1</value>
                <value>2</value>
                <value>3</value>
            </array>
        </property>
<!--        集合属性注入-->
        <property name="list">
            <list>
                <value>tom</value>
                <value>jack</value>
            </list>
        </property>
<!--        set集合注入-->
        <property name="set">
            <set>
                <value>12</value>
                <value>1</value>
            </set>
        </property>
<!--       map集合注入 -->
        <property name="map">
            <map>
                <entry key="tom" value="20"></entry>
                <entry key="jack" value="22"></entry>
            </map>
        </property>
<!--        properties注入-->
        <property name="props">
            <props>
                <prop key="tom">man</prop>
                <prop key="jack">woman</prop>
            </props>
        </property>
    </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

6. IOC注解

IOC注解可以分为以下几类:

  • 用于创建对象的
    • 作用:他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
  • 用于注入数据的
    • 作用:他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的。
  • 用于改变作用范围的
    • 作用:他们的作用就和在bean标签中使用scope属性实现的功能是一样的
  • 和生命周期相关的
    • 作用:他们的作用就和在bean标签中使用init-methoddestroy-methode的作用是一样的。
  • 配置相关注解
    • 作用:用于配置相关项目配置文件信息(包括配置jar包对象创建、注解包扫描、文件的读取

6.1 创建对象注解

(1)@Component

  • 用法: 定义在类名上。
  • 作用: 用于把当前类对象存入spring容器中。
  • 属性: value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写

(2)Component衍生的注解

  • 以下三个注解他们的作用和属性与@Component是一模一样,他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰。

    • @Controller:一般用在表现层。
    • @Service:一般用在业务。
    • @Repository:一般用在持久层。
  • 衍生类注解源码解析

    • @AliasFor注解作用(两个作用)
      • 于为注解属性声明别名
        它有两个属性name和value @AliasFor注解注释了自身,并且name和value 互为别名
        在这里插入图片描述

      • 不定义新的属性而是复用其他注解已有的注解属性(通过此种方法@Component衍生出其他注解类)
        在这里插入图片描述

6.2 注入数据注解

  • @Autowired、@Qualifier、@Resource三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
  • 集合类型的注入只能通过XML来实现。
  • @Value用于注入基本类型和String类型的数据

6.2.1自动按照类型注入

(1)@Autowired

  • 用法: 定义在属性(成员变量)上。
  • 作用: 实现自动按照类型注入。
  • 属性:无属性值,但可以结合 @Qualifier 实现spring容器中指定类型之后,再指定名称的bean对象注入

(2)实现原理:

  • 情况一如果有唯一一个类型匹配时,就直接注入。
    • 容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
    • 详细过程:首先会找到实现类,但是因为实现类继承了接口,所以可以把实现类看作接口(多态)
  • 情况二: 如果有多个匹配时
    • 首先按照类型筛选出来匹配的对象。
    • 其次使用变量名称作为bean的id,然后继续筛选。
    • 如果两次筛选后有唯一匹配的,可以注入成功;否则失败。

6.2.2 其他注入数据注解

(1)@Qualifier

  • 作用:在按照类型注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用;但是在给方法参数注入时可以(这个我们后面讨论)。
  • 属性:value:用于指定注入bean的id。

(2)@Resource

  • 作用:直接按照bean的id注入。它可以独立使用。
  • 属性:name:用于指定bean的id。
  • 特性
    • 若未给出name属性值时(执行两步),首先按照变量名进行匹配对象,若匹配不成功再按照变量数据类型进行筛选
    • 若给出name属性时(执行一步),无论成功与否,只会按照name给的名称进行筛选
    • 属于jdk注解,不属于spring注解

(3)@Value

  • 作用:用于注入基本类型和String类型的数据。
  • 属性:value:用于指定数据的值。它也可以使用spring中SpEL(也就是spring的el表达式)。
  • SpEL的写法:${表达式}。

6.3 改变作用域的注解

(1)@Scope

  • 作用:用于指定bean的作用范围。
  • 属性:value:指定范围的取值。
    • 常用取值:singleton(单例)、prototype(多例)。

6.4 生命周期相关的注解

  • 使用与生命周期相关的注解的作用跟在bean标签中使用init-method和destroy-methode的作用是一样的。

(1)@PostConstruct

  • 作用:用于指定初始化方法。

(2)@PreDestroy

  • 作用:用于指定销毁方法。

6.5 配置类相关注解

  • 配置类的作用和bean.xml一样

(1)@Configuration

  • 作用:指定当前类是一个配置类
  • 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写(建议书写)
  • 源码:
    在这里插入图片描述

(2)@ComponentScan

  • 作用:用于通过注解指定spring在创建容器时要扫描的包
  • 属性:value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
  • 类似xml配置文件如下:
    在这里插入图片描述

(3)@Bean

  • 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
  • 属性: name:用于指定bean的id。当不写时,默认值是当前方法的名称
  • 细节
    • 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。 (推荐使用)
    • 查找的方式和Autowired注解的作用是一样的。

(4)@Import

  • 作用:用于导入其他的配置类
  • 属性:value:用于指定其他配置类的字节码
  • 细节
    • 当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类。
    • 此时子配置类不需要加Configuration注解,只需要在父配置类上添加Import注解

(5)@PropertySource

  • 作用:用于指定properties文件的位置

  • 类似配置文件中:
    在这里插入图片描述

  • 属性:value:指定文件的名称和路径

  • 关键字

    • classpath:表示类路径下
    • 可以结合子配置类和@Value使用,用于读取配置文件
  • 示例:
    在这里插入图片描述

(6)问题:@Configuration注解一定需要吗?

  • 答案:@Configuration注解加不加都对程序的结果没有影响,不会因为@Configuration注解不加,造成bean注入失败(但是若配置文件中有方法相互依赖,可能会出现bean对象不是单例的情况)。
  • 演示:Brunner依赖于ADataSource
    在这里插入图片描述
    • 测试结果:
      在这里插入图片描述
    • 疑问1:为什么ADataSource只创建了1次?
      • 当我们使用@Bean注解修饰aDataSource方法的时候,会将ADataSource这个bean交给容器管理(就会调用ADataSource的无参数构造函数)。
      • 我们使用@Bean注解修饰bRunner方法的时候,由于需要使用到ADataSource,这个时候,不再重新的创建ADataSource的对象,而是直接从spring容器(spring的单例池中获取)。所以ADataSource只会创建1次。
      • 使用@Configuration修饰配置类,会在spring容器中产生一个配置类bean实例,其内部的@Bean修饰的方法产生的bean实例都是单例的
    • 疑问2:如果去掉@Configuration注解只会会如何?
      • 经过测试,程序不会报错,ADataSource和Brunner两个bean都会交给spring容器管理,但是区别在于ADataSource创建了2次:
        在这里插入图片描述
      • 为什么创建两次?
        • 第一次创建是使用@Bean注解修饰aDataSource方法的时候,创建的。第二次创建是@Bean注解修饰bRunner方法的时候,由于BRunner对象的创建需要依赖于ADataSource。此时ADataSource这个bean不是从单例池中获取,而是重新又创建了1次。
        • 不使用@Configuration修饰配置类,则不会在spring容器中产生一个配置类bean实例,在内部存在相互依赖时,使用方法名调用时可能会产生多个bean实例
    • 疑问3:如何不使用@Configuration也保证ADataSource只创建1次?
      • 使用形参的方式@Bean声明在有形参的方法前,为形参赋值时会从spring容器中寻找一个符合形参类型的bean实例为形参赋值(类似于@Autowired :存在符合条件的多个bean实例时,会先筛选类型,再需寻找形参名的bean实例,若二者筛选唯一,则为形参赋值成功,否则赋值失败)

6.6 Spring整合Junit

  • @Runwith

    • 使用junit提供的一个注解(@Runwith)把原有的main方法替换成spring提供的main方法。
    • 该注解的功能是替换Runner(运行器)
  • @ContextConfiguration

    • 作用: 告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置

    • 属性

      • locations:指定xml文件的位置,加上classpath关键字,表示在类路径下。
      • classes:指定配置类所在地位置。

      在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/461420
推荐阅读
相关标签
  

闽ICP备14008679号