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{@Autowiredprivate 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实现, ClassPathXmlApplicationContext和FileSystemXmlApplicationContext
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
class | Instantiating beans |
---|---|
name | Naming beans |
scope | Bean scopes |
constructor arguments | Dependency Injection |
properties | Dependency Injection |
autowiring mode | Autowiring collaborators |
lazy-initialization mode | Lazy-initialized beans |
initialization method | Initialization callbacks |
destruction method | Destruction 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 MovieFinderprivate MovieFinder movieFinder;// a constructor so that the Spring container can inject a MovieFinderpublic 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注解
依赖解析过程
- ApplicationContext通过读取metadata完成创建和初始化
- 对于所有bean,它的依赖都是通过属性,构造器参数,静态工厂方法参数等,当bean真正被创建时(对于 scope为singleton的bean来说,立刻创建),这些依赖被注入
- 每个属性和构造器参数都是一个要被设置的值或是容器中其他bean的引用
- 原始类型的属性被会自动转化,例如将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,或相反
有三个解决方案
- 通过实现 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 CommandCommand command = createCommand();// set the state on the (hopefully brand new) Command instancecommand.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;}
}
复制代码
- 通过 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 interfaceCommand command = createCommand();// set the state on the (hopefully brand new) Command instancecommand.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>
复制代码
- 通过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();}@Lookupprotected abstract MyCommand createCommand();
}
复制代码
注意
- 该类不能是final,被覆盖的方法也不能是final
- 无法于工厂方法一起工作,对于@Configuration的@Bean也是无法工作的
- 单元测试需要自己去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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://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
- 在Spring调用afterPropertiesSet或initMethod之前, beanPostProcessor会被调用
- 在Spring调用initMethod之后,BeanPostProcessor会被调用
ApplicationContext会检查BeanDefinition,对实现了BeanPostProcessor的bean,将其注册为BeanPostProcessor
作为BeanPostProcessor的bean是不会被BeanPostProcessor处理的
BeanFactoryPostProcessor
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> 复制代码
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}"/>
复制代码
- PropertyOverrideConfigurer 覆盖默认值
<context:property-override location="classpath:override.properties"/>
复制代码
FactoryBean 获取制造bean的bean
FactoryBean有三个方法
- Object getObject()
- boolean isSingleton()
- 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 BeanPostProcessor
implementations
@Autowired和@Resource的区别
@Resource
annotation, 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 {@Resourceprivate CustomerPreferenceDao customerPreferenceDao;@Resourceprivate 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
复制代码
spring官方文档阅读笔记相关推荐
- Qt官方文档阅读笔记-对官方Star Delegate Example实例的解析
对应的博文为: 目录 Star Delegate Example StarDelegate Class Definition StarDelegate Class Implementation Sta ...
- Qt官方文档阅读笔记-QStyledItemDelegate Class描述
对应的原文为: 笔记如下: 简单描述: QStyledItemDelegate提供了展示和编辑item的功能,让这两种功能更有个性化.QStyledItemDelegate是所有Item View的默 ...
- Spring官方文档阅读(二)之Bean的简单理解与使用
1.3.Bean简介 Spring IoC容器管理一个或多个bean,这些bean是使用我们提供给容器的配置元数据创建的(例如,以XML<bean//>定义的形式 ),在容器本身内,这些b ...
- Flume官方文档阅读笔记及实际操作
欢迎来到Apache Flume Flume是一个分布式的,高可靠的,高可用的,高性能的海量日志数据采集.聚合和传输的系统.它是基于数据流的简单的灵活的架构.它具有高鲁棒性并且有着可调节的可靠的故障恢 ...
- Ti 官方文档阅读笔记
文章目录 参考资料 Optimizing TI mmWave Radar Configurations for FCC Certification Programming Chirp Paramete ...
- Javassist 官方文档 随手笔记
Javassist 官方文档 随手笔记 Javassist.CtClass Class search path Introspection and customization \$0, \$1, \$ ...
- ZooKeeper官方文档学习笔记03-程序员指南03
我的每一篇这种正经文章,都是我努力克制玩心的成果,我可太难了,和自己做斗争. ZooKeeper官方文档学习笔记04-程序员指南03 绑定 Java绑定 客户端配置参数 C绑定 陷阱: 常见问题及故障 ...
- Spring官方文档中文翻译
准备做个Spring官方文档全翻译专栏以下是大目录, 本翻译是基于Spring5 Core Technologies
- Spring 官方文档彩蛋
Spring 官方文档彩蛋 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句 ...
最新文章
- jvm六:主动使用(1.new一个对象, 2.反射)
- c#根据年份和月份获得本月最后一天
- HTML多选框滚动条,《HTM单选.doc
- [Leedcode][JAVA][第820题][字典树][Set]
- Spark Scalaa 几个常用的示例
- 如何重构千行“又臭又长”的类,IntelliJ IDEA 几分钟搞定!
- 微软提高 Microsoft 365 的漏洞奖励
- deeplearning.ai——TensorFlow指南
- linux 文件系统的简单操作
- Taobao 的 Linux 内核开源贡献
- eclipse php使用方法,Eclipse PHPEclipse 配置的具体步骤
- 【24】基于java的宠物医院管理系统
- 小米miui adb删除自带软件
- 两独立样本非参数检验的Mann-whitneyU检验
- 升级macOS Catalina 后辅助功能空白无法添加的问题
- python用谷歌内核制作浏览器_用cef Python打造自己的浏览器
- L1-040. 最佳情侣身高差
- ORA-04031: 无法分配 3840 字节的共享内存 (“shared pool“,“unknown object“,“sga heap(1,0)“,“kglsim object batch“)
- ASCII2ChineseSheet
- mt4 谐波_【干货分享】比肩波浪理论,被誉为最难技术形态的谐波形态是什么?...