ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-09-08 09:31:15  阅读:77  来源: 互联网

标签:容器 Spring bean 简介 org com 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的优良特性

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

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

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

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

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

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

7 一站式:在 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 容器创建类的对象,并根据bean的id属性值来获取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() {

//通过bean的id属性的值得,获取这个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添加一个属性,提供setXxx和getXxx方法

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属性:指定属性名(其实这个名字是setXxx和getXx方法定义的,和属性名无关)

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() {

//通过bean的id属性的值得,获取这个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属性指定bean的id -->

        <!--

            如果错把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只把这个属性看做一个普通的字符串,不会认为这是一个bean的id,更不会根据它去找到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表示根据bean的id进行匹配。而bean的id是根据需要装配组件的属性的属性名来确定的 -->

    <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标记位置成员变量的变量名作为bean的id进行匹配

能够找到:执行装配

找不到:装配失败

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

能够找到:执行装配

找不到:装配失败

标签:容器,Spring,bean,简介,org,com,IOC
来源: https://www.cnblogs.com/lijili/p/16668322.html

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有