一,Spring的简介和安装,深入理解IOC容器及测试

1) IOC/DI (IOC容器)

2) AOP面向切面编程

3) 声明式事务

官网地址:spring是一家公司-商业化运作。apache是一家基金会组织,接收社会的捐赠。

https://spring.io/

io表示开发者平台

 

 

 

 

1. Spring Framework

Spring 基础框架,可以视为 Spring 基础设施,基本上任何其他 Spring 项目都是以 Spring Framework 为基础的。

Spring5,基于jdk1.8

1.1 Spring Framework的优良特性

非侵入式:使用 Spring Framework 开发应用程序时,Spring 对应用程序本身的结构影响非常小。对领域模型Java Bean)可以做到零污染;对功能性组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构,反而能将组件结构进一步简化。这就使得基于 Spring Framework 开发应用程序时结构清晰、简洁优雅。

控制反转:IOC——Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。

面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功能。

容器:Spring IOC 是一个容器,因为它包含并且管理组件对象的生命周期。组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大的降低了使用门槛,大幅度提高了开发效率。

组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用 XML 和 Java 注解组合这些对象。这使得我们可以基于一个个功能明确、边界清晰的组件有条不紊的搭建超大型复杂应用系统。

6 声明式:很多以前需要编写代码才能实现的功能,现在只需要声明需求即可由框架代为实现。

一站式:在 IOC 和 AOP 的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且 Spring 旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在 Spring Framework 的基础上全部使用 Spring 来实现。

 

1.2 Spring Framework五大功能模块

 

2. IOC容器的概念

2.1 生活中的普通容器

 

 

 

 

普通容器只能用来存储,没有更多功能。

 

2.2 程序中的普通容器

变量,数组,集合

 

普通容器只能用来存储,没有更多功能。

 

2.3 生活中的复杂容器

 

政府管理我们的一生,生老病死都和政府有关

 

4. IOC容器在Spring中的实现

Spring IOC 容器就是 IOC 思想的一个落地的产品实现。IOC 容器中管理的组件也叫做 bean在创建 bean 之前,首先需要创建 IOC 容器。Spring 提供了 IOC 容器的两种实现方式:

BeanFactory

这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。

 

ApplicationContex -- 我们自己使用

BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory

 

所以,以后在 Spring 环境下看到一个类或接口的名称中包含 ApplicationContext,那基本就可以断定,这个类或接口与 IOC 容器有关。

 

ApplicationContext的主要实现类

 

 

 

 

5. 基于XML管理bean

5.1 实验01[重要]创建bean,并根据id获取

5.1.1 目标

Spring IOC 容器创建类的对象,并根据beanid属性值来获取bean对象

5.1.2 思路

 

 

 

 

自定义一个类IEmpService => EmpServiceImpl,这个类在Spring的配置文件中进行配置,然后用ClassPathXMLApplicationContext这个类去读取配置文件,然后根据配置文件中配置的东西,将这个对象建立出来。

 

5.1.3 创建maven项目,导入依赖

完整的pom文件

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.hy</groupId>

<artifactId>mybatis001</artifactId>

<version>0.0.1</version>

<packaging>war</packaging>

 

<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<maven.compiler.encoding>UTF-8</maven.compiler.encoding>

</properties>

 

<dependencies>

<!-- Spring core-->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-context</artifactId>

<version>5.2.20.RELEASE</version>

</dependency>

 

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<version>1.18.20</version>

</dependency>

 

<!-- Spring-Test -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>5.2.20.RELEASE</version>

</dependency>

<!--junit的核心jar-->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

<scope>test</scope>

</dependency>

</dependencies>

 

<build>

<plugins>

<!-- 指定jdk,防止update project -->

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<configuration>

<source>1.8</source>

<target>1.8</target>

<!-- 项目编码 -->

<encoding>UTF-8</encoding>

</configuration>

</plugin>

</plugins>

</build>

</project>

导入一个依赖,传递过来剩下的jar文件

 

 

 

 

 

 

 

5.1.4 EmpService -> EmpServiceImpl

package com.hy.service.impl;

 

import com.hy.service.EmpService;

 

public class EmpServiceImpl implements EmpService {

@Override

public void reg() {

System.out.println("用户注册");

}

}

 

 

1) 先导入spring的相关环境,其实就是导入pom文件jar包的坐标gav

2) 创建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">

 

<!-- 实验01 [重要] 创建bean -->

<!-- bean标签,通过配置bean标签告诉IOC容器需要创建是哪个类的对象

id属性:当前bean的唯一标识,设定了这个唯一标识后,在其他地方就可以用来引用这个bean对象

class属性:指定这个bean要配置的类的全类名 -->

<bean id="empService" class="com.hy.service.impl.EmpServiceImpl" />

</beans>

这个bean没有属性,所以可以写成单标签

5.1.5 创建测试类

public class TestService {

// ApplicationContext就是我们的IOC容器

private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");

 

//实验01 [重要] 创建bean

//IOC容器中获取已配置的bean

@Test

public void testEmpService() {

//通过beanid属性的值得,获取这个bean对象

EmpService empService = (EmpService)iocContainer.getBean("empService");

empService.reg();

}

}

 

注意:Spring底层默认通过反射技术调用类的无参构造方法来创建类的对象。

5.1.6 无参构造方法的重要性,写一个有参的构造方法,测试

package com.hy.service.impl;

 

import com.hy.service.EmpService;

 

public class EmpServiceImpl implements EmpService {

 

public EmpServiceImpl(int age) {

}

 

@Override

public void reg() {

System.out.println("用户注册");

}

}

 

报错: 如果没有无参构造方法,则会抛出下面的异常:

 

 

 

 

所以对一个JavaBean来说,无参构造器属性的getXxx()setXxx()方法必须存在的,特别是在框架中。

 

5.1.7 IOC容器创建对象和自己建区别

 

 

被放入到容器当中,相当于加入了会员俱乐部

 

Spring环境下能够享受到的所有福利,都必须通过 IOC 容器附加到组件类上,所以随着我们在 Spring 中学习的功能越来越多,IOC 容器创建的组件类的对象就会比自己 new 的对象 其功能强大的越来越多。

 

5.2 实验02[重要]创建bean,并根据class类型来获取

5.2.1 指定类型的 bean 唯一(只有一个)

 

5.2.2 测试

package com.hy.test;

 

import org.junit.Test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

import com.hy.service.EmpService;

import com.hy.service.impl.EmpServiceImpl;

 

public class TestService2 {

// ApplicationContext就是我们的IOC容器

private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");

 

//实验02 [重要] 创建bean并且根据类型获取bean对象

//IOC容器中根据类型获取已配置的bean对象

@Test

public void testEmpService() {

// EmpService empService = iocContainer.getBean(EmpService.class);       //正确

EmpService empService = iocContainer.getBean(EmpServiceImpl.class);  //正确

empService.reg();  //用户注册

}

}

 

 

5.2.3 问题:如果配置了两个同样类型的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">

 

<!-- 实验01 [重要] 创建bean -->

<!-- bean标签,通过配置bean标签告诉IOC容器需要创建是哪个类的对象

id属性:当前bean的唯一标识,设定了这个唯一标识后,在其他地方就可以用来引用这个bean对象

class属性:指定这个bean要配置的类的全类名 -->

<bean id="empService" class="com.hy.service.impl.EmpServiceImpl" />

 

<bean id="empService2" class="com.hy.service.impl.EmpServiceImpl" />

</beans>

 

package com.hy.test;

 

import org.junit.Test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

import com.hy.service.EmpService;

import com.hy.service.impl.EmpServiceImpl;

 

public class TestService2 {

// ApplicationContext就是我们的IOC容器

private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");

 

//实验02 [重要] 创建bean并且根据类型获取bean对象

//如果容器中配置了同样类型的两个bean对象,

//IOC容器中根据类型获取已配置的bean对象时,则报错。

@Test

public void testEmpService() {

// EmpService empService = iocContainer.getBean(EmpService.class);       //失败

EmpService empService = iocContainer.getBean(EmpServiceImpl.class);  //失败

}

}

 

 

 

 

 

 

 

 

5.2.4 总结

如果一个类实现了某个接口,根据接口类型可以获取 bean 吗?

可以,前提是bean唯一,如下的配置就不行。

<bean id="empService" class="com.hy.service.impl.EmpServiceImpl" />

 

<bean id="empService2" class="com.hy.service.impl.EmpServiceImpl" />

 

如果一个接口有多个实现类,这些实现类都配置了 bean标签,根据接口类型可以获取 bean 吗?

不行,因为bean不唯一

 

根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。

5.3 实验03[重要] bean的属性赋值:通过setXxx方法注入

5.3.1 首先,给EmpServiceImpl添加一个属性,提供setXxxgetXxx方法

package com.hy.service.impl;

 

import com.hy.service.EmpService;

 

public class EmpServiceImpl implements EmpService {

private String empStatus;

 

 

public String getEmpStatus() {

return empStatus;

}

 

 

public void setEmpStatus(String empStatus) {

this.empStatus = empStatus;

}

@Override

public void reg() {

System.out.println("用户注册");

}

}

 

5.3.2 在配置时给通过property标签给属性赋值

 

 

 

 

 

<!-- 实验03 [重要] 创建bean,同时给bean的属性赋值:通过setXxx方法注入 -->

<bean id="empService3" class="com.hy.service.impl.EmpServiceImpl">

<!-- property标签:通过类的setXxx()方法给该类的对象设置属性值

name属性:指定属性名(其实这个名字是setXxxgetXx方法定义的,和属性名无关)

value属性:指定属性值

 -->

<property name="empStatus" value="积极"/>

</bean>

 

5.3.3 测试

package com.hy.test;

 

import org.junit.Test;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

import com.hy.service.impl.EmpServiceImpl;

 

public class TestService3 {

private ApplicationContext iocContainer = new ClassPathXmlApplicationContext("applicationContext.xml");

 

//实验03 [重要]

@Test

public void testEmpService() {

//通过beanid属性的值得,获取这个bean对象

EmpServiceImpl empService3 = (EmpServiceImpl)iocContainer.getBean("empService3");

System.out.println(empService3.getEmpStatus());  //积极

}

}

 

5.4 实验04[重要] 引入外部已声明的bean

<!-- 实验四 [重要]bean的属性赋值:引用外部已声明的bean -->

    <bean id="empMapper" class="com.hy.mapper.impl.EmpMapperImpl ">

        <property name="name" value="范冰冰"/>

    </bean>

 

    <bean id="empService" class="com.hy.service.impl.EmpServiceImpl ">

        <!-- 使用外部已经配置好的empMapper这个bean来给当前组件的happyMachine属性赋值 -->

        <!-- 引用另外一个bean不能再使用value属性,而要使用ref属性指定beanid -->

        <!--

            如果错把ref属性写成了value属性,会抛出异常:

            Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.' for property ' empMapper ': no matching editors or conversion strategy found

            意思是不能把String类型转换成我们要的EmpMapper类型

            说明我们使用value属性时,Spring只把这个属性看做一个普通的字符串,不会认为这是一个beanid,更不会根据它去找到bean来赋值

        -->

        <property name="empMapper" ref=" empMapper "/>

    </bean>

 

5.5 内部bean[重要]

 

<!-- 内部bean PersonServiceImpl里有一个Dept属性 -->

<bean id="personService" class="com.hy.service.impl.PersonServiceImpl">

<property name="dept" >

           <!-- property标签内部再配置一个bean,这就是内部bean

内部bean可以直接用来给property指定的属性赋值

-          ->

<!--  

内部bean,仅仅被外部bean使用,不能让其他的bean使用,所以内部bean不需要id

 -->

<bean class="com.hy.bean.Dept"/>

</property>

</bean>

 

5.6  引入外部属性文件[重要]

<!-- 实验六 [重要]bean的属性赋值:引入外部属性文件 -->

<!-- 使用context名称空间下的property-placeholder标签引入外部属性文件(本质上就是记录外部属性文件的位置) -->

    <!-- location属性:指定外部属性文件的路径。classpath:表示这个路径从类路径根目录开始 -->

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

 

    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">

        <!-- 通过 ${key}的形式引用外部属性文件中的数据 -->

        <property name="url" value="${jdbc.url}"/>

        <property name="driverClassName" value="${jdbc.driver}"/>

        <property name="username" value="${jdbc.user}"/>

        <property name="password" value="${jdbc.password}"/>

    </bean>

5.7 bean的属性赋值:级联属性赋值(不重要,不怎么使用)

使用内部bean,给级联属性赋值。

 

<!-- 实验七 给bean的属性赋值:级联属性赋值 -->

    <bean id="happyComponent7" class="com.hy.ioc.component.HappyComponent">

 

        <!-- 使用内部bean的形式将happyMachine属性初始化 -->

        <property name="happyMachine">

            <!-- 相当于创建了HappyMachine的空对象赋值给happyMachine属性 -->

            <bean class="com.hy.ioc.component.HappyMachine"/>

        </property>

 

        <!--

            如果happyMachine属性没有被初始化,那么直接访问级联属性会抛出异常:

            Caused by: org.springframework.beans.NullValueInNestedPathException: Invalid property 'happyMachine' of bean class [com.atguigu.ioc.component.HappyComponent]: Value of nested property 'happyMachine' is null

        -->

        <property name="happyMachine.machineName" value="aaa"/>

    </bean>

5.8 通过构造方法,给bean属性赋值[不重要]

<!-- 实验八 给bean的属性赋值:构造器注入 -->

    <bean id="happyTeam" class="com.hy.ioc.component.HappyTeam">

        <!-- 调用类的构造器创建对象并同时传入参数值 -->

        <!-- constructor-arg标签:给构造器对应位置传入参数 -->

        <!-- index属性:指定当前参数在参数列表中的索引位置 -->

        <!-- name属性:指定当前参数的参数名 -->

        <!-- value属性:指定参数值 -->

        <constructor-arg index="0" name="teamName" value="gaysTeam"/>

        <constructor-arg index="1" name="memberCount" value="10" />

        <constructor-arg index="2" name="memberSalary" value="555.55"/>

    </bean>

5.9 bean的集合属性赋值[不重要]

private List<String> nameList;  //标签不需要完全一致,一样可以赋值成功

private Map<String,String> nameMap;

 

<!-- 实验十一 给bean的属性赋值:集合属性 -->

    <bean id="classes" class="com.hy.ioc.component.Classes">

        <property name="nameList">

            <!-- list标签:准备一组集合类型的数据,给集合属性赋值 -->

            <!--<list>

                <value>member01</value>

                <value>member02</value>

                <value>member03</value>

            </list>-->

 

            <!-- 使用set标签也能实现相同效果,只是附带了去重功能 -->

            <!--<set>

                <value>member01</value>

                <value>member02</value>

                <value>member02</value>

            </set>-->

 

            <!-- array也同样兼容 -->

            <array>

                <value>member01</value>

                <value>member02</value>

                <value>member02</value>

            </array>

 

        </property>

 

        <property name="nameMap">

            <!-- Map类型的属性赋值 -->

            <!--<map>

                <entry key="财务部" value="张三"/>

                <entry key="行政部" value="李四"/>

                <entry key="销售部" value="王五"/>

            </map>-->

 

            <!-- 也可以使用props标签 -->

            <props>

                <prop key="财务部">张三2</prop>

                <prop key="行政部">李四2</prop>

                <prop key="销售部">王五2</prop>

            </props>

        </property>

    </bean>

5.10 自动装配 手动装配[了解]

所谓自动装配就是一个组件需要其他组件时,由 IOC 容器负责找到那个需要的组件,并装配进去。

    <!-- 实验十二 自动装配 -->

    <bean id="happyService3" class="com.hy.ioc.component.HappyService"/>

    <bean id="happyService2" class="com.hy.ioc.component.HappyService"/>

 

    <!-- 使用bean标签的autowire属性设置自动装配效果 -->

    <!-- byType表示根据类型进行装配,此时如果类型匹配的bean不止一个,那么会抛NoUniqueBeanDefinitionException -->

    <!-- byName表示根据beanid进行匹配。而beanid是根据需要装配组件的属性的属性名来确定的 -->

    <bean id="happyController"

          class="com.hy.ioc.component.HappyController"

          autowire="byName">

        <!-- 手动装配:在property标签中使用ref属性明确指定要装配的bean -->

        <!--<property name="happyService" ref="happyService"/>-->

    </bean>

@Test

public void testExperiment12() {

    HappyController happyController = iocContainer.getBean(HappyController.class);

    

    HappyService happyService = happyController.getHappyService();

    

    System.out.println("happyService = " + happyService);

}

5.11 bean的声明周期以及初始化

在无参构造方法打一个断点。

bean标签scope配置singleton时,bean的创建是在IOC容器初始化的时候,创建的,而不是在getBean的时候创建的。

bean标签scope配置prototype时,bean的创建实在调用getBean方法的时候创建的。

 

IOC容器关闭之前销毁bean对象。

 

 

 

 

 

 

 

这个接口总没有close方法在子接口中。

 

 

 

 

6,基于注解管理bean

XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。

本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。

 

 

 

 

1) 首先根据所需要的组件类型到IOC容器中查找

能够找到唯一的bean:直接执行装配

如果完全找不到匹配这个类型的bean:装配失败

和所需类型匹配的bean不止一个

没有@Qualifier注解:根据@Autowired标记位置成员变量的变量名作为beanid进行匹配

能够找到:执行装配

找不到:装配失败

使用@Qualifier注解:根据@Qualifier注解中指定的名称作为beanid进行匹配

能够找到:执行装配

找不到:装配失败

posted @ 2022-09-08 09:22  雾里看瓜  阅读(260)  评论(0编辑  收藏  举报