✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
个人主页:Java Fans的博客
个人信条:不迁怒,不贰过。小知识,大智慧。
当前专栏:SSM 框架从入门到精通
✨特色专栏:国学周更-心性养成之路
本文内容:一文吃透 Spring 中的IOC和DI
entity 包中的创建一个 Student 类和 Teacher 类,代码如下:
Student 类:
package cn.kgc.spring02.entity;
import lombok.Data;
@Data
public class Student {
private String name;
private Integer id;
private Teacher teacher;
}
Teacher 类:
package cn.kgc.spring02.entity;
import lombok.Data;
@Data
public class Teacher {
private String name;
private Double salary;
}
创建一个测试类和一个 shouldAnswerWithTrue 测试方法:
package cn.kgc.spring02;
import cn.kgc.spring02.entity.Student;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 如何通过配置 或注解完成自动注入
*/
public class AppTest {
@Test
public void shouldAnswerWithTrue(){
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = ac.getBean("student", Student.class);
System.out.println("student = " + student);
}
}
<bean id="student" class="cn.kgc.spring02.entity.Student" autowire="byType">
<property name="id" value="1">property>
<property name="name" value="tom">property>
bean>
<bean id="teacher" class="cn.kgc.spring02.entity.Teacher">
<property name="name" value="jack">property>
<property name="salary" value="200">property>
bean>
运行测试类中的 shouldAnswerWithTrue 方法,运行效果如下:
局限: 保证spring容器中只能有一个指定类型的bean
<bean id="student" class="cn.kgc.spring02.entity.Student" autowire="byName">
<property name="id" value="1">property>
<property name="name" value="tom">property>
bean>
<bean id="teacher" class="cn.kgc.spring02.entity.Teacher">
<property name="name" value="jack">property>
<property name="salary" value="200">property>
bean>
1.singleton 容器启动的时候创建对象,容器正常关闭时销毁对象
2.prototype 获取对象的时候创建对象,spring容器不负责对象的销毁
生命周期的过程:
1.调用无参创建对象
2.调用set方法初始化属性
3.调用初始化方法
4.对象创建完成,使用对象
5.关闭容器,调用销毁的方法
在配置文件中,除了可以定义 Bean 的属性值和相互之间的依赖关系,还可以声明 Bean 的作用域。例如,如果每次获取 Bean 时,都需要一个 Bean 实例,那么应该将 Bean 的 scope 属性定义为 prototype,如果 Spring 需要每次都返回一个相同的 Bean 实例,则应将 Bean 的 scope 属性定义为 singleton。
作用域的种类
Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的作用域。Spring 5 支持以下 6 种作用域。
1)singleton
默认值,单例模式,表示在 Spring 容器中只有一个 Bean 实例,Bean 以单例的方式存在。
<bean id="..." class="..." scope="singleton"/>
2)prototype
原型模式,表示每次通过 Spring 容器获取 Bean 时,容器都会创建一个 Bean 实例。
<bean id="..." class="..." scope="prototype"/>
3)request
每次 HTTP 请求,容器都会创建一个 Bean 实例。该作用域只在当前 HTTP Request 内有效。
4)session
同一个 HTTP Session 共享一个 Bean 实例,不同的 Session 使用不同的 Bean 实例。该作用域仅在当前 HTTP Session 内有效。
5)application
同一个 Web 应用共享一个 Bean 实例,该作用域在当前 ServletContext 内有效。
类似于 singleton,不同的是,singleton 表示每个 IoC 容器中仅有一个 Bean 实例,而同一个 Web 应用中可能会有多个 IoC 容器,但一个 Web 应用只会有一个 ServletContext,也可以说 application 才是 Web 应用中货真价实的单例模式。
6)websocket
websocket 的作用域是 WebSocket ,即在整个 WebSocket 中有效
除了用 XML 配置方式进行依赖注入外,还可以使用注解直接在类中定义 Bean 实例,这样就不再需要在 Spring 配置文件中声明 Bean 实例。使用注解,除了原有 Spring 配置,还要注意以下关键步骤。
在 spring-config.xml 开启注解的包扫描
<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">
<context:component-scan base-package="cn.kgc.spring02">context:component-scan>
beans>
@Component 该注解的 value 属性用于指定该 bean 的 id 值。例如在实体类 User 上添加注解@Component(value=“user”),它的意思是创建一个 User 类的 bean 实例,bean 的 id 为 user。
@Component("user")
public class User {
private String uid;
private String uname;
private String gender;
private int age;
private Company company;
//省略其他方法
}
【注意】@Component(“user”) 等同于@Component(value=“user”),即默认的属性是value,其效果等同于xml配置文件。
Spring还另外提供了3个功能与@Component等效的注解:
四个注解的功能是一样的,只是使用不同的注解可以看出层次结构
需要在类上使用注解@Scope,其value属性用于指定作用域,默认为singleton(单例bean)。
(1)在项目spring5的Student类中添加下面的注解。
@Scope("prototype")
@Component("student")
public class User {
//省略其他代码
}
@Scope(“prototype”)等同于@Scope(value=“prototype”),这样就设置bean的作用范围为"prototype"(原型bean)
需要在属性上使用注解@Value,该注解的value属性用于指定要注入的值。使用该注解完成属性注入时,类中无需setter。当然,若属性有setter,则也可将其加到setter上。
@Scope("prototype")
@Component("user")
public class User {
@Value("1")
private String uid;
@Value("李白")
private String uname;
@Value("男")
private String gender;
@Value("18")
private int age;
public void show(){
System.out.println("用户编号:"+uid+" 用户姓名:"+uname+"性别:"+gender+"年龄:"+age);
}
//省略其他方法
}
需要在域属性上使用注解@Autowired,该注解默认使用按类型自动装配Bean的方式。根据类型不能完成注入,则在根据名字完成注入, 以上两种方式都失败则抛出异常。
@Component("user")
public class User {
@Value("1")
private String uid;
@Value("李白")
private String uname;
@Value("男")
private String gender;
@Value("18")
private int age;
@Autowired
private Company company;
//省略其他方法
}
@Qualifier的value属性用于指定要匹配的Bean的id值。
@Component("user")
public class User {
//省略其他代码
@Autowired(required=false)
@Qualifier("mycolleg")
private Company company;
}
这时域属性company必须装配id为mycompany的bean。如果找不到会报错。
使用@Resource注解既可以按名称匹配Bean,也可以按类型匹配 Bean。
(1)按类型注入域属性
@Resource 注解若不带任何参数,则会按照类型进行Bean的匹配注入。
@Component("user")
public class User {
//省略其他代码
@Resource
private Company company;
}
(2)按名称注入域属性。@Resource 注解指定其name属性,则name的值即为按照名称进行匹配的Bean的id。
@Component("user")
public class User {
//省略其他代码
@Resource(name="mycompany")
private Company company;
}
注解的好处是: 配置方便,直观。
缺点: 以硬编码的方式写入到了Java代码中,其修改需要重新编译代码的。
xml配置方式的好处是: 对其所做修改,无需编译代码,只需重启服务器即可将新的配置加载。
若注解与xml同用,xml的优先级要高于注解。
码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识,点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。