文章目录
前言
经过上一节,对spring的介绍,相信读者,对spring,有了大致的了解。大量篇幅的文字,看着就有点让人不耐烦,下面,我们就着手动起来。关于此系列的学习,依赖都是通过maven 去管理的。
Eclipse 安装spring插件
工欲善其事必先利其器,在eclipse 安装spring 插件将有利于我们的学习。
安装的时候,一定要注意eclipse 的版本,另外,spring 官网,一般都是最新的插件,在哪找历史的。
添加链接描述,可以参考这篇博客,安装的步骤,就不说,网上一大堆。
Helloworld Spring
首先先添加Spring 的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
定义一个POJO Java 对象类
public class HelloWord {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
定义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="helloworld" class="test.HelloWord">
<property name="message" value="hello spring"></property>//这里的name 是你的属性名,必须和POJO 对象的属性一致,value 是传入该属性的值
</bean>
</beans>
对于Bean 的Id ,我们一定要保证它的唯一性。
创建的Beans.xml 的目录最好在maven工程下的src/main/java,这就是相当于传统Java 项目的src(src是目录的顶层),所以上面class,是从test开始的。还有注意的,Beans.xml 这个命名不是随意的
在main 函数里面测试:
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWord h = (HelloWord) context.getBean("helloworld");//这里的就是Beans.xml的配置的bean 的id,去获取该POJO 对象的。
System.out.println(h.getMessage());
ClassPathXmlApplicationContext 这个是负责加载配置文件,以及实例化所有的对象。
Spring 容器
容器负责创建对象,配置对象,管理对象的生命周期,从创建到消亡。这些对象,在Spring 中,被称之为Bean。配置Bean 的元数据库,可以是在Xml、Java 注解或者Java 代码中。(比如上面的例子,就是在Xml)。
下面是容器工作图:
Spring 容器类型
分两种:
- Spring BeanFactory Container
最简单的容器
- Spring ApplicationContext Container
这个容器,比上面的更高级,对于企业级应用,推荐使用这个。它的功能包含上面容器所有的。
Bean 定义
对象在Spring 容器中管理,从称之为Bean。Bean 一个对象,但是该对象是被实例化了,组装属性的。这些Bean 被创通过你在配置的元数据,去创建。
Bean 的定义,被称之为配置元数据,你配置这些数据,去让容器知道:
- 怎么去创建Bean
- Bean 的生命周期
- Bean 的依赖
Bean 的元数据配置是通过下面一系列的方法:
property | Description |
---|---|
class | 这个属性是必须的,指定Bean 的class |
name | 这个属性是区分Bean 的,也是通过该属性获取Bean 对象,所以必须保证唯一性 |
scope | 指明Bean 的作用域 |
constructor-arg | 被用来指定依赖,后面有细讲 |
properties | 被用来指定依赖,后面有细讲 |
autowiring mode | 被用来指定依赖,后面有细讲 |
lazy-initialization mode | 懒加载,告诉容器创建Bean 实例,不是在开始,而是在第一次请求 |
initialization method | 初始化方法,所有的属性都由容器创建好了,回调的方法 |
destruction method | 销毁方法,所有的属性都由容器创建好了,回调的方法 |
Bean 元数据配置方式
- 基于Xml
- 基于注解
- 基于Java 代码
Bean 作用域
Scope | Description |
---|---|
singleton(单例) | 该Bean作用域是整个容器, 只有一个Bean 实例 |
prototype(原型) | 该Bean作用域是,一个Bean可以有多个实例 |
request | 这将bean定义范围限定为HTTP请求。 在Web应用中,为每个请求创建一个bean实例。 |
session | 这将bean定义范围限定为session。 在Web应用中,为每个会话创建一个bean实例 |
Singleton
将上述Hello world 代码,main 中的代码,做如下的修改:
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWord h = (HelloWord) context.getBean("helloworld");
h.setMessage("i am new Spring");
System.out.println(h.getMessage());
HelloWord h1 = (HelloWord) context.getBean("helloworld");
System.out.println(h1.getMessage());
这里句不需要修改Beans.xml,因为Bean 默认就是这种作用域。
执行之后,你会发现两次打印的一致。实际上,采用这种模式的作用域,容器将把第一次实例的对象,放到缓存中,之后所有的请求,都是在缓存中拿,而不是重新去创建新的实例。
prototype
这种作用域,容器会为每一个Bean 创建新的实例。
基于上面的例子,我们修改Beans.xml:
<bean id="helloworld" class="test.HelloWord" scope="prototype">
同样运行,这次,就一样的结果。说明每一个获取的Bean 实例,是不同的。
Bean 生命周期
当一个Bean 需要使用的时候,它通过容器去创建,当不需要的,从容器上移除。
public class ExampleBean {
public void init() {
System.out.println("bean start init");
}
public void destorty() {
System.out.println("bean is destory");
}
}
<bean id="examplebean" class="test.ExampleBean" init-method="init" destroy-method="destorty"></bean>//这里填写对应的init 的destory 方法名,这个你保持和Bean 的方法一致即可。
还可以实现InitializingBean or DisposableBean 接口,实现Bean 的初始化和销毁,但是不建议,它会直接要你重写方法民,灵活性不行。
当如果多个Bean 里面都有这么一样的初始化、销毁的方法,可以可以直接在Beans 标签中,通过配置default-init-method and default-destroy-method 来减少重复性的设置。
如果上述你是web 应用,那么关闭服务器,就可以发现执行了destory 方法,init 方法不用说,创建的时候直接执行。也可以通过AbstractApplicationContext 的registerShutdownHook 去模拟容器的关闭。
AbstractApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
ExampleBean eb = (ExampleBean) context.getBean("examplebean");
context.registerShutdownHook();
BeanPostProcessor
如果我们需要在Spring容器完成Bean的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现,然后注册到容器中。
实例化Bean 的流程:
public class InitHelloWorld implements BeanPostProcessor {
//Bean 初始化前
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("before init bean "+beanName);
return bean;//这个地方在创建的时候,一定要注意就是返回bean 对象,否则会引起异常。
}
//Bean 初始化后
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("after init bean "+beanName);
return bean;
}
}
<bean class="test.InitHelloWorld"></bean>//这个Bean 是没有id的,你不需要管他
ApplicationContext 会自动检测到所有实现该接口的Bean,并自动注册到容器中,所以,你还是要在Beans.xml中定义。
随便获取一个之前配置的Bean,会发现执行上面两个方法,在bean 初始化前后。
Bean 的继承
一个子类的Bean可以继承父类的Bean,可以重新相关的属性,以及添加自己需要的。
原先的HelloWorld 类不动,在编写一个子类:
public class SubHelloWorld {
private String message;
private String sonmessage;
//相应的set、get方法省略,自行补充
}
这里有一点需要注意:尽管是继承,这里必须要重新定义父类的属性,否则尽管下面设置继承关系,一样没办法设置父类的属性,并且这个父类属性必须要和父类设置一样。
注意这里虽然是继承关系,但是不需要像Java 一样通过extends ,继承的关系,在下面Bean配置的标签parent 来体现:
<bean id="helloworld" class="test.HelloWord" scope="prototype">
<property name="message" value="hello spring" ></property>
</bean>
<bean id="subhelloworld" class="test.SubHelloWorld" parent="helloworld">//parent 后面是跟的是父类Bean 的id
<property name="sonmessage" value="i am son hello world"></property>//在这我们也可以设置父类的message 属性,修改为子类自己特有的,也可以不设置,就是父类的。
</bean>
注意:一定你指定继承关系,如果直接获取子类的Bean,是可以获取父类的属性,无需多此一举,在get父类的Bean。
Bean 模板
在Java se 中,我们都知道可以定义一个抽象类加上继承,去实现一个公用的模板类,在Spring 也一样。
在上述代码,修改helloworld,将其定义成抽象的。
<bean id="helloworld" class="test.HelloWord" abstract="true"></bean>
这样父类可以定义一些属性,子类可以直接用,或者重新赋值,子类还是要定义父类的属性(一模一样)。将父类定义成抽象的好处,父类就是一个纯洁的Bean 定义模板。