Spring笔记
- 1.概述
- 1.1 IOC
- 1.2.context上下文和bean
- 1.3.AOP
- 2.IoC 控制反转
- 2.1. Spring IoC容器和Bean简介
- 2.2. 容器概述
- 2.2.1. 配置元数据
- 2.2.2. 实例化一个容器
- 2.2.3. 使用容器
- 2.3. Bean 概览
- 2.3.1. Bean 命名
- 2.3.2. 实例化 Bean
- 2.3.3 bean的生命周期
- 3.AOP
- AOP 概念
参考资料spring中文文档
1.概述
Spring是一个JavaEE轻量级的一站式开发框架。
它提供的功能涵盖了JavaEE程序中的表示层,服务层,持久层功能组件。这意味着,单单Spring框架就可以满足整个JavaEE程序的开发。
为了降低Java开发的复杂性,Spring采用了以下4种关键策略:
1、基于POJO的轻量级和最小侵入性编程;
2、通过依赖注入(DI)和面向接口实现松耦合;
3、基于切面(AOP)和惯例进行声明式编程;
4、通过切面和模版减少样式代码;
1.1 IOC
IOC,就是控制反转,如最左边,拿公司招聘岗位来举例:
假设一个公司有产品、研发、测试等岗位。如果是公司根据岗位要求,逐个安排人选,如图中向下的箭头,这是正向流程。如果反过来,不用公司来安排候选人,而是由第三方猎头来匹配岗位和候选人,然后进行推荐,如图中向上的箭头,这就是控制反转。
在spring中**,对象的属性是由对象自己创建的,就是正向流程**;如果属性不是对象创建,而是由spring来自动进行装配,就是控制反转。这里的DI也就是依赖注入,就是实现控制反转的方式。正向流程导致了对象于对象之间的高耦合,IOC可以解决对象耦合的问题,有利于功能的复用,能够使程序的结构变得非常灵活。
1.2.context上下文和bean
spring进行IOC实现时使用的有两个概念:context上下文和bean。
如中间图所示,所有被spring管理的、由spring创建的、用于依赖注入的对象,就叫做一个bean。Spring创建并完成依赖注入后,所有bean统一放在一个叫做context的上下文中进行管理。
1.3.AOP
AOP就是面向切面编程。如右面的图,一般程序执行流程是从controller层调用service层、然后service层调用DAO层访问数据,最后再逐层返回结果。
这个是图中向下箭头所示的按程序执行顺序的纵向处理。但是,一个系统中会有多个不同的服务,例如用户服务、商品信息服务等等,每个服务的controller层都需要验证参数,都需要处理异常,如果按照图中红色的部分,对不同服务的纵向处理流程进行横切,在每个切面上完成通用的功能,例如身份认证、验证参数、处理异常等等、这样就不用在每个服务中都写相同的逻辑了,这就是AOP思想解决的问题。
AOP以功能进行划分,对服务顺序执行流程中的不同位置进行横切,完成各服务共同需要实现的功能。
2.IoC 控制反转
2.1. Spring IoC容器和Bean简介
IoC也被称为依赖注入(DI)。它是一个过程,对象仅通过构造参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后在其上设置的属性来定义其依赖关系(即它们与之合作的其他对象)。然后容器在创建 bean 时注入这些依赖关系。这个过程从根本上说是Bean本身通过使用直接构建类或诸如服务定位模式的机制来控制其依赖关系的实例化或位置的逆过程(因此被称为控制反转)。
org.springframework.beans 和 org.springframework.context 包是Spring Framework的IoC容器的基础。 BeanFactory 接口提供了一种高级配置机制,能够管理任何类型的对象。 ApplicationContext 是 BeanFactory 的一个子接口。它增加了:
- 更容易与Spring的AOP功能集成
- Message resource 处理(用于国际化)
- 事件发布
- 应用层的特定上下文,如 WebApplicationContext,用于 web 应用
简而言之,BeanFactory 提供了配置框架和基本功能,而 ApplicationContext 则增加了更多的企业特定功能。ApplicationContext 是 BeanFactory 的一个完整的超集,在本章对Spring的IoC容器的描述中专门使用。关于使用 BeanFactory 而不是 ApplicationContext 的更多信息,请参见涵盖 BeanFactory API 的章节。
在Spring中,构成你的应用程序的骨干并由Spring IoC容器管理的对象被称为Bean。Bean是一个由Spring IoC容器实例化、组装和管理的对象。否则,Bean只是你的应用程序中众多对象中的一个。Bean以及它们之间的依赖关系都反映在容器使用的配置元数据中。
2.2. 容器概述
org.springframework.context.ApplicationContext 接口代表Spring IoC容器,负责实例化、配置和组装bean。容器通过读取配置元数据来获得关于要实例化、配置和组装哪些对象的指示。配置元数据以XML、Java注解或Java代码表示。它可以让你表达构成你的应用程序的对象以及这些对象之间丰富的相互依赖关系。
Spring提供了几个 ApplicationContext 接口的实现。在独立的应用程序中,创建 ClassPathXmlApplicationContext 或 FileSystemXmlApplicationContext 的实例很常见。虽然 XML 一直是定义配置元数据的传统格式,但你可以通过提供少量的 XML 配置来指示容器使用 Java 注解或代码作为元数据格式,以声明性地启用对这些额外元数据格式的支持。
在大多数应用场景中,不需要明确的用户代码来实例化Spring IoC容器的一个或多个实例。例如,在Web应用场景中,通常只需在应用程序的 web.xml 文件中编写8行(或更多)模板式的Web描述符就足够了(参见 为web应用程序提供方便的 ApplicationContext 实例化)。如果你使用 Spring Tools for Eclipse(一个由Eclipse驱动的开发环境),你只需点击几下鼠标或按键就可以轻松创建这种模板配置。
下图显示了Spring工作方式的高层视图。你的应用程序类与配置元数据相结合,这样,在 ApplicationContext 被创建和初始化后,你就有了一个完全配置好的可执行系统或应用程序。
2.2.1. 配置元数据
如上图所示,Spring IoC容器消费一种配置元数据。这种配置元数据代表了你,作为一个应用开发者,如何告诉Spring容器在你的应用中实例化、配置和组装对象。
配置元数据传统上是以简单直观的XML格式提供的,这也是本章大部分内容用来传达Spring IoC容器的关键概念和特性。
基于XML的元数据并不是配置元数据的唯一允许形式。Spring IoC容器本身与这种配置元数据的实际编写格式是完全解耦的。如今,许多开发者为他们的Spring应用程序选择 基于Java的配置。
关于在Spring容器中使用其他形式的元数据的信息,请参见。
基于注解的配置: 使用基于注解的配置元数据定义Bean。
Java-based configuration: 通过使用Java而不是XML文件来定义你的应用类外部的Bean。要使用这些特性,请参阅 @Configuration, @Bean, @Import, 和 @DependsOn 注解。
Spring的配置包括至少一个,通常是一个以上的Bean定义,容器必须管理这些定义。基于XML的配置元数据将这些Bean配置为顶层 元素内的 元素。Java配置通常使用 @Configuration 类中的 @Bean 注解的方法。
这些Bean的定义对应于构成你的应用程序的实际对象。通常,你会定义服务层对象、持久层对象(如存储库或数据访问对象(DAO))、表现对象(如Web控制器)、基础设施对象(如JPA EntityManagerFactory)、JMS队列等等。通常,人们不会在容器中配置细粒度的domain对象,因为创建和加载domain对象通常是 repository 和业务逻辑的责任。
下面的例子显示了基于XML的配置元数据的基本结构。
<?xml version="1.0" encoding="UTF-8"?><bean id="..." class="...">
<!-- 这个bean的合作者和配置在这里 -->
</bean>
<bean id="..." class="...">
<!-- c这个bean的合作者和配置在这里 -->
</bean>
<!-- 更多bean 定义在这里 -->
id 属性是一个字符串,用于识别单个Bean定义。
class 属性定义了 Bean 的类型,并使用类的全路径名。
id 属性的值可以用来指代协作对象。本例中没有显示用于引用协作对象的XML。更多信息请参见 依赖。
2.2.2. 实例化一个容器
提供给 ApplicationContext 构造函数的一条或多条路径是资源字符串,它让容器从各种外部资源(如本地文件系统、Java CLASSPATH 等)加载配置元数据。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
在了解了Spring的IoC容器后,你可能想了解更多关于Spring的 Resource 抽象(如 资源(Resources) 中所述),它为从URI语法中定义的位置读取 InputStream 提供了方便的机制。特别是,Resource 路径被用来构建应用上下文,如 Application Context 和资源路径 中所述。
下面的例子显示了 service 对象(services.xml)配置文件。
<?xml version="1.0" encoding="UTF-8"?><!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
下面的例子显示了数据访问对象(data access object) daos.xml 文件。
<?xml version="1.0" encoding="UTF-8"?><bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
在前面的例子中,服务层由 PetStoreServiceImpl 类和两个类型为 JpaAccountDao 和 JpaItemDao 的数据访问对象组成(基于JPA对象-关系映射标准)。property name 元素指的是 JavaBean 属性的名称,而 ref 元素指的是另一个Bean定义的名称。id 和 ref 元素之间的这种联系表达了协作对象之间的依赖关系。关于配置一个对象的依赖关系的细节,请看 依赖。
构建基于XML的配置元数据
让Bean的定义跨越多个XML文件可能很有用。通常情况下,每个单独的XML配置文件代表了你架构中的一个逻辑层或模块。
你可以使用 application context 构造函数从所有这些XML片段中加载Bean定义。这个构造函数需要多个 Resource 位置,如 上一节 所示。或者,使用一个或多个 元素的出现来从另一个或多个文件中加载Bean定义。下面的例子展示了如何做到这一点。
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
在前面的例子中,外部Bean定义从三个文件中加载:services.xml、messageSource.xml 和 themeSource.xml。所有的位置路径都是相对于进行导入的定义文件而言的,所以 services.xml 必须与进行导入的文件在同一目录或 classpath 位置,而 messageSource.xml 和 themeSource.xml 必须在导入文件的位置以下的 resources 位置。
命名空间本身提供了导入指令的功能。除了普通的Bean定义之外,更多的配置功能可以在Spring提供的一些XML命名空间中获得,例如,context 和 util 命名空间。
Groovy Bean Definition DSL
作为外部化配置元数据的另一个例子,Bean定义也可以用Spring的Groovy Bean Definition DSL来表达,正如Grails框架所知道的。通常情况下,这种配置存在于 “.groovy” 文件中,其结构如下例所示。
beans {
dataSource(BasicDataSource) {
driverClassName = “org.hsqldb.jdbcDriver”
url = “jdbc:hsqldb:mem:grailsDB”
username = “sa”
password = “”
settings = [mynew:“setting”]
}
sessionFactory(SessionFactory) {
dataSource = dataSource
}
myService(MyService) {
nestedBean = { AnotherBean bean ->
dataSource = dataSource
}
}
}
这种配置风格基本上等同于XML Bean定义,甚至支持Spring的XML配置命名空间。它还允许通过 importBeans 指令导入XML Bean定义文件。
2.2.3. 使用容器
ApplicationContext 是一个高级工厂的接口,能够维护不同Bean及其依赖关系的注册表。通过使用方法 T getBean(String name, Class requiredType),你可以检索到Bean的实例。
ApplicationContext 可以让你读取Bean定义(definition)并访问它们,如下例所示。
// 创建和配置bean
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// 检索配置的实例
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// 使用配置的实例
List<String> userList = service.getUsernameList();
通过Groovy配置,引导看起来非常相似。它有一个不同的 context 实现类,它能识别Groovy(但也能理解XML bean定义)。下面的例子显示了 Groovy 配置。
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
最灵活的变体是 GenericApplicationContext 与 reader delegate 的结合—例如,与 XmlBeanDefinitionReader 一起用于XML文件,如下例所示。
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
你也可以将 GroovyBeanDefinitionReader 用于Groovy文件,如下例所示。
GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
你可以在同一个 ApplicationContext 上混合和匹配这样的 reader delegate,从不同的配置源读取bean定义。
然后你可以使用 getBean 来检索Bean的实例。ApplicationContext 接口还有其他一些检索Bean的方法,但理想情况下,你的应用代码不应该使用这些方法。事实上,你的应用程序代码根本就不应该调用 getBean() 方法,因此对Spring的API根本就没有依赖性。例如,Spring与Web框架的集成为各种Web框架组件(如 controller 和JSF管理的Bean)提供了依赖注入,让你通过元数据(如autowiring注解)声明对特定Bean的依赖。
2.3. Bean 概览
一个Spring IoC容器管理着一个或多个Bean。这些Bean是用你提供给容器的配置元数据创建的(例如,以XML 定义的形式)。
在容器本身中,这些Bean定义被表示为 BeanDefinition 对象,它包含(除其他信息外)以下元数据。
一个全路径类名:通常,被定义的Bean的实际实现类。
Bean的行为配置元素,它说明了Bean在容器中的行为方式(scope、生命周期回调,等等)。
对其他Bean的引用,这些Bean需要做它的工作。这些引用也被称为合作者或依赖。
要在新创建的对象中设置的其他配置设置—例如,pool的大小限制或在管理连接池的Bean中使用的连接数。
这个元数据转化为构成每个Bean定义的一组属性。下表描述了这些属性。
除了包含如何创建特定 Bean 的信息的 Bean 定义外,ApplicationContext 实现还允许注册在容器外(由用户)创建的现有对象。这是通过 getBeanFactory() 方法访问 ApplicationContext 的 BeanFactory 来实现的,该方法返回 DefaultListableBeanFactory 实现。DefaultListableBeanFactory 通过 registerSingleton(…) 和 registerBeanDefinition(…) 方法支持这种注册。然而,典型的应用程序只与通过常规Bean定义元数据定义的Bean一起工作。
Bean 元数据和手动提供的单体实例需要尽早注册,以便容器在自动注入和其它内省步骤中正确推导它们。虽然在某种程度上支持覆盖现有的元数据和现有的单体实例,但 官方不支持在运行时注册新的Bean(与对工厂的实时访问同时进行),这可能会导致并发访问异常、Bean容器中的不一致状态,或者两者都有。
2.3.1. Bean 命名
每个Bean都有一个或多个标识符(identifier)。这些标识符在承载Bean的容器中必须是唯一的。一个Bean通常只有一个标识符。然而,如果它需要一个以上的标识符,多余的标识符可以被视为别名。
在基于XML的配置元数据中,你可以使用 id 属性、name 属性或两者来指定Bean标识符。id 属性允许你精确地指定一个 id。传统上,这些名字是字母数字(‘myBean’、'someService’等),但它们也可以包含特殊字符。如果你想为Bean引入其他别名,你也可以在 name 属性中指定它们,用逗号(,)、分号(;)或空格分隔。尽管 id 属性被定义为 xsd:string 类型,但 bean id 的唯一性是由容器强制执行的,尽管不是由 XML 解析器执行。
你不需要为Bean提供一个 name 或 id。如果你不明确地提供 name 或 id,容器将为该 Bean 生成一个唯一的名称。然而,如果你想通过使用 ref 元素或服务定位器风格的查找来引用该 bean 的名称,你必须提供一个名称。不提供名字的动机与使用 内部Bean 和 注入协作者(Autowiring Collaborators) 有关。
Bean的命名规则
惯例是在命名Bean时使用标准的Java惯例来命名实例字段名。也就是说,Bean的名字以小写字母开始,然后以驼峰字母开头。这种名称的例子包括 accountManager、accountService、userDao、loginController 等等。
统一命名Bean使你的配置更容易阅读和理解。另外,如果你使用Spring AOP,在对一组按名称相关的Bean应用 advice 时,也有很大的帮助。
在classpath中的组件扫描(component scanning),Spring为未命名的组件生成Bean名称,遵循前面描述的规则:基本上,取简单的类名并将其初始字符变成小写。然而,在(不寻常的)特殊情况下,当有一个以上的字符,并且第一个和第二个字符都是大写时,原来的大小写会被保留下来。这些规则与 java.beans.Introspector.decapitalize(Spring在此使用)所定义的规则相同。
在 Bean Definition 之外对Bean进行别名
在 Bean 定义中,你可以为Bean提供一个以上的名字,通过使用由 id 属性指定的最多一个名字和 name 属性中任意数量的其他名字的组合。这些名字可以是同一个Bean的等效别名,在某些情况下很有用,比如让应用程序中的每个组件通过使用一个特定于该组件本身的Bean名字来引用一个共同的依赖关系。
然而,在实际定义Bean的地方指定所有别名并不总是足够的。有时,为一个在其他地方定义的Bean引入别名是可取的。这种情况通常发生在大型系统中,配置被分割到每个子系统中,每个子系统都有自己的对象定义集。在基于XML的配置元数据中,你可以使用 元素来实现这一点。下面的例子展示了如何做到这一点。
在这种情况下,一个名为 fromName 的bean(在同一个容器中)在使用这个别名定义后,也可以被称为 toName。
例如,子系统A的配置元数据可以引用一个名为 subsystemA-dataSource 的数据源。子系统B的配置元数据可以引用一个名为 subsystemB-dataSource 的数据源。当组成使用这两个子系统的主应用程序时,主应用程序以 myApp-dataSource 的名字来引用数据源。为了让这三个名字都指代同一个对象,你可以在配置元数据中添加以下别名定义。
现在,每个组件和主应用程序都可以通过一个独特的名称来引用dataSource,并保证不与任何其他定义冲突(有效地创建了一个命名空间),但它们引用的是同一个bean。
Java 配置
如果你使用Java配置,@Bean 注解可以被用来提供别名。详情请参见 使用 @Bean 注解。
2.3.2. 实例化 Bean
bean 定义(definition)本质上是创建一个或多个对象的“配方”。容器在被要求时查看命名的Bean的“配方”,并使用该Bean定义所封装的配置元数据来创建(或获取)一个实际的对象。
如果你使用基于XML的配置元数据,你要在 元素的 class 属性中指定要实例化的对象的类型(或class)。这个 class 属性(在内部是 BeanDefinition 实例的 Class 属性)通常是强制性的。(关于例外情况,请看 用实例工厂方法进行实例化 和 Bean 定义(Definition)的继承)。你可以以两种方式之一使用 Class 属性。
通常,在容器本身通过反射式地调用构造函数直接创建Bean的情况下,指定要构造的Bean类,有点相当于Java代码中的 new 操作符。
在不太常见的情况下,即容器在一个类上调用 static 工厂方法来创建 bean 时,要指定包含被调用的 static 工厂方法的实际类。从 static 工厂方法的调用中返回的对象类型可能是同一个类或完全是另一个类。
嵌套类名
如果你想为一个嵌套类配置一个Bean定义(definition),你可以使用嵌套类的二进制名称或源(source)名称。
例如,如果你在 com.example 包中有一个叫做 SomeThing 的类,而这个 SomeThing 类有一个叫做 OtherThing 的静态嵌套类,它们可以用美元符号( )或点( . )分开。所以在 B e a n 定义中的 c l a s s 属性的值将是 c o m . e x a m p l e . S o m e T h i n g )或点(.)分开。所以在Bean定义中的 class 属性的值将是 com.example.SomeThing )或点(.)分开。所以在Bean定义中的class属性的值将是com.example.SomeThingOtherThing 或 com.example.SomeThing.OtherThing。
用构造函数进行实例化
当你用构造函数的方法创建一个Bean时,所有普通的类都可以被Spring使用并与之兼容。也就是说,被开发的类不需要实现任何特定的接口,也不需要以特定的方式进行编码。只需指定Bean类就足够了。然而,根据你对该特定Bean使用的IoC类型,你可能需要一个默认(空)构造函数。
Spring IoC容器几乎可以管理任何你希望它管理的类。它并不局限于管理真正的JavaBean。大多数Spring用户更喜欢真正的JavaBean,它只有一个默认的(无参数)构造函数,以及按照容器中的属性建模的适当的setter和getter。你也可以在你的容器中拥有更多奇特的非bean风格的类。例如,如果你需要使用一个绝对不遵守JavaBean规范的传统连接池,Spring也可以管理它。
通过基于XML的配置元数据,你可以按以下方式指定你的bean类。
关于向构造函数提供参数(如果需要)和在对象被构造后设置对象实例属性的机制的详细信息,请参见 依赖注入。
用静态工厂方法进行实例化
在定义一个用静态工厂方法创建的Bean时,使用 class 属性来指定包含 static 工厂方法的类,并使用名为 factory-method 的属性来指定工厂方法本身的名称。你应该能够调用这个方法(有可选的参数,如后文所述)并返回一个活的对象,随后该对象被视为通过构造函数创建的。这种Bean定义的一个用途是在遗留代码中调用 static 工厂。
下面的Bean定义规定,Bean将通过调用工厂方法来创建。该定义并没有指定返回对象的类型(class),而是指定了包含工厂方法的类。在这个例子中,createInstance() 方法必须是一个 static 方法。下面的例子显示了如何指定一个工厂方法。
下面的例子显示了一个可以与前面的Bean定义(definition)一起工作的类。
JavaKotlin
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
关于向工厂方法提供(可选)参数以及在对象从工厂返回后设置对象实例属性的机制,详见 依赖和配置详解。
用实例工厂方法进行实例化
与 通过静态工厂方法进行的实例化 类似,用实例工厂方法进行的实例化从容器中调用现有 bean 的非静态方法来创建一个新的 bean。要使用这种机制,请将 class 属性留空,并在 factory-bean 属性中指定当前(或父代或祖代)容器中的一个 Bean 的名称,该容器包含要被调用来创建对象的实例方法。用 factory-method 属性设置工厂方法本身的名称。下面的例子显示了如何配置这样一个Bean。
下面的例子显示了相应的类。
JavaKotlin
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
一个工厂类也可以容纳一个以上的工厂方法,如下例所示。
下面的例子显示了相应的类。
JavaKotlin
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
这种方法表明,工厂Bean本身可以通过依赖注入(DI)进行管理和配置。请看详细的 依赖和配置。
在Spring文档中,“factory bean” 是指在Spring容器中配置的Bean,它通过 实例 或 静态工厂方法创建对象。相比之下,FactoryBean(注意大写字母)是指Spring特定的FactoryBean 实现类。
确定Bean的运行时类型
要确定一个特定Bean的运行时类型是不容易的。在Bean元数据定义中指定的类只是一个初始的类引用,可能与已声明的工厂方法相结合,或者是一个 FactoryBean 类,这可能导致Bean的运行时类型不同,或者在实例级工厂方法的情况下根本没有被设置(而是通过指定的 factory-bean 名称来解决)。此外,AOP代理可能会用基于接口的代理来包装Bean实例,对目标Bean的实际类型(只是其实现的接口)的暴露有限。
要了解某个特定Bean的实际运行时类型,推荐的方法是对指定的Bean名称进行 BeanFactory.getType 调用。这将考虑到上述所有情况,并返回 BeanFactory.getBean 调用将为同一Bean名称返回的对象类型。
2.3.3 bean的生命周期
第1步:调用bean的构造方法创建bean;
第2步:通过反射调用setter方法进行属性的依赖注入;
第3步:如果实现BeanNameAware接口的话,会设置bean的name;
第4步:如果实现了BeanFactoryAware,会把bean factory设置给bean;
第5步:如果实现了ApplicationContextAware,会给bean设置ApplictionContext;
第6步:如果实现了BeanPostProcessor接口,则执行前置处理方法;
第7步:实现了InitializingBean接口的话,执行afterPropertiesSet方法;
第8步:执行自定义的init方法;
第9步:执行BeanPostProcessor接口的后置处理方法。
3.AOP
AOP 概念
让我们首先定义一些核心的AOP概念和术语。这些术语并不是针对Spring的。不幸的是,AOP的术语并不是特别直观。然而,如果Spring使用自己的术语,那就更令人困惑了。
- Aspect(切面): 一个跨越多个类的关注点的模块化。事务管理是企业级Java应用中横切关注点的一个很好的例子。在Spring AOP中,切面是通过使用常规类(基于 schema 的方法)或使用 @Aspect 注解的常规类(@AspectJ 风格)实现的。
- Join point: 程序执行过程中的一个点,例如一个方法的执行或一个异常的处理。在Spring AOP中,一个连接点总是代表一个方法的执行。
- Advice: 一个切面在一个特定的连接点采取的行动。不同类型的advice包括 “around”、“before” 和 “after” 的advice(Advice 类型将在后面讨论)。许多AOP框架,包括Spring,都将advice建模为一个拦截器,并在连接点(Join point)周围维护一个拦截器链。
- Pointcut: 一个匹配连接点的谓词(predicate)。advice与一个切点表达式相关联,并在切点匹配的任何连接点上运行(例如,执行一个具有特定名称的方法)。由切点表达式匹配的连接点概念是AOP的核心,Spring默认使用AspectJ的切点表达式语言。
- Introduction: 代表一个类型声明额外的方法或字段。Spring AOP允许你为任何 advice 的对象引入新的接口(以及相应的实现)。例如,你可以使用引入来使一个bean实现 IsModified 接口,以简化缓存。(介绍在AspectJ社区中被称为类型间声明)。
- Target object: 被一个或多个切面所 advice 的对象。也被称为 “advised object”。由于Spring AOP是通过使用运行时代理来实现的,这个对象总是一个被代理的对象。
AOP proxy: 一个由AOP框架创建的对象,以实现切面契约(advice 方法执行等)。在Spring框架中,AOP代理是一个JDK动态代理或CGLIB代理。
Weaving(织入): 将aspect与其他应用程序类型或对象连接起来,以创建一个 advice 对象。这可以在编译时(例如,使用AspectJ编译器)、加载时或运行时完成。Spring AOP和其他纯Java AOP框架一样,在运行时进行织入。
Spring AOP包括以下类型的advice。
-
Before advice: 在连接点之前运行的Advice ,但它不具备以下能力 阻止执行流进行到 join point 的能力(除非它抛出一个异常)。
-
After returning advice: 在一个连接点正常完成后运行的Advice (例如,如果一个方法返回时没有抛出一个异常)。
-
After (finally) advice: 无论连接点以何种方式退出(正常或特殊返回),都要运行该advice。
-
Around advice: 围绕一个连接点的advice,如方法调用。这是最强大的一种advice。Around advice可以在方法调用之前和之后执行自定义行为。它还负责选择是否继续进行连接点或通过返回自己的返回值或抛出一个异常来缩短advice方法的执行。
Around advice 是最通用的一种advice。由于Spring AOP和AspectJ一样,提供了完整的advice类型,我们建议你使用功能最少的advice类型来实现所需的行为。例如,如果你只需要用一个方法的返回值来更新缓存,那么你最好实现一个 after returning advice,而不是一个 around advice,尽管 around advice 可以完成同样的事情。使用最具体的 advice 类型可以提供一个更简单的编程模型,减少错误的可能性。例如,你不需要在用于 around advice 的 JoinPoint 上调用 proceed() 方法,因此,你不能失败地调用它。
所有的advice参数都是静态类型的,因此你可以使用适当类型的advice参数(例如,方法执行的返回值的类型)而不是 Object 数组。
由 pointcuts 匹配的连接点的概念是AOP的关键,它区别于只提供拦截的旧技术。点切使advice能够独立于面向对象的层次结构而成为目标。例如,你可以将提供声明性事务管理的 around advice 应用于跨越多个对象的一组方法(如 service 层的所有业务操作)。