Spring IoC
目录
1. Spring核心之IoC控制反转
1.1 IoC的概念
IoC
—Inversion of Control
,即“控制反转”,不是什么技术,而是一种设计思想。- IoC是指在程序开发中,实例的创建不再由调用者管理,而是由Spring容器创建。Spring容器会负责控制程序之间的关系,而不是由程序代码直接控制,因此,控制权由程序代码转移到了Spring容器中,控制权发生了反转,这就是Spring的IoC思想。
- Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IoC容器中取出需要的对象。
- 创建实例/对象:构造方法
new
、反射、克隆、序列化、动态代理
1.2 Spring入门案例
1.2.1 创建maven项目
- 创建完整的目录结构 - 此处只需要创建一个Maven项目即可.
1.2.2 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>
1.2.3 创建一个实体类
/**
* 球队实体类
*
* @author murphy
* @since 2021/6/19 4:25 下午
*/
public class Team {
private Integer id;
private String name;
private String location;
public Team() {
System.out.println("Team - 默认构造方法:id=" + id + ",name=" + name + ",location=" + location);
}
}
1.2.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">
<!--
Spring 配置文件:
beans:根标签 - Spring中Java的对象称为Java Bean
spring-beans.xsd:约束文件 - 约束XML文件中能编写哪些标签
-->
<!--
创建对象:声明bean,告知Spring容器创建哪些对象 - 一个bean标签表示一个对象
id = "对象名" - 要求唯一值
class = "完全限定名" - Spring底层通过反射方式创建对象,不能写接口
相当于 com.murphy.pojo.Team team1 = new com.murphy.pojo.Team();
然后将创建的对象放入Spring容器的一个集合Map中
springMap.put(id,对象) - 例如:springMap.put("team1", new Team());
-->
<bean id="team1" class="com.murphy.pojo.Team" />
<bean id="team2" class="com.murphy.pojo.Team" />
<!-- 非自定义对象的创建 -->
<bean id="date1" class="java.util.Date"/>
</beans>
1.2.5 使用Spring容器创建对象
配置文件中创建对象,代码如上
<bean id="team1" class="com.murphy.pojo.Team" />
<bean id="team2" class="com.murphy.pojo.Team" />
1.2.6 获取Spring容器
Spring 提供了两种 IoC 容器,分别为BeanFactory
和ApplicationContext
.
1.2.6.1 BeanFactory
BeanFactory
是基础类型的 IoC 容器,是一个管理Bean的工厂,它主要负责初始化各种Bean,并调用它们的生命周期方法。BeanFactory
接口有多个实现类,最常见的是org.Springframework.beans.factory.xml.XmlBeanFactory
,它是根据XML配置文件中的定义装配Bean的。
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(Spring配置文件的名称 - 全路径));
1.2.6.2 ApplicationContext
ApplicationContext
是BeanFactory的子接口,也被称为应用上下文。它不仅提供了BeanFactory的所有功能,还添加了对i18n
(国际化)、资源访问、事件传播等方面的良好支持。- ApplicationContext接口有两个常用的实现类:
1.2.6.2.1 ClassPathXmlApplicationContext —— 常用
- 该类从类路径
ClassPath
中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(Spring配置文件的名称);
1.2.6.2.2 FileSystemXmlApplicationContext
- 它与ClassPathXmlApplicationContext的区别是:在读取Spring的配置文件时,
FileSystemXmlApplicationContext
不再从类路径中读取配置文件,而是通过参数指定配置文件的位置,它可以获取类路径之外的资源,如“D:\application.xml”。
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
1.2.7 通过上下文对象获取容器中的对象
import com.murphy.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;
/**
* 测试类
*
* @author murphy
* @since 2021/6/19 4:27 下午
*/
public class Test01 {
@Test
public void test01(){
System.out.println("原有写法:");
// 原有写法:自己创建对象
Team team = new Team();
// Spring容器创建对象的方式
String springConfig = "applicationContext.xml";
// 1. 获取容器
// 1.1 方式一 - 推荐使用
System.out.println("ApplicationContext:");
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(springConfig);
// 从容器中根据ID获取对象 - 默认Object类型
Team team1 = (Team) applicationContext.getBean("team1");
// 1.2 方式二 - 已过时,不推荐
System.out.println("BeanFactory:");
BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource("配置文件全路径"));
// 根据ID从IoC容器中获取对象 - Object类型
beanFactory.getBean("team1");
// 1.3 方式三 - ApplicationContext
ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("配置文件全路径");
// 2. 容器中的其他API
// 获取bean定义的数量
int beanDefinitionCount = applicationContext.getBeanDefinitionCount();
System.out.println("容器中的对象的个数:" + beanDefinitionCount);
System.out.println("容器中所有对象的名称:");
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName + "\t");
}
// 3. 获取非自定义的对象
Date date1 = (Date) applicationContext.getBean("date1");
System.out.println("获取非自定义的对象date = " + date1);
}
}
1.2.8 创建非自定义对象
<!--创建非自定义的对象-->
<bean id="date1" class="java.util.Date"></bean>
// 获取非自定义的对象 - 测试方法
Date date1 = (Date) applicationContext.getBean("date1");
System.out.println("获取非自定义的对象date = " + date1);
1.2.9 Bean标签的属性
示例演示:
Team 实体类补充如下方法:
public void init(){
System.out.println("Team - init() ---");
}
public void destroy(){
System.out.println("Team - destroy() ---");
}
applicationContext.xml
补充的Bean属性及运用:
<!--
scope = "singleton / prototype"
singleton: 单例,默认值,容器启动完成之后单例对象就被创建了,而且容器中只有唯一的一个对象,每次获取都是单一对象
prototype: 多例,多例的对象在每次获取的时候都创建新对象
lazy-init = "true / false" - 是否懒加载,默认值false,针对单例对象
true: 懒加载,获取对象的时候才创建对象
false: 不懒加载,立即创建对象,不管是否使用
生命周期相关:
init-method = "对象创建完毕之后立即调用的初始化方法"
destroy-method = "spring容器调用销毁方法时执行的方法"
-->
<bean id="team1" name="team1" class="com.murphy.pojo.Team" scope="singleton" />
<bean id="team2" name="team2" class="com.murphy.pojo.Team" scope="prototype" />
<bean id="team3" name="team3" class="com.murphy.pojo.Team" scope="singleton" lazy-init="true" init-method="init" destroy-method="destroy"/>
测试类:
@Test
public void test02(){
String springConfig = "applicationContext.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);
Team team3 = (Team) applicationContext.getBean("team3");
System.out.println(team3);
applicationContext.close();
}
图解说明:
scope="singleton/prototype"
- 单例模式多次取值对象单一,只创建一次对象 / 多例模式每次取值都要创建新对象
lazy-init = "true/false"
- 针对单例模式,是否懒加载
- 懒加载
true
- 获取对象的时候才创建对象
- 非懒加载
false
- 立即创建对象,不管是否使用,默认值
init-method
- 对象创建完毕之后立即调用的初始化方法 /destroy-method
- spring容器调用销毁方法时执行的方法
1.2.10 Bean的作用域
1.3 Spring容器创建对象的方式
1.3.1 使用默认的构造方法
<!-- 1. 通过默认构造方法 -->
<bean id="team1" class="com.murphy.pojo.Team"/>
1.3.2 使用带参数的构造方法
<!-- 2. 通过带参数的构造方法 -->
<!-- 方式1:根据参数的属性名称 -->
<bean id="team2" class="com.murphy.pojo.Team">
<constructor-arg name="id" value="1001"/>
<constructor-arg name="name" value="Warriors"/>
<constructor-arg name="location" value="Oakland"/>
</bean>
<!-- 方式2:根据参数的下标索引 -->
<bean id="team3" class="com.murphy.pojo.Team">
<constructor-arg index="0" value="1002"/>
<constructor-arg index="1" value="Heat"/>
<constructor-arg index="2" value="Miami"/>
</bean>
1.3.3 使用工厂类
<!-- 3. 通过工厂方法:实例方法 / 静态方法 -->
<!-- 3.1 实例方法 -->
<bean id="factory" class="com.murphy.pojo.MyFactory"/>
<bean id="instanceTeam" factory-bean="factory" factory-method="instanceFun"/>
<!-- 3.2 静态方法 -->
<bean id="staticTeam" class="com.murphy.pojo.MyFactory" factory-method="staticFun"/>
三种方式的案例如下:
Team.java
类中添加带参数构造方法:
public Team(Integer id, String name, String location) {
this.id = id;
this.name = name;
this.location = location;
System.out.println("Team - 带参数的构造方法:id=" + id + ",name=" + name + ",location=" + location);
}
创建工厂类MyFactory.java
:
/**
* 工厂方法
*
* @author murphy
*/
public class MyFactory {
/**
* 实例方法
* @return
*/
public Team instanceFun() {
System.out.println("MyFactory - instanceFun()");
return new Team(1003,"Net","Brooklyn");
}
/**
* 静态方法
* @return
*/
public static Team staticFun() {
System.out.println("MyFactory - staticFun()");
return new Team(1004,"Lakers","Los Angels");
}
public static void main(String[] args) {
MyFactory factory = new MyFactory();
Team team = factory.instanceFun();
MyFactory.staticFun();
}
}
创建新的配置文件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.murphy.pojo.Team"/>
<!-- 2. 通过带参数的构造方法 -->
<!-- 方式1:根据参数的属性名称 -->
<bean id="team2" class="com.murphy.pojo.Team">
<constructor-arg name="id" value="1001"/>
<constructor-arg name="name" value="Warriors"/>
<constructor-arg name="location" value="Oakland"/>
</bean>
<!-- 方式2:根据参数的下标索引 -->
<bean id="team3" class="com.murphy.pojo.Team">
<constructor-arg index="0" value="1002"/>
<constructor-arg index="1" value="Heat"/>
<constructor-arg index="2" value="Miami"/>
</bean>
<!-- 3. 通过工厂方法:实例方法 / 静态方法 -->
<!-- 3.1 实例方法 -->
<bean id="factory" class="com.murphy.pojo.MyFactory"/>
<bean id="instanceTeam" factory-bean="factory" factory-method="instanceFun"/>
<!-- 3.2 静态方法 -->
<bean id="staticTeam" class="com.murphy.pojo.MyFactory" factory-method="staticFun"/>
</beans>
测试类CreateTypeTest.java
:
/**
* 创建对象 测试类
*
* @author murphy
*/
public class CreateTypeTest {
@Test
public void test1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("createType.xml");
}
}
测试结果:
1.4 基于XML的DI
DI — Dependency Injection
,即依赖注入:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。IoC
是一个概念,是一种思想,其实现方式多种多样。依赖注入就是其中用的比较多的一种方式。- Ioc和DI是同一个概念的不同角度描述 。IoC是一种思想、概念,DI是实现它的手段。Spring框架使用依赖注入实现IoC。
- Spring容器是一个超级大工厂,负责创建、管理所有的Java对象,这些Java对象被称为Bean。Spring容器管理着容器中Bean之间的依赖关系,Spring 使用依赖注入的方式来管理 Bean 之间的依赖关系。使用
IoC
实现对象之间的解耦和。
1.4.1 注入分类
bean
实例在调用无参构造器创建对象后,就要对bean对象的属性进行初始化。初始化是由容器自动完成的,称为注入。
1.4.1.1 通过set方法
set注入
也叫设值注入是指,通过setter方法
传入被调用者的实例。这种注入方式简单、直观,因而在Spring的依赖注入中大量使用。
1.4.1.2 通过构造方法
- 构造注入是指在构造调用者实例的同时,完成被调用者的实例化,使用构造器设置依赖关系。
1.4.1.3. 自动注入
- 对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签设置
autowire
属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)。根据自动注入判断标准的不同,可以分为两种:byName
:根据名称自动注入byType
: 根据类型自动注入
byName
- 当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。
byType
- 使用
byType
方式自动注入,要求:配置文件中被调用者bean的class
属性指定的类,要与代码中调用者bean
类的某引用类型属性类型同源。即要么相同,要么有is-a
关系(子类,或是实现类)。但这样的同源的被调用bean只能有一个,多于一个,容器就不知该匹配哪一个了。
示例:
创建TeamDao.java
/**
* Dao
*
* @author murphy
*/
public class TeamDao {
public void add() {
System.out.println("TeamDao - add---");
}
}
创建TeamService.java
/**
* Service
*
* @author murphy
*/
public class TeamService {
private TeamDao teamDao;
public void add() {
teamDao.add();
System.out.println("TeamService - add---");
}
public TeamService(TeamDao teamDao) {
this.teamDao = teamDao;
}
public TeamService() {
}
public TeamDao getTeamDao() {
return teamDao;
}
public void setTeamDao(TeamDao teamDao) {
this.teamDao = teamDao;
}
}
创建配置文件DI.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="teamDao" class="com.murphy.dao.TeamDao"/>
<bean id="teamService" class="com.murphy.service.TeamService">
<!-- 使用setter方法注入属性值 -->
<property name="teamDao" ref="teamDao"></property>
</bean>
<bean id="teamService2" class="com.murphy.service.TeamService">
<!-- 使用构造方法注入 -->
<constructor-arg name="teamDao" ref="teamDao"></constructor-arg>
</bean>
<bean id="teamService3" class="com.murphy.service.TeamService" autowire="byName">
<!-- 查询与属性名相同的容器中的对象 -->
</bean>
<bean id="teamService4" class="com.murphy.service.TeamService" autowire="byType">
<!-- 查询与属性值相同类型的容器中的对象,但是要求类型相同的对象唯一,否则抛出异常:无法精确匹配 -->
</bean>
</beans>
创建测试类:
import com.murphy.dao.TeamDao;
import com.murphy.service.TeamService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* DITest
*
* @author murphy
*/
public class DITest {
@Test
public void test1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("DI.xml");
TeamService teamService = (TeamService) applicationContext.getBean("teamService");
teamService.add();
System.out.println("--------------------");
TeamService teamService2 = (TeamService) applicationContext.getBean("teamService2");
teamService2.add();
System.out.println("--------------------");
TeamService teamService3 = (TeamService) applicationContext.getBean("teamService3");
teamService3.add();
System.out.println("--------------------");
TeamService teamService4 = (TeamService) applicationContext.getBean("teamService4");
teamService4.add();
}
}
1.5 基于注解实现IoC
- 对于DI使用注解,将不再需要在Spring配置文件中声明bean实例。Spring中使用注解,需要在原有Spring运行环境基础上再做一些改变。
1.5.1 声明Bean的注解 @Component
- 在类上添加注解
@Component
表示该类创建对象的权限交给Spring容器。注解的value属性用于指定bean
的id
值,value可以省略。 @Component
不指定value属性,bean的
id是类名的首字母小写。
/**
* 球队实体类
*
* @author murphy
*/
@Component
public class Team {
private Integer id;
private String name;
private String location;
public Team() {
System.out.println("Team - 默认构造方法:id=" + id + ",name=" + name + ",location=" + location);
}
}
除此之外,Spring中还提供了其他 3 个用于创建对象的注解:
@Repository
:用于dao实现类的的注解
/**
* Dao
* @Repository
* @Component(value = "teamDao")
* @Component 注解标识在类上,表示对象由Spring容器创建 - value属性表示创建的ID值 / value可以省略,值可以省略,默认类名的首字母小写
* @author murphy
*/
@Repository
public class TeamDao {
public TeamDao() {
System.out.println("TeamDao - 默认的构造方法");
}
}
@Service
:用户service实现类的注解
/**
* Service
* @Service
* @author murphy
*/
@Service
public class TeamService {
public TeamService() {
System.out.println("TeamService - 默认的构造方法");
}
}
@Controller
:用于controller实现类的注解
/**
* Controller
* @Controller
* @author murphy
*/
@Controller
public class TeamController {
public TeamController() {
System.out.println("TeamController - 默认的构造方法");
}
}
这三个注解与@Component
都可以创建对象,但这三个注解还有其他的含义,@Service
创建业务层对象,业务层对象可以加入事务功能,@Controller
注解创建的对象可以作为处理器接收用户的请求。@Repository
,@Service
,@Controller
是对@Component
注解的细化,标注不同层的对象。即持久层对象,业务层对象,控制层对象。
测试类:
/**
* 注解 测试类
*
* @author murphy
*/
public class AnnotationTest {
@Test
public void test() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("annotation.xml");
}
}
// ---------------------------------------------------------------------------------------------------------
运行结果:
TeamDao - 默认的构造方法
TeamService - 默认的构造方法
TeamController - 默认的构造方法
Team - 默认构造方法:id=null,name=null,location=null
1.5.2 包扫描
- 需要在Spring配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。 如果没有报扫描,添加的创建对象的注解不生效 。
- 如果要扫描的包有多个,可以有以下方式扫描:
- 使用多个
context:component-scan
指定不同的包路径 - 指定
base-package
的值使用分隔符- 分隔符可以使用逗号
(,)
、分号(;)
,还可以使用空格,不建议使用空格。
- 分隔符可以使用逗号
base-package
指定到父包名base-package
的值表是基本包,容器启动会扫描包及其子包中的注解,当然也会扫描到子包下级的子包。所以base-package可以指定一个父包就可以。- 但不建议使用顶级的父包,扫描的路径比较多,导致容器启动时间变慢。指定到目标包和合适的,也就是注解所在包全路径。
- 使用多个
<!-- annotation.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"
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
https://www.springframework.org/schema/context/spring-context.xsd">
<!--
表示告知Spring要扫描的包
这些包以及子包当中的类上如果添加了 @Component 注解,这些添加了注解的类就交给Spring容器创建对象
注意:在beans标签中添加
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
-->
<!-- 多个包的扫描:方式1 - 使用多个 <context:component-scan> 表明 -->
<context:component-scan base-package="com.murphy.dao"/>
<context:component-scan base-package="com.murphy.service"/>
<context:component-scan base-package="com.murphy.controller"/>
<!-- 多个包的扫描:方式2 - base-package 中直接声明要扫描的多个包,多个包使用逗号/分号/空格分隔,空格不推荐 -->
<context:component-scan base-package="com.murphy.dao,com.murphy.service,com.murphy.controller"/>
<!-- 多个包的扫描:方式3 - base-package 中直接声明被扫描的多个包的父包 -->
<context:component-scan base-package="com.murphy"/>
</beans>
1.5.3 属性注入 @Vaule
- 需要在属性上使用注解
@Value
,该注解的value属性用于指定要注入的值。使用该注解完成属性注入时,类中无需setter
。当然,若属性有setter,则也可将其加到setter上。 - 相当于
<property name="name" value="murphy"/>
字段属性注入:
/**
* 球队实体类
*
* @author murphy
*/
@Component
public class Team {
@Value("1001")
private Integer id;
@Value("Warriors")
private String name;
@Value("Oakland")
private String location;
public Team() {
System.out.println("Team - 默认构造方法:id=" + id + ",name=" + name + ",location=" + location);
}
@Override
public String toString() {
return "Team{" +
"id=" + id +
", name='" + name + '\'' +
", location='" + location + '\'' +
'}';
}
}
// --------------------------------------------------------------------------------------------------------
// 测试类
@Test
public void test() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("annotation.xml");
Team team = (Team) applicationContext.getBean("team");
System.out.println(team);
}
// 运行结果:
TeamDao - 默认的构造方法
TeamService - 默认的构造方法
TeamController - 默认的构造方法
Team - 默认构造方法:id=null,name=null,location=null
Team{id=1001, name='Warriors', location='Oakland'}
setter
属性注入:
/**
* 球队实体类
*
* @author murphy
*/
@Component
public class Team {
private Integer id;
private String name;
private String location;
@Value("1001")
public void setId(Integer id) {
this.id = id;
}
@Value("Warriors")
public void setName(String name) {
this.name = name;
}
@Value("Oakland")
public void setLocation(String location) {
this.location = location;
}
public Team() {
System.out.println("Team - 默认构造方法:id=" + id + ",name=" + name + ",location=" + location);
}
@Override
public String toString() {
return "Team{" +
"id=" + id +
", name='" + name + '\'' +
", location='" + location + '\'' +
'}';
}
}
// --------------------------------------------------------------------------------------------------------
// 测试类
@Test
public void test() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("annotation.xml");
Team team = (Team) applicationContext.getBean("team");
System.out.println(team);
}
// 运行结果:
TeamDao - 默认的构造方法
TeamService - 默认的构造方法
TeamController - 默认的构造方法
Team - 默认构造方法:id=null,name=null,location=null
Team{id=1001, name='Warriors', location='Oakland'}
1.5.4 byType自动注入 @Autowired
- 需要在引用属性上使用注解
@Autowired
,该注解默认使用按类型自动装配Bean的方式。使用该注解完成属性注入时,类中无需setter
。当然,若属性有setter,则也可将其加到setter上。
/**
* Service
* @Service
* @author murphy
* @since 2021/6/19 6:39 下午
*/
@Service
public class TeamService {
/**
* @Autowired 自动装配,自动按照类型装配
* required true - 不存在则装配失败 / false - 不存在则置为空指针
* @Qualifier 按照名称自动装配 - 需注解搭配使用
*/
@Autowired(required = true)
@Qualifier("teamDao")
private TeamDao teamDao;
public void add() {
teamDao.add();
System.out.println("TeamService - add---");
}
public TeamService() {
System.out.println("TeamService - 默认的构造方法");
}
public TeamService(TeamDao teamDao) {
this.teamDao = teamDao;
}
public void setTeamDao(TeamDao teamDao) {
this.teamDao = teamDao;
}
}
// 测试类
@Test
public void test() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("annotation.xml");
TeamService teamService = (TeamService) applicationContext.getBean("teamService");
teamService.add();
}
// 运行结果
TeamDao - add---
TeamService - add---
1.5.5 byName自动注入 @Autowired和@Qualifier
- 需要在引用属性上联合使用注解
@Autowired
与@Qualifier
。@Qualifier
的value
属性用于指定要匹配的Bean的id
值。类中无需set方法,也可加到set方法上。 @Autowired
还有一个属性required
,默认值为true
,表示当匹配失败后,会终止程序运行。若将其值设置为false
,则匹配失败,将被忽略,未匹配的属性值为null
。
1.5.6 自动注入 @Resource
- Spring提供了对JDK中@Resource注解的支持。
@Resource
注解既可以按名称匹配Bean,也可以按类型匹配Bean。 - 默认是按名称注入。使用该注解,要求JDK必须是6及以上版本。@Resource可在属性上,也可在set方法上。
/**
* Controller
* @Controller
* @author murphy
* @since 2021/6/19 10:05 下午
*/
@Controller
public class TeamController {
/**
* @Resource JDK > 1.6
* 默认按照名称,若名称不相符,则按照类型自动装配
* @Resource(name = "teamService", type = TeamService.class)
*/
@Resource
private TeamService teamService;
public void add() {
teamService.add();
System.out.println("TeamController - add---");
}
public TeamController() {
System.out.println("TeamController - 默认的构造方法");
}
}
// 测试类
@Test
public void test() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("annotation.xml");
TeamController teamController = (TeamController) applicationContext.getBean("teamController");
teamController.add();
}
// 运行结果
TeamDao - add---
TeamService - add---
TeamController - add---
byType
注入引用类型属性
@Resource
注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入bean,则会按照类型进行Bean的匹配注入。
byName
注入引用类型属性
@Resource
注解指定其name
属性,则name
的值即为按照名称进行匹配的Bean的id。
@Resource
和@Autowired
的区别:
- 都是用来自动装配的,都可以放在属性字段上
@Autowired
通过byType
的方式实现,而且必须要求这个对象存在!@Resource
通过byName
的方式实现,如果找不到名字,则通过byType
实现!两者都失败的情况下则报错。- 执行顺序不同:
@Autowired
通过byType
的方式实现,@Resource
通过byName
的方式实现。