文章目录
Spring
参考视频:B站狂神,写这个只是方便个人复习,怎么写是我自己的事,我能看懂就行,没要求非要让你看!白嫖还挑刺,是很没有风度的事情。希望做个有风度的“五好青年”!
6、依赖注入
6.1 构造器注入
- 之前的案例已经说过了。
6.2 Set 注入 (重点)
依赖注入(Set注入!)。
- 依赖 : Bean对象的创建依赖于容器!
- 注入 : Bean对象中的所有属性,由容器来设置和装配。
【环境搭建】
- 复杂类型
- Address.java
package com.github.subei.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
- 真实测试对象
- Student.java
package com.github.subei.pojo;
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobby;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobby=" + hobby +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
- beans.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="student" class="com.github.subei.pojo.Student">
<!-- 第一种:普通值注入,value -->
<property name="name" value="subei"/>
</bean>
</beans>
- 测试类
import com.github.subei.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getName());
}
}
2、Bean注入
- 注意点:这里的值是一个引用,ref
<bean id="address" class="com.github.subei.pojo.Address">
<property name="address" value="成都"/>
</bean>
<bean id="student" class="com.github.subei.pojo.Student">
<!-- 第一种:普通值注入,value -->
<property name="name" value="subei"/>
<!-- 第二种:Bean注入,ref -->
<property name="address" ref="address"/>
</bean>
3、数组注入
<bean id="student" class="com.github.subei.pojo.Student">
<!-- 第三种:数组注入 -->
<property name="books">
<array>
<value>Mybatis</value>
<value>Spring</value>
<value>SpringMVC</value>
</array>
</property>
</bean>
4、List注入
<bean id="student" class="com.github.subei.pojo.Student">
<!-- 第四种:list注入 -->
<property name="hobby">
<list>
<value>家里蹲</value>
<value>精神萎靡</value>
<value>无法沟通</value>
</list>
</property>
</bean>
5、Map注入
<bean id="student" class="com.github.subei.pojo.Student">
<!-- 第五种:Map注入 -->
<property name="card">
<map>
<entry key="学生证" value="20201014"/>
<entry key="身份证" value="14253686"/>
</map>
</property>
</bean>
6、set注入
<bean id="student" class="com.github.subei.pojo.Student">
<!-- 第六种:set注入 -->
<property name="games">
<set>
<value>保卫萝卜1</value>
<value>保卫萝卜2</value>
<value>保卫萝卜3</value>
</set>
</property>
</bean>
7、Null注入
<bean id="student" class="com.github.subei.pojo.Student">
<!-- 第七种:null注入 -->
<property name="wife">
<null/>
</property>
</bean>
8、Properties注入
<bean id="student" class="com.github.subei.pojo.Student">
<!-- 第八种:Properties注入 -->
<property name="info">
<props>
<prop key="学号">20210106</prop>
<prop key="性别">保密</prop>
<prop key="姓名">subei</prop>
</props>
</property>
</bean>
- 测试
import com.github.subei.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
}
}
6.3 拓展方式注入
- 我们可以使用p命令空间和C命令空间进行注入
- 官方解释:
p命名和c命名注入翻译:
- p-namespace 允许使用
bean
元素的属性(而不是嵌套的<property/>
元素)来描述协作 Bean 的属性值,或同时使用这两者。- c-namespace 允许使用内联属性来配置构造函数参数,而不是嵌套的
constructor-arg
元素。
- User.java
package com.github.subei.pojo;
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- P命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<!-- p命名空间注入,可以直接注入属性的值: property -->
<bean id="user" class="com.github.subei.pojo.User" p:name="subei" p:age="21" />
- c 命名空间注入 : 需要在头文件中加入约束文件
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<!-- c命名空间注入,可以通过构造器注入: construct-args -->
<bean id="user2" class="com.github.subei.pojo.User" c:name="subei" c:age="22" />
- 发现问题:爆红了,没有写有参构造!
- 解决:把有参、无参构造器加上。
- 由此可知:c 就是所谓的构造器注入!
- 测试代码
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
User user = context.getBean("user2",User.class);
System.out.println(user);
}
6.4 Bean的作用域
在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。
简单地讲,bean就是由IoC容器初始化、装配及管理的对象。
Scope | Description |
---|---|
singleton | (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。 |
prototype | 将单个 bean 定义的作用域限定为任意数量的对象实例。 |
request | 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext 中有效。 |
session | 将单个 bean 定义的范围限定为 HTTP Session 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
application | 将单个 bean 定义的范围限定为ServletContext 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
websocket | 将单个 bean 定义的范围限定为WebSocket 的生命周期。仅在可感知网络的 Spring ApplicationContext 上下文中有效。 |
6.4.1 Singleton(单例)
单例模式(Spring默认机制)
- 下图显示了单例作用域如何工作:
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。
注意,Singleton作用域是Spring中的缺省作用域。
要在XML中将bean定义成singleton,可以这样配置:
<bean id="user" class="com.github.subei.pojo.User"/>
<!-- 以下内容是等价的,尽管是多余的(默认为单例作用域) -->
<bean id="user2" class="com.github.subei.pojo.User" scope="singleton"/>
- 测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("UserBeans.xml");
User user = context.getBean("user2",User.class);
User user2 = context.getBean("user2",User.class);
System.out.println(user);
System.out.println(user==user2);
}
6.4.2 Prototype(原型)
原型模式:每次从容器中get的时候,都会产生一个新对象!
每次对特定 bean 提出请求时,bean 部署的非单一原型范围都会导致创建一个新 bean 实例。也就是说,将 Bean 注入到另一个 Bean 中,或者您可以通过容器上的
getBean()
方法调用来请求它。通常,应将原型作用域用于所有有状态 Bean,将单例作用域用于 StatelessBean。下图说明了 Spring 原型范围:
在XML中将bean定义成prototype,可以这样配置:
<bean id="user2" class="com.github.subei.pojo.User" scope="prototype"/>
- 其余的request、session、application、这些个只能在web开发中使用到!
6.4.3 Request
Spring 容器通过为每个 HTTP 请求使用
loginAction
bean 定义来创建LoginAction
bean 的新实例。也就是说,loginAction
bean 的作用域是 HTTP 请求级别。您可以根据需要更改创建实例的内部状态,因为从同一loginAction
bean 定义创建的其他实例看不到这些状态更改。它们特定于单个请求。当请求完成处理时,将限制作用于该请求的 Bean。
- 考虑以下 XML 配置来定义 bean :
<bean id="loginAction" class="com.something.LoginAction" scope="request"/>
使用注解驱动的组件或 Java 配置时,可以使用
@RequestScope
注解 将组件分配给request
范围。以下示例显示了如何执行此操作:
@RequestScope
@Component
public class LoginAction {
// ...
}
6.4.4 Session
当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
- 考虑下面bean定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
在单个 HTTP
Session
的生存期内,Spring 容器通过使用userPreferences
bean 定义来创建UserPreferences
bean 的新实例。换句话说,userPreferences
bean 的作用域实际上是 HTTPSession
级别。与请求范围的 Bean 一样,您可以根据需要任意更改所创建实例的内部状态,因为知道其他 HTTPSession
实例(也使用从相同userPreferences
Bean 定义创建的实例)不会看到这些状态更改,因为它们特定于单个 HTTPSession
。当最终丢弃 HTTPSession
时,也将丢弃作用于该特定 HTTPSession
的 bean。
- 使用注解驱动的组件或 Java 配置时,可以使用
@SessionScope
注解 将组件分配给session
范围。
@SessionScope
@Component
public class UserPreferences {
// ...
}
7、自动装配Bean
- 自动装配是Spring满足bean依赖一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种自动装配的方式:
- 在xml中显示的配置;
- 在java中显示配置;
- 隐式的自动装配bean。【重要!】
7.1 测试
- 环境搭建:一个人有两个宠物!
- Cat.java
package com.github.subei.pojo;
public class Cat {
public void shout(){
System.out.println("喵!喵!喵!");
}
}
- Dog.java
package com.github.subei.pojo;
public class Dog {
public void shout(){
System.out.println("汪!汪!汪!");
}
}
- People.java
package com.github.subei.pojo;
public class People {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
- beans.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="cat" class="com.github.subei.pojo.Cat"/>
<bean id="dog" class="com.github.subei.pojo.Dog"/>
<bean id="people" class="com.github.subei.pojo.People">
<property name="name" value="哇哈哈"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
- 测试类
import com.github.subei.pojo.People;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
people.getCat().shout();
people.getDog().shout();
}
}
7.2 ByName自动装配
<!--
byName:会自动在容器上下文中查找,和自己对象set方法后而的值对应的beanid!
-->
<bean id="people" class="com.github.subei.pojo.People" autowire="byName">
<property name="name" value="哇哈哈"/>
</bean>
、
7.3 ByType自动装配
<bean class="com.github.subei.pojo.Cat"/>
<bean class="com.github.subei.pojo.Dog"/>
<!--
byName:会自动在容器上下文中查找,和自己对象set方法后而的值对应的beanid!
byType:会自动在容器上下文中查找,和自己对象属性类型机同的bean!
-->
<bean id="people" class="com.github.subei.pojo.People" autowire="byType">
<property name="name" value="哇哈哈"/>
</bean>
小结:
- byname的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
- bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!
7.4 使用注解实现自动装配
- JDK 1.5支持的注解,Spring2.5就支持注解了!
- The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML.
- 要使用注解须知:
- 导入约束,context约束
- <mark>配置注解的支持;context:annotation-config/</mark>
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
@Autowired
- 修改beans.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" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/aop https://www.springframework.org/schema/context/spring-aop.xsd">
<!-- 开启注解的支持 -->
<context:annotation-config/>
<bean id="cat" class="com.github.subei.pojo.Cat"/>
<bean id="dog" class="com.github.subei.pojo.Dog"/>
<bean id="people" class="com.github.subei.pojo.People"/>
</beans>
- 修改People.java文件
package com.github.subei.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class People {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
- 运行
- 直接在属性上使用!也可以在set方式上使用!
- 使用Autowired我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname!
- 科普一下:
@Nullable 字段标记了这个注解,说明这个字段可以为null
- 测试代码
package com.github.subei.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class People {
// 如果显示定义了Autowtred的requtred属性为false,说明这个对象可以为null.否则不允许为空。
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
- 如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
package com.github.subei.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class People {
@Autowired
@Qualifier(value = "cat111")
private Cat cat;
@Autowired
@Qualifier(value = "dog222")
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
@Resource
package com.github.subei.pojo;
import javax.annotation.Resource;
public class People {
@Resource(name="cat2")
private Cat cat;
@Resource
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
小结,@Resource 和@Autowired的区别:
- 都是用来自动装配的,都可以放在属性字段上!
- @Autowired 通过byType的方式实现,而且必须要求这个对象存在!
- @Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的倩况下,就报错!<mark>【常用】</mark>
- 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byname的方式实现。