核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression等模块组成,细节如下:
数据访问/集成层包括JDBC,ORM,JMS和事务处理模块,它们的细节如下:
Web层由Web、Web-MVC、Web-Portlet组成,它们的细节如下:
Test模块:spring支持Junit和TestNG测试框架,而且还额外提供了一些基于Spring的测试功能,比如在测试Web框架时,模拟Http请求的功能。
Spring容器是Spring框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象为SpringBeans。
Spring提供了以下两种类型的容器:
Spring BeanFactory
它是最简单的容器,给DI提供了基本的支持,它用org.springframework.beans.factory.BeanFactory接口来定义。BeanFactory或者相关的接口,如BeanFactoryAware,InitializingBean,DisposableBean,在Spring中仍然存在具有大量的与spring整合的第三方框架的反向兼容性的目的。
Spring ApplicationContext容器
该容器添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由org.springframework.context.ApplicationContext接口定义。
ApplicationContext容器包括BeanFactory容器的所有功能,所以不建议使用BeanFactory。BeanFactory仍然可以用于轻量级的应用程序,如移动设备或基于applet的应用程序,其中它的数据量和速度是显著。
这是一个最简单的容器,它主要的功能是为依赖注入(DI)提供支持。
在Spring中,有大量对BeanFactory接口的实现。其中,最常被使用的是xmlBeanFactory类。这个容器从一个XML文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
步骤 | 描述 |
---|---|
1 | 使用idea新建一个spring的项目 |
2 | 在src下新建2个包beanfactory和xml |
3 | 在beanfactory里面写Student类和test类 |
4 | 在xml里面写Beans.xml |
public class Student {
private String name;
private String code;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
public class test {
public static void main(String[] args) {
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("xml/Beans.xml"));
Student student = (Student) factory.getBean("1");
System.out.println(student.getName()+"+"+student.getCode());
}
}
Application Context是BeanFactory的子接口,也被称为Spring上下文。
最常被使用的ApplicationContext接口实现:
public class testApplicationContext {
public static void main(String[] args) {
ApplicationContext context=new FileSystemXmlApplicationContext("D:/soft_File/IdeaFile/hand_rtt/springLearning/src/xml/Beans.xml");
Student student=(Student)context.getBean("1");
System.out.println(student.getName()+"+"+student.getCode());
}
}
Bean定义
被称作bean的对象是构成应用程序的支柱也是由SpringIoC容器管理的。bean是一个被实例化,组装,并通过SpringIoC容器所管理的对象。这些bean是由容器提供的配置元数据创建的,例如,在XML的表单中的定义。
bean定义包含称为配置元数据的信息
上述所有的配置元数据转换成一组构成每个bean定义的下列属性。
属性 | 描述 |
---|---|
class | 这个属性是强制性的,并且指定用来创建bean的bean类 |
name | 这个属性指定唯一的bean标识符。在基于XML的配置元数据中,你可以使用ID和name属性来指定bean标识符 |
scope | 这个属性由特定的bean定义创建的对象的作用域。 |
constructor-arg | 它是用来注入依赖关系的 |
properties | 它是用来注入依赖关系的 |
autowiring mode | 它是用来注入依赖关系的 |
lazy-initialization mode | 延迟初始化的bean告诉IoC容器在它第一次请求时,而不是在启动时去创建一个bean实例 |
initialization | 在bean的所有必需的属性被容器设置之后,调用回调方法。 |
destruction | 当包含该bean的容器被销毁时,使用回调方法。 |
Bean与Spring容器的关系:
Spring配置元数据
Spring IoC容器完全由实际编写的配置元数据的格式解耦。有下面三个重要的方法把配置元数据提供给Spring容器:
提示:对于基于XML的配置,Spring2.0以后使用Schema的格式,使得不同类型的配置拥有了自己的命名空间,使配置文件更具有扩展性。
在上述示例中:
当在Spring中定义一个bean时,你必须声明该bean的作用域的选项。例如,为了强制Spring在每次需要时都产生一个新的bean实例,你应该声明bean的作用域的属性为prototype。同理,如果你想让Spring在每次在每次需要时都返回同一个bean实例,你应该声明bean的作用域为singleton
Spring框架支持五个作用域:singleton,prototype,request,session和global-session
注意:如果你使用web-aware ApplicationContext时,其中三个是可用的。
作用域 | 描述 |
---|---|
singleton | 在spring Ioc容器仅存在一个bean实例,Bean以单例方式存在 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean时,都相当于new XXBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
global-session | 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境 |
为了定义安装和拆卸一个bean,我们只要声明带有init-method和destroy-method参数的。init-method属性指定一个方法,在实例化bean时,立即调用该方法。同样,destory-method指定一个方法,只有从容器中移除bean之后,才能调用该方法。
Bean的声明周期可以表达为:Bean的定义->Bean的初始化->Bean的使用->Bean的销毁
//添加init,destroy方法
public class Student {
private String name;
private String code;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public void init(){
System.out.println("bean被初始化了");
}
public void destroy(){
System.out.println("bean被销毁了");
}
}
public class testApplicationContext {
public static void main(String[] args) {
AbstractApplicationContext context=new ClassPathXmlApplicationContext("xml/Beans.xml");
Student student=(Student)context.getBean("1");
System.out.println(student.getName()+"+"+student.getCode());
//确保正常关闭
context.registerShutdownHook();
}
}
默认的初始化和销毁方法
如果你有太多具有相同名称的初始化或者销毁方法的 Bean,那么你不需要在每一个 bean 上声明初始化方法和销毁方法。框架使用 元素中的 default-init-method 和 default-destroy-method 属性提供了灵活地配置这种情况,如下所示:
Bean后置处理器允许在调用初始化方法前后对Bean进行额外的处理。
BeanPostProcessor接口定义回调方法,你可以实现该方法来提供自己的实例化逻辑,依赖解析逻辑等。你也可以在Spring容器通过插入一个或多个BeanPostProcessor的实现来完成实例化,配置和初始化一个bean之后实现一些自定义逻辑回调方法。
你可以配置多个BeanPostProcessor接口,通过设置BeanPostProcessor实现的Ordered接口提供的order属性来控制这些BeanPostProcessor接口的执行顺序。
注意:ApplicationContext会自动检测由BeanPostProcessor接口的实现定义的bean,注册这些bean为后置处理器,然后通过容器中创建bean,在适当的时候调用它。
在你自己自定义的BeanPostProcessor接口实现类中,要实现以下两个抽象方法BeanPostProcessor.postProcessBeforeInitialization(Object,String)和BeanPostProcessor.postProcessAfterInitialization(Object,String)
public class StudentOne {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void init(){
System.out.println("studentOne初始化了");
}
public void destroy(){
System.out.println("studentOne销毁了");
}
}
public class InitStudent implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+"初始化之前");
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+"初始化之后");
return null;
}
}
public class testApplicationContext {
public static void main(String[] args) {
AbstractApplicationContext context=new ClassPathXmlApplicationContext("xml/Beans.xml");
Student student=(Student)context.getBean("1");
System.out.println("----------------");
System.out.println(student.getName()+"+"+student.getCode());
StudentOne studentOne=(StudentOne)context.getBean("2");
System.out.println(studentOne.getName()+"测试看看");
//确保正常关闭
context.registerShutdownHook();
}
}
bean定义可以包含很多的配置信息,包括构造函数的参数、属性值、容器的具体信息例如初始化方法,静态工厂方法名,等等。
子bean的定义继承父定义的配置数据。自定义可以根据需要重写一些值,或者添加其他值。
Spring Bean定义的继承与Java类的继承无关,但是继承的概念是一样的。你可以一定一个父bean的定义作为模板和其他子bean就可以从父bean中继承所需要的配置。
Bean定义模板
你可以创建一个Bean定义模板,不需要花太多功夫它就可以被其他子bean定义使用,在定义一个Bean定义模板,你不应该指定类的属性,而应该指定带true值得抽象属性:
父bean自身不能被实例化,因为它是不完整的,而且它也被明确标记为抽象的。当一个定义是抽象的,它仅仅作为一个纯粹的模板bean定义来使用的,充当自定义的父定义来使用。
Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。
依赖注入有助于把一些类粘合在一起,同时保持他们独立。
//假设学生依赖于书本
public class Student{
private Book book;
public Student(){book=new Book();}
}
在控制反转IoC的场景中,我们会这样做:
public class Student{
private Book book;
public Student(Book book){book=new Book();}
}
在这里Student不用担心Book的实现,Book将会独立实现,并且在Student实例化的时候提供给Student,整个过程由Spring框架控制。
在这里,我们已经从Student删除了全面控制,并且把它保存到其他地方(即 XML 配置文件),且依赖关系(即 Book类)通过类构造函数被注入到 Student 类中。因此,控制流通过依赖注入(DI)已经“反转”,因为你已经有效地委托依赖关系到一些外部系统。
依赖注入的第二种方法是通过Student类的Setter方法,我们将创建Book实例,该实例将被用于调用setter方法来初始化Student属性。
DI主要有两种变体:
序号 | 依赖注入类型&描述 |
---|---|
1 | Constructor-based dependency injection 当容器调用带有多个参数的构造函数类时,实现基于构造函数的 DI,每个代表在其他类中的一个依赖关系。 |
2 | Setter-based dependency injection 基于 setter 方法的 DI 是通过在调用无参数的构造函数或无参数的静态工厂方法实例化 bean 之后容器调用 beans 的 setter 方法来实现的。 |
如果存在不止一个参数时,当把参数传递给构造函数时,可能会存在歧义。要解决这个问题,那么构造函数的参数在 bean 定义中的顺序就是把这些参数提供给适当的构造函数的顺序就可以了。
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
package x.y;
public class Foo {
public Foo(int year, String name) {
// ...
}
}
//使用type属性显式的指定了构造函数参数的类型,容器也可以使用与简单类型匹配的类型
//使用index属性显示的指定构造函数参数的索引
最后,如果你想要向一个对象传递一个引用,你需要使用标签的ref属性,如果你想要直接传递值,那么你应该使用如上所示的value属性。
//getter和setter函数
class student{
private BigString name;
public BigString getName(){
return name;
}
public void setName(BigString name){
this.name=name;
}
}
基于构造函数注入和基于设值函数注入中的 Beans.xml 文件的区别就是在基于构造函数注入中,我们使用的是〈bean〉标签中的〈constructor-arg〉元素,而在基于设值函数的注入中,我们使用的是〈bean〉标签中的〈property〉元素。
第二个你需要注意的点是,如果你要把一个引用传递给一个对象,那么你需要使用 标签的 ref 属性,而如果你要直接传递一个值,那么你应该使用 value 属性。
使用p-namespace实现XML配置
-ref部分表明这不是一个直接的值,而是对另一个bean的引用。
正如你所知道的Java内部类是在其他类的范围内被定义的,同理,inner beans是在其他bean的范围内定义的bean。因此或元素中的元素被称为内部bean.
现在如何你想传递多个值,如Java Collection类型LIst、Set、Map、Properties。为了处理这种情况,Spring提供了四种类型的集合的配置元素:
元素 | 描述 |
---|---|
有助于连线,如注入一列值,允许重复 | |
有助于连线,如注入一列值,不能重复 | |
可以用来注入键值对的集合,其中键和值可以是任何类型 | |
可以用来注入键值对的集合,其中键和值可以都是字符串类型 |
public class User {
private List list;
private Set set;
private Map map;
private Properties pros;
public List getList() {
System.out.println("User.list="+list);
return list;
}
public void setList(List list) {
this.list = list;
}
public Set getSet() {
System.out.println("User.set="+set);
return set;
}
public void setSet(Set set) {
this.set = set;
}
public Map getMap() {
System.out.println("User.map="+map);
return map;
}
public void setMap(Map map) {
this.map = map;
}
public Properties getPros() {
System.out.println("User.pros="+pros);
return pros;
}
public void setPros(Properties pros) {
this.pros = pros;
}
}
list1
list2
list3
set1
set2
set3
RTT1
RTT2
RTT3
public class test {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("xml/Beans.xml");
User user=(User)context.getBean("user");
user.getList();
user.getMap();
user.getPros();
user.getSet();
}
}
注入空字符串
Spring容器可以在不使用和元素的情况下自动装配相互协作的bean之间的关系,这有助于减少编写一个大的基于Spring的应用程序的XML配置的数量。
自动装配模式
下列自动装配模式,它们可用于指示Spring容器为来使用自动装配依赖注入。你可以使用元素的autowire属性为一个bean定义指定自动装配模式。
模式 | 描述 |
---|---|
byName | 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 |
byType | 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 |
autodetect(3.0版本不支持) | Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
自动装配的局限性
限制 | 描述 |
---|---|
重写的可能性 | 你可以使用总是重写自动装配的 和 设置来指定依赖关系。 |
原始数据类型 | 你不能自动装配所谓的简单类型包括基本类型,字符串和类。 |
混乱的本质 | 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。 |
public class Student {
private String name;
private Book book;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
}
public class Book {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class test {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("xml/Beans.xml");
Student student=(Student)context.getBean("student");
System.out.println(student.getName()+"看了"+student.getBook().getName()+"这本书");
}
}
将之前的配置文件改为如下,就是按照类型自动装配
public class Student {
private String name;
private Book book;
public Student(String name, Book book) {
this.name = name;
this.book = book;
}
public String getName() {
return name;
}
public Book getBook() {
return book;
}
}
从Spring2.5开始就可以使用注解来配置依赖注入,而不是采用XML,你可以使用相关类,方法或字段声明的注解,将bean配置移动到组件类本身
注解连线在默认情况下在 Spring 容器中不打开。因此,在可以使用基于注解的连线之前,我们将需要在我们的 Spring 配置文件中启用它。所以如果你想在 Spring 应用程序中使用的任何注解,可以考虑到下面的配置文件。
序号 | 注解&描述 |
---|---|
1 | @Required 注解应用于 bean 属性的 setter 方法。 |
2 | @Autowired 注解可以应用到 bean 属性的 setter 方法,非 setter 方法,构造函数和属性。 |
3 | @Qualifier通过指定确切的将被连线的 bean,@Autowired 和 @Qualifier 注解可以用来删除混乱。 |
4 | JSR-250 Annotations:Spring 支持 JSR-250 的基础的注解,其中包括了 @Resource,@PostConstruct 和 @PreDestroy 注解。 |
@Required注解应用于bean属性的setter方法,它表明受影响的bean属性在配置时必须放在XML配置文件中,idea会出现爆红
public class Book {
private String name;
public String getName() {
return name;
}
@Required
public void setName(String name) {
this.name = name;
}
}
@Autowired注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
那么使用@Autowire的原理是什么?
其实在启动spring IoC时,容器自动装载了一个AutowiredAnnotationPostProcessor后置处理器,当容器扫描到@Autowired、@Resource、@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性
注意事项:
在使用@Autowired时,首先在容器中查询对应类型的bean,如果查询结果刚好就一个,将该bean装配给@Autowired指定的数据,如果查询的结果不止一个(例如这个类被继承或是实现),那么@Autowired会根据名称来查找,可以使用@Qualifier指定需要装配bean的名称,如果查询的结果为空,那么会抛出异常,解决方法:使用required=false
当你创建多个具有相同类型的bean时,并且想要用一个属性只为它们其中一个进行装配,在这种情况下,你可以使用@Qualifier注解和@Autowired注解通过指定哪一个真正的bean将会被装配来消除误会。
public class Book {
private String name;
public String getName() {
return name;
}
@Required
public void setName(String name) {
this.name = name;
}
@PostConstruct
public void init(){
System.out.println("实例化前");
}
@PreDestroy
public void destroy(){
System.out.println("销毁前");
}
}
public class test {
public static void main(String[] args) {
AbstractApplicationContext context1=new ClassPathXmlApplicationContext("xml/BeanAs.xml");
Book book=(Book)context1.getBean("book");
System.out.println(book.getName());
context1.registerShutdownHook();
}
}
singleton作用域的bean通常会随着容器的关闭而销毁,但问题是:ApplicationContext容器在什么时候关闭呢?在基于Web的ApplicationContext实现中,系统已经提供了相应的代码保证关闭Web应用时恰当的关闭Spring容器。但对于一个非Web应用的环境下,为了让Spring容器优雅的关闭,并自动调用singleton上的相应回调方法,则需要在JVM里面注册一个关闭钩子(shutdown hook)也就是registerShutdownHook(),这样就可以保证Spring容器被恰当关闭,并自动执行singleton的Bean里面的相应回调方法。
使用@PostConstruct和@PreDestroy可以作为init-method="init"和destroy-method=“destroy”的替代
@Resource
该属性以一个bean名称的形式被注入,遵循byName自动连接语义。
基于Java的配置选项,可以使你在不用配置XML的情况下编写大多数的Spring.
@Configuration和@Bean注解
带有@Configuration的注解类表示这个类可以使用Spring IoC容器作为bean定义的来源。@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
package com.tutorialspoint;
import org.springframework.context.annotation.*;
@Configuration
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
上面的代码等同于下面的XML配置:
@Configuration
public class HelloConfig {
@Bean
public Book book(){
return new Book();
}
//注入bean的依赖性
@Bean
public Student student(){
return new Student("RTT",new Book());
}
}
public class testAnnotation {
public static void main(String[] args) {
AbstractApplicationContext ctx=
new AnnotationConfigApplicationContext(HelloConfig.class);
Book book=ctx.getBean(Book.class);
Student student=ctx.getBean(Student.class);
book.setName("455");
System.out.println(book.getName());
System.out.println(student.getBook().getName());
System.out.println( student.getName());
ctx.registerShutdownHook();
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
@Import注解
@import注解允许从另一个配置类中加载@Bean定义。
@Configuration
@Import(HelloConfig.class)
public class HelloBConfig {
@Bean
public User user(){
return new User();
}
}
public class testAnnotation {
public static void main(String[] args) {
//将HelloConfig改成HelloBConfig
AbstractApplicationContext ctx=
new AnnotationConfigApplicationContext(HelloBConfig.class);
Book book=ctx.getBean(Book.class);
Student student=ctx.getBean(Student.class);
book.setName("455");
System.out.println(book.getName());
System.out.println(student.getBook().getName());
System.out.println( student.getName());
ctx.registerShutdownHook();
}
}
结果没有变化
生命周期回调
@Bean注解支持指定任意的初始化和销毁的回调方法:
public class Foo {
public void init() {
// initialization logic
}
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup" )
public Foo foo() {
return new Foo();
}
}
指定Bean的范围:
默认范围是单实例,但是你可以重写带有@Scope注解的该方法:
@Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public Foo foo() {
return new Foo();
}
}
你已经看到了在所有章节中 Spring 的核心是 ApplicationContext,它负责管理 beans 的完整生命周期。当加载 beans 时,ApplicationContext 发布某些类型的事件。例如,当上下文启动时,ContextStartedEvent 发布,当上下文停止时,ContextStoppedEvent 发布。
通过 ApplicationEvent 类和 ApplicationListener 接口来提供在 ApplicationContext 中处理事件。如果一个 bean 实现 ApplicationListener,那么每次 ApplicationEvent 被发布到 ApplicationContext 上,那个 bean 会被通知。
Spring提供了以下的标准事件:
序号 | Spring内置事件&描述 |
---|---|
1 | ContextRefreshedEvent:ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。 |
2 | ContextStartedEvent:当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。 |
3 | ContextStoppedEvent:当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。 |
4 | ContextClosedEvent:当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。 |
5 | RequestHandledEvent:这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。 |
public class ContextStartHandler implements ApplicationListener {
@Override
public void onApplicationEvent(ContextStartedEvent contextStartedEvent) {
System.out.println("上下文开启被监听到了");
}
}
public class ContextStopHandler implements ApplicationListener {
@Override
public void onApplicationEvent(ContextStoppedEvent contextStoppedEvent) {
System.out.println("上下文停止被监听到了");
}
}
public class testEvent {
public static void main(String[] args) {
ConfigurableApplicationContext context=new ClassPathXmlApplicationContext("xml/BeanAs.xml");
context.start();
Book book=(Book)context.getBean("book");
System.out.println("book="+book.getName());
context.stop();
}
}
public class CustomEvent extends ApplicationEvent {
public CustomEvent(Object source) {
super(source);
}
public void happen(){
System.out.println("自定义事件发生");
}
}
public class CustomPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher=applicationEventPublisher;
}
public void publish(){
CustomEvent customEvent=new CustomEvent(this);
applicationEventPublisher.publishEvent(customEvent);
}
}
public class CustomListener implements ApplicationListener {
@Override
public void onApplicationEvent(CustomEvent customEvent) {
customEvent.happen();
}
}
public class testCustomEvent {
public static void main(String[] args) {
ConfigurableApplicationContext context=
new ClassPathXmlApplicationContext("xml/BeanBs.xml");
CustomPublisher customPublisher=(CustomPublisher)context.getBean("customPublisher");
customPublisher.publish();
customPublisher.publish();
}
}
Spring框架的关键组件是面向切面编程框架。面向切面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。在软件开发过程中有各种各样的很好的切面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
AOP术语
顶 | 描述 |
---|---|
Aspect | 一个模块具有一组提供横切需求的 APIs。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面,这取决于需求。 |
Join point | 在你的应用程序中它代表一个点,你可以在插件 AOP 方面。你也能说,它是在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。 |
Advice | 这是实际行动之前或之后执行的方法。这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。 |
Pointcut | 这是一组一个或多个连接点,通知应该被执行。你可以使用表达式或模式指定切入点正如我们将在 AOP 的例子中看到的。 |
Introduction | 引用允许你添加新方法或属性到现有的类中。 |
Target object | 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。 |
Weaving | Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。 |
通知的类型:
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知 |
后置通知 | 在一个方法之后,不考虑其结果,执行通知 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知 |
环绕通知 | 在建议方法调用之前和之后,执行通知 |
实现自定义方面
Spring支持@AspectJ annotation tyle的方法和基于模式来实现自定义方面。
方法 | 描述 |
---|---|
XML Schama based | 方面是使用常规类以及基于配置的XML来实现的 |
@AspectJ based | @AspectJ引用一种声明方面的风格作为带有Java5注释的常规Java类注释 |
在使用普通的JDBC框架时,会需要写一些代码来打开连接和关闭连接等。但Spring JDBC框架负责所有的底层细节,从开始打开连接,准备和执行SQL语句,处理异常,处理事务,到最后关闭连接。
JdbcTemplate类
JdbcTemplate类执行SQL查询、更新语句和存储过程调用,执行迭代结果集和提取返回参数值。它也捕获JDBC异常并转换它们到org.springframework.dao包中定义的通用类、更多的信息、异常层次结构。
JdbcTemplate类的实例是线程安全配置的。所以你可以配置JdbcTemplate的单个实例,然后将这个共享的引用安全地注入到多个DAOs中。
使用JdbcTemplate类时常见的做法是在你的Spring配置文件中配置数据源,然后共享数据源bean依赖注入到DAO类中,并在数据源的设值函数中创建了JdbcTemplate。
ACID:
Spring支持两种类型的事务管理:
Spring 事务抽象
Spring事务管理的五大属性:隔离级别、传播行为、是否只读、事务超时、回滚规则
MVC框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的web应用程序的组件。
DispatcherServlet
MVC框架是围绕DispatcherServlet设计的,DispatcherServlet用来处理所有的HTTP请求和响应,工作流程如下图:
上面所提到的所有组件,即HandlerMapping、Controller和ViewResolver是WebApplicationContext的一部分,而WebApplicationContext是带有一些对web应用程序必要的额外特性的ApplicationContext的扩展。