Spring Ioc容器
什么是Ioc?
控制反转((Inversion of Control,英文缩写为IoC)把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入(Dependency Injection,简称DI和依赖查找(Dependency Lookup)。
说得简单一点就是我们在写Java代码的时候我们的对象不需要自己来创建,而是通过容器来帮助我们生成。用我们生活中的例子来说,打个比方,我们想要一辆汽车,我们不可能去自己造一辆汽车,而是去买一辆。
所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
Ioc容器的初始化
image.pngIoC容器初始化过程主要经过以下几个阶段:
1.解析阶段:Spring会解析Bean的XML配置文件,将XML元素进行抽象,抽象成Resource对象。
2.转换阶段:通过Resource对象将配置文件进行抽象后转换成Spring能够理解的BeanDefinition结构。
3.注册阶段:Spring IoC容器的实现,从根源上是beanfactory,但真正可以作为一个可以独立使用的ioc容器还是DefaultListableBeanFactory。
DefaultListableBeanFactory间接实现了BeanFactory接口,是整个bean加载的核心部分,是Spring注册及加载bean的默认实现,我们可以理解为Spring bean工厂的发动机。
Spring Bean 定义
被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的。bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象。这些 bean 是由用容器提供的配置元数据创建的。
属性 | 描述 |
---|---|
class | 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。 |
name | 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。 |
scope | 这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。 |
lazy-initialization mode | 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。 |
initialization | 在 bean 的所有必需的属性被容器设置之后,调用回调方法 |
destruction | 当包含该 bean 的容器被销毁时,使用回调方法 |
Spring Bean作用域
当在 Spring 中定义一个 bean 时,你必须声明该 bean 的作用域的选项。例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为prototype。同理,如果你想让 Spring 在每次需要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为singleton。
Spring框架支持以下五个作用域:
作用域 | 描述 |
---|---|
singleton | 该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中的一个单一实例(默认)。 |
prototype | 该作用域将单一 bean 的定义限制在任意数量的对象实例。 |
request | 该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
session | 该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。 |
global-session | 该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
看下面的例子:第一个是singleton,第二个是prototype。
Hello.java
package com.tutorialspoint;
public class Hello {
private String message;
public void setMessage(String message) {
this.message = message;
}
public void getMessage() {
System.out.println("Your message is:" + message);
}
}
MainApp.java
package com.tutorialspoint;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Hello object1 = (Hello) context.getBean("bean");
object1.setMessage("这不大傻瓜吗");
object1.getMessage();
Hello object2 = (Hello) context.getBean("bean");
object2.getMessage();
}
}
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-3.0.xsd">
<bean id="bean" class="com.tutorialspoint.Hello" scope="singleton">
</bean>
</beans>
输出的结果为如图:
image.png将Beans.xml中的scope="singleton"改为scope="prototype"后,其余代码不变,输出结果可以猜到了,第二个Bean将不会得到实例化,因此输出的值为空即null。
结果正确:
image.pngSpring Bean的生命周期
Spring的生命周期跟Servlet的生命周期像,都有初始化跟销毁的方法,参数分别为:init-mehtod和destroy-method。init-method属性指定一个方法,在实例化 bean 时,立即调用该方法。同样,destroy-method指定一个方法,只有从容器中移除 bean 之后,才能调用该方法。
Hello.java
package com.tutorialspoint;
public class Hello {
private String message;
public void setMessage(String message) {
this.message = message;
}
public void getMessage() {
System.out.println("Your message is:" + message);
}
public void init() {
System.out.println("bean正在被初始化");
}
public void destroy() {
System.out.println("bean正在被销毁");
}
}
MainApp.java
package com.tutorialspoint;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Hello object1 = (Hello) context.getBean("bean");
object1.setMessage("这不大傻瓜吗");
object1.getMessage();
//在非web应用中关闭Ioc容器
context.registerShutdownHook();
}
}
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-3.0.xsd">
<bean id="bean" class="com.tutorialspoint.Hello" init-method="init" destroy-method="destroy">
</bean>
</beans>
image.png
这是关于registryShutdownHook()的详细解释。
Spring Bean定义继承
bean 定义可以包含很多的配置信息,包括构造函数的参数,属性值,容器的具体信息例如初始化方法,静态工厂方法名,等等。子 bean 的定义继承父定义的配置数据。子定义可以根据需要重写一些值,或者添加其他值。Spring Bean 定义的继承与 Java 类的继承无关,但是继承的概念是一样的。你可以定义一个父 bean 的定义作为模板和其他子 bean 就可以从父 bean 中继承所需的配置。当你使用基于 XML 的配置元数据时,通过使用父属性,指定父 bean 作为该属性的值来表明子 bean 的定义。
看例子:
Hello.java
package com.tutorialspoint;
public class Hello {
private String message1;
private String message2;
public void setMessage1(String message) {
this.message1 = message;
}
public void getMessage1() {
System.out.println("Your message is:" + message1);
}
public void setMessage2(String message) {
this.message2 = message;
}
public void getMessage2() {
System.out.println("Your message is:" + message2);
}
}
HelloWorld.java
package com.tutorialspoint;
public class HelloWorld {
private String message1;
private String message2;
private String message3;
public void setMessage1(String message){
this.message1 = message;
}
public void setMessage2(String message){
this.message2 = message;
}
public void setMessage3(String message){
this.message3 = message;
}
public void getMessage1(){
System.out.println("Your message is: " + message1);
}
public void getMessage2(){
System.out.println("Your message is:" + message2);
}
public void getMessage3(){
System.out.println("Your message is: " + message3);
}
}
MainApp.java
package com.tutorialspoint;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Hello obj1 = (Hello) context.getBean("hello");
obj1.getMessage1();
obj1.getMessage2();
HelloWorld obj2 = (HelloWorld) context.getBean("helloworld");
obj2.getMessage1();
obj2.getMessage2();
obj2.getMessage3();
}
}
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-3.0.xsd">
<bean id="hello" class="com.tutorialspoint.Hello">
<property name="message1" value="message1" />
<property name="message2" value="message2" />
</bean>
<bean id="helloworld" class="com.tutorialspoint.HelloWorld" parent="hello">
<property name="message1" value="helloworld1" />
<property name="message3" value="helloworld2"/>
</bean>
</beans>
输出结果如下:
image.png可以看出,我们在HelloWorld.java中并未定义有getMessage2()的方法,但是在主类中我们调用了该方法,因此Bean就继承了主类的该方法,所以子类调用的是父类的方法,输出跟父类的结果一致。