Bean的作用域
Bean的作用域有两种,第一种是Singleton(单例模式),第二种是prototype(原型模式)
-
Singleton 单例模式(Spring默认机制)
设定被Spring容器装配的类块代码全局唯一,就算ClassPathXML对象连续取出该类,被取出的地址也是唯一的。
有时候并发的情况下访问单例作用域对象也会引发延迟或者数据不一致,推荐单线程使用单例模式。
<bean id="User" class="com.darling.pojo.user" scope="singleton"></bean>
-
prototype 原型模式(又称克隆模式)
设定被Spring容器装配的类代码块独立存在,也就是说被ClassPathXML对象连续取出该类,被取出的每一个地址都是独立的,也就是取出一次就创建一次该类的对象。
相对于单例而言更浪费资源,推荐多线程使用原型模式。
<bean id="User" class="com.darling.pojo.user" scope="prototype"></bean>
-
request 请求
在产生请求的时候相当于为bean创建了生存区间,只不过这个区间很小,这也就是导致了这种作用域的存活时间很短暂,一旦请求结束就会死亡。
-
Session 会话
相对于request请求Session的存活区间较大,该区间始于浏览器打开,终于浏览器结束,Session生于客户端请求,死于与浏览关闭。
-
request,Session,application 这些作用域属性只会哎Web开发中使用到。
作用域示例
Student实体类:
package com.darling.Pojo;
/**
* 该类为测试Spring容器中转配类的作用域问题所制定的测试类
*/
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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 "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Spring容器(IOC容器)Beans.xml配置代码(将实体类装配进Spring容器中):
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" class="com.darling.Pojo.Student" scope="prototype">
<property name="name" value="张杰"/>
<property name="age" value="18"/>
</bean>
</beans>
编写测试代码
/**
* 测试类
*/
public class SpringText {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Student student1 = context.getBean("student", Student.class);
Student student2 = context.getBean("student", Student.class);
//判断两个对象是否为同一个对象
System.out.println(student1==student2);
}
}
自己运行一下就可以知道,当Beans.xml配置文件中的Bean标签中Scope属性是singleton时,输出结果为true,代表这两个对象是同一个对象,反之当Scope值为prototype时,两个对象不同。
Bean的自动装配
自动装配是Spring完善Bean依赖的一种简便方式,也可以视为一种容错机制。
Spring会在上下文中自动查找,并给Bean属性自动转配值。
Spring容器中装配对象的三种方式:
- 手动装配(前面咱们做的都是手动的显示装配)
- 自动装配(隐式装配)
- 在Java中显示配置
自动装配实现
自动装配的实现方式有两种,第一种是在Bean标签中增加autowire属性,第二种是在被装配属性的实体类上使用注解的方式,我们先来了解第一种自动装配的方法。
-
Byname
Byname是autowire属性的取值范围之一,代表按照名称的方式进行装配,名称就是类绑定到Spring容器的时候Bean标签中的id属性值,通过这个找到被装配类中相应的Set方法。
-
ByType
ByType是autowire属性取值范围之一,代表按照类型的方式进行装配,如果使用该方式进行装配引用对象的话,那么绑定到Spring容器中被引用对象的id属性可以不写。
装配示例演示
环境搭建:
创建三个实体类,分别是主类Person,功能类Sleep,功能类Sport。
可以看出来,以上两个功能类分别是主类Person人具有的动作,所以可以很好理解以上三个类之间的关系。代码如下
package com.darling.Pojo;
/**
* 该类为测试Spring容器自动转配属性功能所搭建的环境
*/
public class Person {
private String name; //姓名
private int age; //年龄
private Sport sport;
private Sport sprot2;//运动
private Sleep sleep; //睡眠
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public Sport getSprot2() {
return sprot2;
}
public void setSprot2(Sport sprot2) {
this.sprot2 = sprot2;
}
public void setAge(int age) {
this.age = age;
}
public Sport getSport() {
return sport;
}
public void setSport(Sport sport) {
this.sport = sport;
}
public Sleep getSleep() {
return sleep;
}
public void setSleep(Sleep sleep) {
this.sleep = sleep;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sport=" + sport +
", sport2=" + sprot2 +
", sleep=" + sleep +
'}';
}
}
package com.darling.Pojo;
/**
* 该类为补充人行为的功能类
*/
public class Sport {
public void Task(){
System.out.println("今天进行了运动,身体更加强健了!");
}
}
package com.darling.Pojo;
/**
* 该类为补充人行为的功能类
*/
public class Sleep {
public void Task(){
System.out.println("进行了优质的睡眠,精神更加充沛了!");
}
}
创建Spring容器配置文件(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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 将人的拓展功能类装配到Spring容器中,用于给Person类装配属性使用-->
<bean id="sport" class="com.darling.Pojo.Sport"/>
<bean id="sleep" class="com.darling.Pojo.Sleep"/>
<!-- 要进行自动装配属性的测试对象-->
<bean id="person" class="com.darling.Pojo.Person" autowire="byName">
<!--基本数据类型无法使用自动装配-->
<property name="name" value="卡特琳娜"/>
<property name="age" value="18"/>
</bean>
</beans>
测试类:
public class Spring_text {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("Beans.xml");
Person person = Context.getBean("person", Person.class);
//输出Person对象所有属性的赋值状态
System.out.println(person.toString());
person.getSleep().Task();
person.getSport().Task();
}
}
细节剖析以及错误预判
首先呢Byname和ByType这两种方式都是依赖实体类中的Set方法进行属性值注入的,如果实体类中没有生成Set方法则会导致自动装配错误。重点注意Byname的时候,如果容器绑定类ID值与实体类中的Set方法名(不包括Set前三个字母)不一致,那么也是会错误。
在使用ByType方式进行自动装配的时候需要重点注意一个点,分别是装配前和装配时,这时候可能大家就有疑问了,现在解释一下什么叫**“装配前”和“装配时”**,全体目光向我看齐,这是非常重要的一个点。
自动装配顾名思义就是将一个值注入到另一个地址中,这就是装配的全部过程,那么问题来了,如果是ByType方式注入的话,在寻找注入值的时候出现了同等类型的不同对象,这时就会报错,Byname不用担心,因为ID是唯一的。继续往下走,当找到注入值,准备要将注入值注入到地址中的时候,它还是按照类型注入的,这时出现了同等类型的注入地址,它不会报错,不会报错,不会报错!!!
(重点理解一下,好好品一品!)
书接上一段,当使用ByType进行自动装配的时候,注入值有多个被注入地址的时候它不会报错,这时候它有一个容错机制(我自己这么叫它)。在细想一下注入值只有一个,被注入地址有多个,这时候就有两种情况了:
- 第一种,被注入地址都被注入同一个对象,也就是同一个值
- 第二种,被注入地址被注入不同对象,也就是不同值
区分上面两种结果的分割线就是注入值(也就是注入对象的作用域),Spring默认是singleton,也就是单例模式,全局唯一。如果设定了Scope=prototype,代表原型模式(克隆模式),每个对象都是独立的,即被注入的对象也是不同的。
使用注解进行自动装配
使用注解的前置依赖:
重点多了 context 相关的关键字。
<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>
注解介绍:
-
@Value(“”)
基本数据类型值注入的一种方式。
-
@Autowired
该注解是可以放在引用数据类型上面,或者引用数据类型的Set方法上面,只能按ByType进行自动装配。
-
@Qualifier(“log1”)
该注解可以放在引用数据类型上面,或者引用数据类型的方法上面,只能按ByName进行自动装配。
-
@Resource(name=“”)
这个注解与上面两个注解不同的是它并不是Spring框架提供的注解,这个是Java提供的注解,从使用上来说它的功能比前两个都强大,是先按照类型来找,类型找不到或者区分不出来的话在按照提供的ID名称去找。
-
@Component
放在类上,说明这个类被Spring托管了,相当于在Spring容器中使用标签注册Bean。
在使用注解前,我先明确一下示例环境,现在有Pojo包下有两个实体类,分别是User类,Log类
/**
* 为测试注解自动装配,编写的模拟用户类
*/
public class User {
//网站登录名称
private String name;
//网站日志对象
private Log log;
public User() {
}
public User(String name, Log log) {
this.name = name;
this.log = log;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Log getLog() {
return log;
}
public void setLog(Log log) {
this.log = log;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", log=" + log.GetUserLog() +
'}';
}
}
Log类中有一个公共方法,是记录登录的时间的日志,代表该用户什么时候登录该网站。
/**
* 该类为用户类中日志属性的对象
*/
public class Log {
public String GetUserLog(){
return "该用户与"+new Date().toString()+"登录该网站";
}
}
明确一下我的实验思想,我要使用注解的方式实现属性值的自动装配,在此之前我们首先要做的就是将我们实际操作的所有类绑定到Spring容器(IOC容器)中,将类的实例化交给Spring容器处理,基本数据类型的值和引用数据类型的值我们就不在Beans.xml中做了,重点看自动装配注解的使用。
-
将类绑定到Spring容器中
<?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 /> <bean id="user" class="com.darling.Pojo.User"/> <bean id="log1" class="com.darling.Pojo.Log" /> <bean id="log2" class="com.darling.Pojo.Log" /> </beans>
可以看到我们将上面的两个实体类已经交给Spring管理了,与此同时我们还对Log类多配置了一个对象。
-
使用注解
public class User { @Value("喜多川海梦") private String name; //网站登录名称 @Autowire private Log log; //网站日志对象 ... }
如果仅仅向上面这样配置的时候最终的结果是会报错的,因为上面也提到了**@Autowire是按照ByType来进行自动装配的,当前Beans.xml中存在两个同等类型的注册类**,这时候它就会分不清注入值是哪一个了,这时候需要结合**@Qualifier(“log1”)**来使用。
public class User { @Value("喜多川海梦") private String name; //网站登录名称 @Autowire @Qualifier("log1") private Log log; //网站日志对象 ... }
以上是使用Spring提供的注解进行实现的属性自动装配,也可以使用Java的注解来实现。
public class User { @Value("喜多川海梦") private String name; //网站登录名称 @Resource("log1") private Log log; //网站日志对象 ... }
测试类编写
public class Spring_Text {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("Beans.xml");
User user = Context.getBean("user", User.class);
System.out.println(user.toString());
}
}