博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring官方文档阅读笔记
阅读量:6331 次
发布时间:2019-06-22

本文共 13709 字,大约阅读时间需要 45 分钟。

前言

几个月前阅读spring文档时做的笔记,记录了以写我认为重要的内容.

IOC container

IOC(Inverse of Control) 控制反转,也称为DI(Dependency Inject)依赖注入

一个对象定义它所需要的依赖,IOC容器会在对象创建时将这些依赖注入.

举个例子 如果没有IOC,对于依赖我们可能这样处理

public class A{    private B b;    public A(){        this.b=new B();    }}复制代码

现实情况会更复杂,B可能还有自己的依赖,如果B的实例化过程发生变化,所有对B有依赖的对象都要做修改.更麻烦的是,如果A,B有相同的依赖,或者形成的循环依赖(有一部分循环依赖IOC也无法处理,但是有很大一部分通过IOC根本不会形成循环依赖),依赖关系会更无法处理.

有了IOC就会变成这样

public class A{    @Autowired    private B b;}复制代码

A只需要声明它需要的依赖,IOC容器会在A实例化时自动注入相关的依赖.如果B发生变化,A无需改动.这里体现了IOC的一个重要作用解耦

org.springframework.beans 和 org.springframework.context

上面两个包是IOC容器的基础,BeanFactor提供了配置和管理功能,ApplicationContext在BeanFactory的基础上进一步封装,添加了一些企业级特性,更易集成使用

什么是bean

在Spring中,构成应用骨架的且由IOC Container进行管理的那些Object称为bean.一个bean就是一个由IOC Container进行实例化,装配且管理的Object

Container 概述

ApplicationContext

org.springframework.context.ApplicationContext 接口代表container,负责bean的实例化,配置和装配.

container通过读取configuration metadata获取实例化,配置和装配bean的说明,configuration metadata由xml文件,注解及java代码表示.

Spring 提供了两个开箱即用的ApplicationContext实现,  ClassPathXmlApplicationContextFileSystemXmlApplicationContext

Configuration metadata

configuration metadata 是用户(你)以开发者的角度提供给Spring的数据,告诉Spring应该怎样去处理bean.

主要有三种类型的metadata

  • XML-Based
  • Annotation-Based
  • Java-Based @Configuration @Bean @Import @DependsOn

对于XML,顶级标签下的每个元素表示一个bean definition

对于注解,含有@Configuration的类中含有@Bean的方法都是一个bean definition

使用import元素来整合多个配置文件(相对路径),Spring官方不推荐"../service.xml"这可能导致依赖

复制代码

基于groovy的bean definition,看起来更加方便

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        }    }}复制代码

容器的启动

基于groovy的启动

ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");复制代码

GeneralApplicationContext使用最灵活,也最麻烦,需要关注细节,如下

GenericApplicationContext context = new GenericApplicationContext();new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");context.refresh();复制代码

beanDefinition

如果没有提供命名,默认以下面这种方式命名:bean names start with a lowercase letter, and are camel-cased from then on

class
name
scope
constructor arguments
properties
autowiring mode
lazy-initialization mode
initialization method
destruction method

基于xml的实例化

  • 通过构造器实例化
复制代码
  • 通过静态工厂方法实例化
复制代码
  • 通过工厂方法实例化
复制代码

DI

  • 构造器注入
public class SimpleMovieLister {    // the SimpleMovieLister has a dependency on a MovieFinder    private MovieFinder movieFinder;    // a constructor so that the Spring container can inject a MovieFinder    public SimpleMovieLister(MovieFinder movieFinder) {        this.movieFinder = movieFinder;    }    // business logic that actually uses the injected MovieFinder is omitted...}复制代码
  • 构造器注入不同类型的多个参数
复制代码
  • 构造器注入多个不同简单类型
复制代码
  • 构造器注入时指定顺序
复制代码
  • 构造器注入时指定名称(需要在编译时开启debug)
复制代码
  • 作为前者的替代,使用java annotation
package examples;public class ExampleBean {    // Fields omitted    @ConstructorProperties({
"years", "ultimateAnswer"}) public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; }}复制代码

构造器注入vs Setter注入

  • 构造器注入强制依赖不为空,并且可以保证bean在装配完成后不会再发生变化.
  • 原始的setter注入用来注入那些非必须的依赖,@Required注入可以使其变为强制依赖
  • 按照目前情况,setter注入更为实用,尤其是@Autowired注解

依赖解析过程

  1. ApplicationContext通过读取metadata完成创建和初始化
  2. 对于所有bean,它的依赖都是通过属性,构造器参数,静态工厂方法参数等,当bean真正被创建时(对于 scope为singleton的bean来说,立刻创建),这些依赖被注入
  3. 每个属性和构造器参数都是一个要被设置的值或是容器中其他bean的引用
  4. 原始类型的属性被会自动转化,例如将String转为int,long double等等.

使用setter注入取代构造器注入解决循环依赖问题??

ApplicationContext 默认使用 pre-instantiate初始化singleton的bean,也可以更改为lazy-initialize,如果一个bean到运行很长时间后才被请求到,这时候去创建时,很可能出现异常.Spring采用以启动时间和内存换去运行安全的策略

idref标签

用以传递值(而非ref),主要目的是提供部署时检查,通常用在

*A common place (at least in versions earlier than Spring 2.0) where the element brings value is in the configuration of  in a ProxyFactoryBean bean definition. Using  elements when you specify the interceptor names prevents you from misspelling an interceptor id. *

复制代码

作用和下面相同

复制代码

idref的local标签在beans xsd 4.0已被废弃

容器支持

administrator@example.org
support@example.org
development@example.org
a list element followed by a reference
just some string
复制代码

map或set中的值可以为以下几种

bean | ref | idref | list | set | map | props | value | null复制代码

继承时集合的融合和覆盖

administrator@example.com
support@example.com
sales@example.com
support@example.co.uk
复制代码

对于list有一点特殊,他有顺序的概念,父类优于子类

对集合中空字符串和null的处理

exampleBean.setEmail("");复制代码
exampleBean.setEmail(null);复制代码

复合属性要求路径上的对象不能为空, fred,bob都不能为空,否则nullpointer异常

复制代码

depends on

对于关联较小且由实例化顺序要求的两个bean,通过 depends on实现强制指定顺序处理

复制代码

对于多个依赖

复制代码

对于singleton的bean,depends on 还有控制 destory 顺序的作用

lazy-initializad

在第一次请求到时才初始化,不推荐,ApplicationContext的默认策略是pre-instantiation

复制代码

控制全局的初始化策略

复制代码

基于 xml的 autowired

不推荐 限制太多

方法注入

场景, 一个singleton的bean需要依赖于一个prototype的bean,或相反

有三个解决方案

  1. 通过实现 ApplicationContextAware 接口获取ApplicationContext,再用来实现自定义功能
// a class that uses a stateful Command-style class to perform some processingpackage fiona.apple;// Spring-API importsimport org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;public class CommandManager implements ApplicationContextAware {    private ApplicationContext applicationContext;    public Object process(Map commandState) {        // grab a new instance of the appropriate Command        Command command = createCommand();        // set the state on the (hopefully brand new) Command instance        command.setState(commandState);        return command.execute();    }    protected Command createCommand() {        // notice the Spring API dependency!        return this.applicationContext.getBean("command", Command.class);    }    public void setApplicationContext(            ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }}复制代码
  1. 通过 lookup的xml形式(通过cglib生成子类完成)
package fiona.apple;// no more Spring imports!public abstract class CommandManager {    public Object process(Object commandState) {        // grab a new instance of the appropriate Command interface        Command command = createCommand();        // set the state on the (hopefully brand new) Command instance        command.setState(commandState);        return command.execute();    }    // okay... but where is the implementation of this method?    protected abstract Command createCommand();}
复制代码
  1. 通过lookup的注解形式,最方便,推荐
public abstract class CommandManager {    public Object process(Object commandState) {        Command command = createCommand();        command.setState(commandState);        return command.execute();    }    @Lookup("myCommand")    protected abstract Command createCommand();}public abstract class CommandManager {    public Object process(Object commandState) {        MyCommand command = createCommand();        command.setState(commandState);        return command.execute();    }    @Lookup    protected abstract MyCommand createCommand();}复制代码

注意

  1. 该类不能是final,被覆盖的方法也不能是final
  2. 无法于工厂方法一起工作,对于@Configuration的@Bean也是无法工作的
  3. 单元测试需要自己去stub一个子类

Scope

singleton

每个容器中只会有一个singleton的bean,Spring的singleton定义为per container and per bean

prototype

每次向容器请求时都会生成一个新的Object,与其它scope的类型最大的区别就是它的回收需要客户端自己去处理(可以通过beanPostProcessor处理)

long-live bean中注入short-live bean时可以使用以下方法

复制代码

上面的例子中,userPreferences有apo:scoped-proxy/,这说明他是一个被代理的对象,每次userService调用到它时,实际是调用了代理,代理通过自身逻辑获取对应的session-scoped的bean,再执行真实对象的操作(默认使用的CGLIB)

自定义Scope

实现 org.springframework.beans.factory.config.Scope 实现自定义scope

Spring 拓展点

BeanPostProcessor

If you want to implement some custom logic after the Spring container finishes instantiating, configuring, and initializing a bean, you can plug in one or more BeanPostProcessor implementations.

对spring中解决bean的依赖并且初始化后的操作,可以使用BeanPostProcessor

  1. 在Spring调用afterPropertiesSet或initMethod之前, beanPostProcessor会被调用
  2. 在Spring调用initMethod之后,BeanPostProcessor会被调用

ApplicationContext会检查BeanDefinition,对实现了BeanPostProcessor的bean,将其注册为BeanPostProcessor

作为BeanPostProcessor的bean是不会被BeanPostProcessor处理的

BeanFactoryPostProcessor

  1. PropertyPlaceholderConfigurer 实现参数值替换

    复制代码
  2. PropertyPlaceholderConfigurer 实现类名替换

classpath:com/foo/strategy.properties
custom.strategy.class=com.foo.DefaultStrategy
复制代码
  1. PropertyOverrideConfigurer 覆盖默认值
复制代码

FactoryBean 获取制造bean的bean

FactoryBean有三个方法

  1. Object getObject()
  2. boolean isSingleton()
  3. boolean isSingleton()

ApplicationContext.getBean("$beanName")可以获取FactoryBean

lifeCycle

afterPropertiesSet和PostConstruct

在Spring容器完成必要的依赖注入后,会调用该方法完成初始化,此时所有的BeanPostProcessor还未执行

复制代码
//这两个作用也相同,但是更推荐前者public class ExampleBean {    public void init() {        // do some initialization work    }}public class AnotherExampleBean implements InitializingBean {    public void afterPropertiesSet() {        // do some initialization work    }}复制代码

destroy和@PreDestroy

在Spring容器销毁前会调用该方法

复制代码
public class ExampleBean {    public void cleanup() {        // do some destruction work (like releasing pooled connections)    }}public class AnotherExampleBean implements DisposableBean {    public void destroy() {        // do some destruction work (like releasing pooled connections)    }}复制代码

*实现了java.lang.AutoCloseable or java.io.Closeable的bean,可以让Spring自动检测它的close/shutdown方法 *

如果同时定义个多个 lifecycle,会按照 annotation interface xml的顺序执行,并且同名的只执行一次

Startup and shutdown callbacks

stop不能保证

public interface Lifecycle {    void start();    void stop();    boolean isRunning();}public interface LifecycleProcessor extends Lifecycle {    void onRefresh();    void onClose();}复制代码

Context refresh :所有bean都被实例化和初始化后

@Autowired@Inject@Resource, and @Value annotations are handled by Spring BeanPostProcessorimplementations

@Autowired和@Resource的区别

@Resourceannotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process. @Autowired has rather different semantics: After selecting candidate beans by type, the specified String qualifier value will be considered within those type-selected candidates only, e.g. matching an "account" qualifier against beans marked with the same qualifier label.

CustomAutowireConfigurer

也是beanPostProcessor,用来处理自定义注解(如@Qualify)

@Autowired的匹配过程

先匹配名字的customerPreferenceDao的bean,再匹配类型是CustomerPreferenceDao的bean

public class MovieRecommender {    @Resource    private CustomerPreferenceDao customerPreferenceDao;    @Resource    private ApplicationContext context;    public MovieRecommender() {    }    // ...}复制代码

CommonAnnotationBeanPostProcessor

处理@Resource和与生命周期相关的bean

@ComponentScan(basePackages = "org.example")

完全的java config,与之对应的xml为

复制代码

the AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor are both included implicitly when you use the component-scan element.

使用component-scan后会自动包含 AutowiredAnnotationBeanPostProcessor 和CommonAnnotationBeanPostProcessor

在package-scan的基础上包含或去处class

@Configuration@ComponentScan(basePackages = "org.example",        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),        excludeFilters = @Filter(Repository.class))public class AppConfig {    ...}复制代码

#{ expression }

实现BeanNameGenerator实现自定义bean命名

@Configuration@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)public class AppConfig {    ...}
复制代码

实现 ScopeMetadataResolver接口实现自定义Scope解析

@Configuration@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)public class AppConfig {    ...}
复制代码

@Profile

使用Profile时尽量不要使用重载,可能导致十分奇怪的问题

激活profile有一下方式

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();ctx.getEnvironment().setActiveProfiles("development");ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);ctx.refresh();-Dspring.profiles.active="profile1,profile2"复制代码

设置默认的profile

setDefaultProfiles() spring.profiles.default复制代码

转载地址:http://ikboa.baihongyu.com/

你可能感兴趣的文章
获取系统托盘图标的坐标及文本
查看>>
log4j Test
查看>>
HDU 1255 覆盖的面积(矩形面积交)
查看>>
Combinations
查看>>
SQL数据库无法附加,提示 MDF" 已压缩,但未驻留在只读数据库或文件组中。必须将此文件解压缩。...
查看>>
第二十一章流 3用cin输入
查看>>
在workflow中,无法为实例 ID“...”传递接口类型“...”上的事件“...” 问题的解决方法。...
查看>>
获取SQL数据库中的数据库名、所有表名、所有字段名、列描述
查看>>
Orchard 视频资料
查看>>
简述:预处理、编译、汇编、链接
查看>>
调试网页PAIP HTML的调试与分析工具
查看>>
路径工程OpenCV依赖文件路径自动添加方法
查看>>
玩转SSRS第七篇---报表订阅
查看>>
WinCE API
查看>>
POJ 3280 Cheapest Palindrome(DP 回文变形)
查看>>
oracle修改内存使用和性能调节,SGA
查看>>
SQL语言基础
查看>>
对事件处理的错误使用
查看>>
最大熵模型(二)朗格朗日函数
查看>>
深入了解setInterval方法
查看>>