spring官方文档阅读笔记

前言

几个月前阅读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"这可能导致依赖

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>
复制代码

基于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

classInstantiating beans
nameNaming beans
scopeBean scopes
constructor argumentsDependency Injection
propertiesDependency Injection
autowiring modeAutowiring collaborators
lazy-initialization modeLazy-initialized beans
initialization methodInitialization callbacks
destruction methodDestruction callbacks

基于xml的实例化

  • 通过构造器实例化
<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
复制代码
  • 通过静态工厂方法实例化
<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
复制代码
  • 通过工厂方法实例化
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
复制代码

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...
}
复制代码
  • 构造器注入不同类型的多个参数
<beans>
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
    </bean>

    <bean id="bar" class="x.y.Bar"/>

    <bean id="baz" class="x.y.Baz"/>
</beans>
复制代码
  • 构造器注入多个不同简单类型
<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>
复制代码
  • 构造器注入时指定顺序
<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>
复制代码
  • 构造器注入时指定名称(需要在编译时开启debug)
<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>
复制代码
  • 作为前者的替代,使用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 AOP interceptors in a ProxyFactoryBean bean definition. Using  elements when you specify the interceptor names prevents you from misspelling an interceptor id. *

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>
复制代码

作用和下面相同

<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>
复制代码

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

容器支持

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>
复制代码

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

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

继承时集合的融合和覆盖


<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">administrator@example.com</prop>
                <prop key="support">support@example.com</prop>
            </props>
        </property>
    </bean>
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <!-- the merge is specified on the child collection definition -->
            <props merge="true">
                <prop key="sales">sales@example.com</prop>
                <prop key="support">support@example.co.uk</prop>
            </props>
        </property>
    </bean>
<beans>
复制代码

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

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

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

exampleBean.setEmail("");
复制代码
<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

exampleBean.setEmail(null);
复制代码

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

<bean id="foo" class="foo.Bar">
    <property name="fred.bob.sammy" value="123" />
</bean>
复制代码

depends on

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


<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
复制代码

对于多个依赖


<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
复制代码

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

lazy-initializad

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


<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>
复制代码

控制全局的初始化策略


<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>
复制代码

基于 xml的 autowired

不推荐 限制太多

方法注入

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

有三个解决方案

  1. 通过实现 ApplicationContextAware 接口获取ApplicationContext,再用来实现自定义功能

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import 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();
}

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>
复制代码
  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时可以使用以下方法


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean -->
        <aop:scoped-proxy/>
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <bean id="userService" class="com.foo.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>
复制代码

上面的例子中,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 实现参数值替换

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:com/foo/jdbc.properties"/>
    </bean>
    
    <bean id="dataSource" destroy-method="close"
            class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    复制代码
  2. PropertyPlaceholderConfigurer 实现类名替换


<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <value>classpath:com/foo/strategy.properties</value>
    </property>
    <property name="properties">
        <value>custom.strategy.class=com.foo.DefaultStrategy</value>
    </property>
</bean>

<bean id="serviceStrategy" class="${custom.strategy.class}"/>
复制代码
  1. PropertyOverrideConfigurer 覆盖默认值

<context:property-override location="classpath:override.properties"/>
复制代码

FactoryBean 获取制造bean的bean

FactoryBean有三个方法

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

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

lifeCycle

afterPropertiesSet和PostConstruct

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


<!--两个作用相同,约定的力量-->
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
复制代码

//这两个作用也相同,但是更推荐前者
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容器销毁前会调用该方法


<!--两者作用相同-->
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
复制代码

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为


<context:component-scan base-package="org.example"/>

复制代码

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 {
    ...
}

<beans>
    <context:component-scan base-package="org.example"
        name-generator="org.example.MyNameGenerator" />
</beans>
复制代码

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


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

<beans>
    <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>
复制代码

@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
复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值