1、IOC概念
1、IOC-Inversion of Control,即控制反转,不是什么技术,而是一种设计思想。
2、IoC 是指在程序开发中,实例的创建不再由调用者管理,而是由 Spring 容器创建。Spring 容器会负责控 制程序之间的关系,而不是由程序代码直接控制,因此,控制权由程序代码转移到了 Spring 容器中,控 制权发生了反转,这就是 Spring 的 IoC 思想。
2、Spring入门案例
2.1、创建maven项目
pom.xml文件
<dependencies>
<!--单元测试--> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--spring依赖--> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--编译插件--> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2.2、创建一个实体类
public class Team {
private Integer id;
private String name;
private String location;
public Team() { System.out.println("Team的默认构造方法被调用:id="+id+",name="+name+",
location="+location);
}
}
2.3、创建Spring的配置文件application.xml
2.4、使用Spring容器创建对象
配置文件中创建对象
<?xml version="1.0" encoding="UTF-8"?>
<!--spring的配置文件
1、beans: 根标签,spring中java的对象成为bean 2、spring-beans.xsd 是约束文件(约束XML文件中能编写哪些标签)-->
<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,通知spring要创建哪个类的对象 一个bean标签声明一个对象:
名
-->
id="自定义的对象名称" ,要求唯一
class="类的完全限定名" 包名+类名,spring底层是反射机制创建对象,所以必须使用类
相当于 Team team1=new Team();创建好的对象放入一个集合Map中 例如:springMap.put("team1",new Team());
<bean id="team1" class="com.kkb.pojo.Team"></bean>
</beans>
2.5、获取Spring容器
Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext.
2.5.1、BeanFactory
BeanFactory 是基础类型的 IoC 容器,是一个管理 Bean 的工厂,它主要负责初始化各种 Bean,并调用 它们的生命周期方法。
BeanFactory 接口有多个实现类,最常见的是org.Springframework.beans.factory.xml.XmlBeanFactory,它是根据 XML 配置文件中的定义装配 Bean 的。
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(Spring配置文件 的名称));
2.5.2、ApplicationContext
ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。它不仅提供了 BeanFactory 的 所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。
ApplicationContext 接口有两个常用的实现类:
- ClassPathXmlApplicationContext——常用
该类从类路径 ClassPath 中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作
ApplicationContext applicationContext=new ClassPathXmlApplicationContext(Spring配 置文件的名称);
- FileSystemXmlApplicationContext
ApplicationContext applicationContext = new
FileSystemXmlApplicationContext(String configLocation);
它与 ClassPathXmlApplicationContext 的区别是:在读取 Spring 的配置文件时, FileSystemXmlApplicationContext 不再从类路径中读取配置文件,而是通过参数指定配置文件的位 置,它可以获取类路径之外的资源,如“D:\application.xml”。
2.6、通过上下文对象获取容器中的对象
import com.pojo.Team;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import java.util.Date;
public class Test1 {
@Test
public void test01(){ //使用spring容器创建对象 //1、指定spring配置文件的名称
String springConfig="application.xml"; //2、创建spring容器的对象: //方式1:不推荐,了解
//BeanFactory beanFactory = new XmlBeanFactory(new
FileSystemResource("D:/workspaces/ideaProjects/MySpring/spring01/src/main/resour
ces/application.xml"));
//beanFactory.getBean("team1");//根据ID从IOC容器获取对象 //方式2:applicationContext--常用
ApplicationContext applicationContext=new
ClassPathXmlApplicationContext(springConfig);//这里执行完毕容器中的对象都已经创建完成
//方式3:applicationContext--了解
//ApplicationContext applicationContext2 = new
FileSystemXmlApplicationContext("D:/workspaces/ideaProjects/MySpring/spring01/sr
c/main/resources/application.xml");
//3、获取容器中的对象
Team team1= (Team) applicationContext.getBean("team1"); //4、容器其他api
int beanDefinitionCount = applicationContext.getBeanDefinitionCount(); System.out.println("spring容器中对象的个数:"+beanDefinitionCount); String[] beanDefinitionNames =
applicationContext.getBeanDefinitionNames(); System.out.println("spring容器中所有对象的名称:"); for (String name : beanDefinitionNames) {
}
}
2.7、创建非自定义对象
<!--创建非自定义的对象-->
<bean id="date" class="java.util.Date"></bean>
上面的测试方法中添加如下内容:
//5、获取日期对象
Date date1= (Date) applicationContext.getBean("date1"); System.out.println("日期:"+date1);
2.8、Bean标签的属性
属性 | 说明 |
---|---|
class | 指定bean对应类的全路径 |
name | name是bean对应对象的一个标识 |
scope | 执行bean对象创建模式和生命周期,scope="singleton"和scope=“prototype” |
id | id是bean对象的唯一标识,不能添加特别字符 |
lazy-init | 是否延时加载 默认值:false。true 延迟加载对象,当对象被调用的时候才会加载,测试 的时候,通过getbean()方法获得对象。lazy-init=“false” 默认值,不延迟,无论对象 是否被使用,都会立即创建对象,测试时只需要加载配置文件即可。注意:测试的时候只 留下id,class属性 |
init- method | 只需要加载配置文件即可对象初始化方法 |
destroy- method | 对象销毁方法 |
示例演示:
Team实体类补充如下方法:
public void init(){
System.out.println("Team ---- init()");
}
public void destroy(){
System.out.println("Team ---- destroy()");
}
application.xml配置文件添加如下内容:
<!-- bean标签的属性:
id="自定义的对象名称" ,要求唯一 name="bean对于的一个标识“,一般使用id居多 class="类的完全限定名" scope="singleton/prototype" 单例/多例
singleton:默认值,单例:在容器启动的时候就已经创建了对象,而且整个容器只有为一个
的一个对象
prototype:多例,在使用对象的时候才创建对象,每次使用都创建新的对象 lazy-init="true/false" 是否延迟创建对象,只针对单例有效
true:不延迟创建对象,容器加载的时候立即创建
false:默认加载,使用对象的时候才去创建对象 init-method="创建对象之后执行的初始化方法" destroy-method="对象销毁方法,调用容器destroy方法的时候执行"
-->
<bean id="team2" class="com.kkb.pojo.Team" scope="singleton" lazy-
init="true" init-method="init" destroy-method="destroy"/>
<bean id="team3" class="com.kkb.pojo.Team" scope="prototype" />
@Test
public void test02(){
String springConfig="application.xml";
ClassPathXmlApplicationContext applicationContext=new
ClassPathXmlApplicationContext(springConfig);
Team team1 = (Team) applicationContext.getBean("team1");
Team team11 = (Team) applicationContext.getBean("team1");
System.out.println(team1);
System.out.println(team11);
Team team2 = (Team) applicationContext.getBean("team2");
Team team22 = (Team) applicationContext.getBean("team2");
System.out.println(team2);
System.out.println(team22);
applicationContext.close();//关闭容器
}
3、Spring容器创建对象的方式
1、使用默认的构造方法
2、使用带参的构造方法
3、使用工厂类
public class MyFactory {
/**
* 实例方法 * @return */
public Team instanceFun(){
System.out.println("MyFactory------instanceFun");
return new Team(1003,"湖人","洛杉矶");
}
/**
* 静态方法 * @return */
public static Team staticFun(){
System.out.println("MyFactory------staticFun");
return new Team(1004,"小牛","达拉斯");
}
public static void main(String[] args) {
Team team1 = MyFactory.staticFun();
MyFactory factory=new MyFactory();
Team team = factory.instanceFun();
} }
创建新的配置文件createType.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">
<!--spring容器创建对象的方式: 1、通过默认构造方法
2、通过带参数的构造方法
3、通过工厂方法:实例方法,静态方法--> <!--1、通过默认构造方法-->
<bean id="team1" class="com.kbb.pojo.Team"></bean> <!-- 2、通过带参数的构造方法-->
<bean id="team2" class="com.kbb.pojo.Team">
<!--name:表示参数的名称-->
<constructor-arg name="id" value="1001"/> <constructor-arg name="name" value="勇士"/> <constructor-arg name="location" value="金州"/>
</bean>
<bean id="team3" class="com.kbb.pojo.Team">
<!--index:表示参数的下标索引--> <constructor-arg index="0" value="1002"/> <constructor-arg index="1" value="热火"/> <constructor-arg index="2" value="迈阿密"/>
</bean>
<!--3、通过工厂方法: 3.1 静态方法
Team team1 = MyFactory.staticFun();-->
<bean id="staticTeam" class="com.kbb.pojo.MyFactory" factory-
method="staticFun"></bean>
<!--3、通过工厂方法: 3.2 实例方法
MyFactory factory=new MyFactory();
Team team = factory.instanceFun();-->
<bean id="factory" class="com.kbb.pojo.MyFactory"></bean>
<bean id="instanceTeam" factory-bean="factory" factory-method="instanceFun">
</bean>
</beans>
测试类:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CreateTypeTest {
@Test
public void test01(){
ApplicationContext ac=new
ClassPathXmlApplicationContext("createType.xml");
}
}
4、基于XML的DI
-
DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即 由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为 了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过 简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资 源来自何处,由谁实现。
-
IoC 是一个概念,是一种思想,其实现方式多种多样。依赖注入就是其中用的比较多的一种方式。
-
Ioc和DI是同一个概念的不同角度描述。IoC是一种思想,概念,DI是实现它的手段。Spring框架使用依
赖注入实现IoC. -
Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。
4.1、注入分类
bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完
成的,称为注入。
4.1.1、通过set方法
set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在
Spring 的依赖注入中大量使用。
4.1.2、通过构造方法
构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。使用构造器设置依赖关系。
4.1.3、自动注入
对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签
设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属 性)。根据 自动注入判断标准的不同,可以分为两种:
- byName:根据名称自动注入
- byType: 根据类型自动注入
1、byName
当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式, 让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的 被调用者 bean 的 id 进行比较而实现自动注入的。
2、byType
使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用 者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样 的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。
5、基于注解实现IOC
对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。Spring 中使用注解,需要在原有
Spring 运行环境基础上再做一些改变。
5.1、声明Bean的注解 @Compent
在类上添加注解@Component表示该类创建对象的权限交给Spring容器。注解的value属性用于指定 bean的id值,value可以省略。
@Component 不指定 value 属性,bean 的 id 是类名的首字母小写。
除此之外,Spring中还提供了其他3个用于创建对象的注解:
@Repository : 用于dao实现类的的注解
@Service: 用户service实现类的注解
@Controller: 用于controller实现类的注解
这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义,@Service创建业务层对 象,业务层对象可以加入事务功能,@Controller 注解创建的对象可以作为处理器接收用户的请求。
@Repository,@Service,@Controller 是对@Component 注解的细化,标注不同层的对象。即持久 层对象,业务层对象,控制层对象。
5.2、包扫描
需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。如果没有包扫描,添加的
创建对象的注解不生效。
如果要扫描的包有多个,可以有以下方式扫描:
1、使用多个context:component-scan指定不同的包路径
2、指定 base-package的值使用分隔符
分隔符可以使用逗号(,)分号(;)还可以使用空格,不建议使用空格。
<!--多个包的扫描: 方式2 : base-package中直接声明要扫描的多个包 ,多个值用逗号,分号或者空格 分割,但是空格不推荐-->
<context:component-scan base-
package="com.kkb.dao,com.kkb.service,com.kkb.controller"></context:component-
scan>
3、base-package是指定到父包名
base-package 的值表示基本包,容器启动会扫描包及其子包中的注解,当然也会扫描到子包下级的子
包。所以 base-package 可以指定一个父包就可以。 但不建议使用顶级的父包,扫描的路径比较多,导致容器启动时间变慢。指定到目标包和合适的。也就
是注解所在包全路径。
<!--多个包的扫描: 方式3: base-package中直接声明要扫描的多个包的父包--> <context:component-scan base-package="com.kkb"></context:component-scan>
5.3、属性注入(@value)
需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。使用该注解完成属性注入 时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。
5.4、byType自动注入@Autowired
需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配 Bean 的方式。使用该注解完
成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。
5.5、byName自动注入@Autowired和@Qualifier
需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用于指定要匹配 的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。
@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行。若将其值 设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。
5.6、自动注入@Resource
Spring提供了对 jdk中@Resource注解的支持。@Resource 注解既可以按名称匹配Bean,也可以按类 型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。@Resource 可在属性 上,也可在 set 方法上。
5.6.1、byType注入引用类型属性
@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型
进行 Bean 的匹配注入。
5.6.2、byName注入引用类型属性
@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。