Spirng IOC
依赖注入
- 根据名称注入
xml
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="foo" class="com.liyong.learn.inject.Foo">
<property name="name" value="liyong"></property>
<property name="age" value="22"></property>
</bean>
<bean id="foo1" class="com.liyong.learn.inject.Foo">
<property name="name" value="liyong1`"></property>
<property name="age" value="221"></property>
</bean>
<bean id="bar" class="com.liyong.learn.inject.Bar">
<property name="name" value="liyong"></property>
<property name="age" value="22"></property>
<property name="addr" value="cq"></property>
</bean>
<bean id="list" class="com.liyong.learn.inject.ListFoo">
<property name="list">
<!-- 注入List -->
<util:list>
<ref bean="foo"></ref>
<ref bean="foo1"></ref>
</util:list>
</property>
</bean>
</beans>
注入的时候也可以直接注入:
xml
<bean id="list" class="com.liyong.learn.inject.ListFoo" autowire="byType"></bean>
xml
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 导入资源 -->
<import resource="beans.xml"></import>
</beans>
java
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("inject.xml");
Bar bean = beanFactory.getBean(Bar.class);
System.out.println(bean);
ListFoo listFoo = beanFactory.getBean(ListFoo.class);
listFoo.getList().stream().forEach(System.out::println);
}
重点关注util 注入和byType注入
- 根据Bean类型注入
- 单个Bean对象
- 集合Bean对象
- 注入非Bean对象
- 注入容器内建Bean对象
- 注入类型
- 实时注入
- 延迟注入
java
public class ListFoo {
private Collection<Foo> list;
private BeanFactory beanFactory;
private ObjectFactory<ApplicationContext> objectFactory;
}
java
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("inject.xml");
Bar bean = beanFactory.getBean(Bar.class);
System.out.println(bean);
ListFoo listFoo = beanFactory.getBean(ListFoo.class);
listFoo.getList().stream().forEach(System.out::println);
System.out.println(listFoo.getBeanFactory());
// false 这里注入的bean 并不是beanFactory
System.out.println(beanFactory == listFoo.getBeanFactory());
// 输出为 DefaultListableBeanFactory
System.out.println(listFoo.getBeanFactory().getClass());
// 会错误 没有这个bean 说明我们依赖注入和依赖查找并不是同源的
beanFactory.getBean(BeanFactory.class);
System.out.println(beanFactory == listFoo.getObjectFactory().getObject());
}
SpirngIOC 依赖来源
- 自定义Bean(配置的bean xml 或者注解)
- 容器内建对象(例如Environment Bean 容器默认初始化)
- 容器内建依赖(比如上面例子中的BeanFactory就是内建依赖)
Spring配置元信息
- bean定义配置
- 基于XML
- 基于Properties文件
- 基于Java注解
- 基于JavaAPI
- IOC容器配置
- 基于XML文件
- 基于Java注解
- 基于JavaAPI
- 外部化配置
基于Java注解
BeanFactory还是ApplicationContext?
java
// 为什么这个表达式不会成立呢?因为他们是两个不同的对象 他们只是复用同一个接口
System.out.println(beanFactory == listFoo.getBeanFactory());
ApplicationContext是BeanFactory的一个超集,提供了更多企业级的功能。通过源码也可以看到我们下面的ApplicationContext是通过组合进来了一个BeanFactory。
BeanFactory 与 ApplicationContext的对比
官网推荐我们使用ApplicationContext,因为BeanFactory 有的能力BeanFactory都有
ApplicationContext 除了IOC容器角色还提供那些特性?
- 面向切面AOP
- 配置元信息
- 资源管理
- 事件
- 国际化
- 注解
- Environment抽象
使用BeanFactory 与 ApplicationContext
1 XML的方式使用
直接使用,但是这样的话就不会提供事件机制比如BeanPostProcessor
java
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions("inject.xml");
Bar bean = factory.getBean(Bar.class);
// 如果需要支持BeanPostProcessor 通过下面的方式进行设置
factory.addBeanPostProcessor(new MyBeanPostProcessor());
2 注解的方式使用
java
@Configuration
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Main.class);
context.refresh();
Bar bean = context.getBean(Bar.class);
System.out.println(bean);
}
@Bean
public Bar initBar() {
return new Bar();
}
}
SpringIOC容器生命周期
1 启动
java
public void refresh() throws BeansException, IllegalStateException {
// 加锁 因为不确定是单线程还是在多线程的环境中会创建 ApplicationContext
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 内建对象
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
2 停止
java
protected void doClose() {
// Check whether an actual close attempt is necessary...
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (logger.isDebugEnabled()) {
logger.debug("Closing " + this);
}
LiveBeansView.unregisterApplicationContext(this);
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
// Stop all Lifecycle beans, to avoid delays during individual destruction.
if (this.lifecycleProcessor != null) {
try {
this.lifecycleProcessor.onClose();
}
catch (Throwable ex) {
logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
}
}
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// Close the state of this context itself.
closeBeanFactory();
// Let subclasses do some final clean-up if they wish...
onClose();
// Reset local application listeners to pre-refresh state.
if (this.earlyApplicationListeners != null) {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Switch to inactive.
this.active.set(false);
}
}
BeanFactory 是容器 区别于 FactoryBean
FactoryBean 是 Spring 框架提供的一种特殊的工厂 Bean 接口,用于创建复杂的 Bean 对象。与普通的 Bean 不同,FactoryBean 实现类不直接返回实例化的 Bean 对象,而是通过工厂方法来创建 Bean 实例。这样可以为 Bean 的创建和初始化过程提供更多的灵活性和控制。
下面是一些 FactoryBean 的主要用途和特点:
- 定制化实例化逻辑:通过实现 FactoryBean 接口,你可以自定义创建 Bean 实例的逻辑,包括可能需要的条件判断、初始化过程和配置管理等。
- 延迟初始化:FactoryBean 可以实现延迟加载,即在需要时才实际创建对象,而不是在应用启动时就建。
- 装饰器模式:通过 FactoryBean,你可以实现装饰器模式,对原始 Bean 进行一些额外的处理或包装。
- 对象的复用:FactoryBean 可以控制对象的创建与返回,从而实现对象的重用,避免重复创建实例。
- 提供灵活性:FactoryBean 的存在使得 Spring 容器中的 Bean 创建更加灵活,可以根据需求定制不同的创建逻辑。
BeanDefinition 元信息
1 定义bean
java
// 1 通过 BeanDefinitionBuilder 构建
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Foo.class);
builder.addPropertyValue("name", "liyong");
builder.addPropertyValue("age", 11);
// 这里还可以对 beanDefinition 进行修改
BeanDefinition beanDefinition = builder.getBeanDefinition();
//2 通过 AbstractBeanDefinition 以及派生类
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
// 设置Bean类型
genericBeanDefinition.setBeanClass(Foo.class);
MutablePropertyValues values = new MutablePropertyValues();
values.addPropertyValue("name", "liyong");
values.addPropertyValue("age", 11);
genericBeanDefinition.setPropertyValues(values);
2 命名Bean
- 命名规范可以按照参数的命名规范(xml一般自定义名称比较多)
- DefaultBeanNameGenerator 是Spirng提供的一个API用于生成BeanName
- AnnotationBeanNameGenerator 基于注解扫描的BeanNameGenerator实现
3 别名Bean
xml
<!-- 将 foo -> 映射别名 aliasFoo -->
<alias name="foo" alias="aliasFoo"></alias>
java
BeanFactory beanFactory = new ClassPathXmlApplicationContext("beans.xml");
Foo aliasFoo = (Foo) beanFactory.getBean("aliasFoo");
Foo foo = (Foo) beanFactory.getBean("foo");
System.out.println(foo == aliasFoo);
注册BeanDefinition
1 xml配置
2 注解配置元信息
@Bean
@Component
@Import
通过注解我们的Bean,Spring容器不会重复注册同样的bean
java
@Import({Demo.Config.class})
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Demo.class);
context.refresh();
// 通过 命名生产器生成的 如果没有生成兜底策略为类名称
Map<String, Config> beansOfType = context.getBeansOfType(Config.class);
System.out.println(beansOfType);
Map<String, Bar> barMap = context.getBeansOfType(Bar.class);
System.out.println(barMap);
context.close();
}
@Component
static class Config {
@Bean
public Bar userBean() {
Bar bar = new Bar();
bar.setAddr("fdsa");
bar.setName("li");
bar.setAge(10);
return bar;
}
}
}
3 Java Api 配置元信息
- BeanDefinitionRegistry#registerBeanDefinition (用于命名方式)
- BeanDefinitionReaderUtils#registerWithGeneratedName (用于非命令方式)
- AnnotatedBeanDefinitionReader(配置类的方式)
java
public static void registerFooBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(Bar.class);
builder.addPropertyValue("name", "liyong");
builder.addPropertyValue("age", 20);
if (StringUtils.hasText(beanName)) {
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
} else {
BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), registry);
}
}
实例化Spring Bean
- Bean 实例化
- 常规方式
- 通过构造器(配置元信息:XML,Java注解和Java API)
- 通过静态工厂方法(配置元信息:XML 和 Java API)
- 通过Bean工厂方法(配置元信息:XML 和 Java API)
- 通过FactoryBean (配置元信息:XML,Java注解和Java API)
- 特殊方式
- 通过ServiceLoaderFactoryBean(配置元信息:XML,Java注解和Java API)
- 通过AutowireCapableBeanFactory#createBean(Class,boolean)
- 通过BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefiniton)
通过静态方法
- 常规方式
xml
<bean id="developer" class="com.liyong.learn.createbean.HighDeveloper" factory-method="createDeveloper"></bean>
在类里面定义静态的构造Bean的方法如下,省略get,set
java
public class HighDeveloper {
private String name;
private Integer salary;
public HighDeveloper() {
}
public HighDeveloper(String name, Integer salary) {
this.name = name;
this.salary = salary;
}
@Override
public String toString() {
return "HighDeveloper{" +
"name='" + name + '\'' +
", salary=" + salary +
'}';
}
public static HighDeveloper createDeveloper() {
HighDeveloper developer = new HighDeveloper();
developer.setName("ly");
developer.setSalary(15 * 15);
return developer;
}
}
通过Bean工厂方法
xml
<bean id="developer" class="com.liyong.learn.createbean.HighDeveloper" factory-method="createDeveloper"></bean>
<bean id="developerFactory" class="com.liyong.learn.createbean.DefaultDeveloperCreateFactory"></bean>
<bean id="developerByFc" class="com.liyong.learn.createbean.HighDeveloper" factory-bean="developerFactory" factory-method="createDeveloper"></bean>
java
BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean-create.xml");
HighDeveloper developer = beanFactory.getBean("developer", HighDeveloper.class);
HighDeveloper developerFc = beanFactory.getBean("developerByFc", HighDeveloper.class);
System.out.println(developer);
System.out.println(developerFc);
System.out.println(developer == developerFc);
通过FactoryBean
这里和上面有所区别,这里我们只需要定义工厂就行了,不需要先定义工厂,然后还要定义具体的bean来指定实现工厂。
xml
<bean id="developer-by-factory-bean" class="com.liyong.learn.createbean.DeveloperFactoryBean"></bean>
java
HighDeveloper developerFcBean = beanFactory.getBean("developer-by-factory-bean", HighDeveloper.class);
通过ServiceLoaderFactoryBean的方式
java
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean-create.xml");
// 基于xml配置的bean
ServiceLoader<DeveloperCreateFactory> bean = beanFactory.getBean(ServiceLoader.class);
Iterator<DeveloperCreateFactory> it = bean.iterator();
while (it.hasNext()) {
DeveloperCreateFactory next = it.next();
System.out.println(next.createDeveloper());
}
// ServiceLoader 加载的bean
ServiceLoader<DeveloperCreateFactory> load = ServiceLoader.load(DeveloperCreateFactory.class, Thread.currentThread().getContextClassLoader());
Iterator<DeveloperCreateFactory> iterator = load.iterator();
while (iterator.hasNext()) {
DeveloperCreateFactory next = iterator.next();
System.out.println(next.createDeveloper());
}
}
基于xml
java
<bean id="developerFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean">
<property name="serviceType" value="com.liyong.learn.createbean.DeveloperCreateFactory"></property>
</bean>
基于ServiceLoader也就是SPI机制,需要再这个目录下放置文件:
AutowireCapableBeanFactory
java
ApplicationContext context = new ClassPathXmlApplicationContext("bean-create.xml");
AutowireCapableBeanFactory autowireCapableBeanFactory = context.getAutowireCapableBeanFactory();
HighDeveloper developer = autowireCapableBeanFactory.createBean(HighDeveloper.class);
System.out.println(developer);
Bean初始化
- @PostConstruct
- 实现InitializingBean 接口的afterPropertiesSet()方法
- 自定义初始化方法
- xml 指定 inti-method
- 注解 @Bean(initMethod = 'init')
- Java API AbstractBeanDefinition#setInitMethodName(String)
在bean中指定init方法,在创建bean的时候回去调用这个初始化方法
java
@PostConstruct
public void init() {
System.out.println("init ing");
}
@Bean 指定initMethod
java
@Bean(initMethod = "initMethod")
public HighDeveloper highDeveloper() {
return new HighDeveloper();
}
// 在类中添加方法
public void initMethod() {
System.out.println("@Bean inti ing");
}
通过InitializingBean 的实现
java
public class HighDeveloper implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean init ing");
}
三者的顺序:PostConstruct -> InitializingBean -> @Bean
通过AbstractBeanDefinition的方式
java
AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext();
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(HighDeveloper.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanDefinition.setInitMethodName("initMethod");
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, beanFactory);
beanFactory.refresh();
HighDeveloper developer = beanFactory.getBean(HighDeveloper.class);
System.out.println(developer);
延迟初始化SpringBean
-
xml配置 (Lazy-init="true")
-
Java 注解 @Lazy(true)
不使用延迟初始化
java
public static void main(String[] args) {
AnnotationConfigApplicationContext beanFactory = new AnnotationConfigApplicationContext();
beanFactory.register(Client.class);
beanFactory.refresh();
System.out.println("context finish");
HighDeveloper developer = beanFactory.getBean(HighDeveloper.class);
System.out.println(developer);
}
@Bean(initMethod = "initMethod")
@Lazy(value = false)
public HighDeveloper highDeveloper() {
return new HighDeveloper();
}
使用延迟初始化
java
@Lazy(value = true)
public HighDeveloper highDeveloper() {
return new HighDeveloper();
}
总结:对比上面我们可以发现,如果不使用延迟加载在上下文刷新的时候Bean的初始化已经完成了。延迟加载的话,在真正使用的时候才会进行初始化。
Bean的销毁阶段
1 通过标注 注解 @PreDestory
2 实现DisposableBean 接口的destory()方法
3 自定义销毁方法
- XML配置 bean destory='destory'
- Java注解 @Bean(destory='destory')
- Java API:AbstractBeanDefiniton#setDestoryMethodName()
java
public class Client implements DisposableBean {
public Client(){
}
private String name;
private Integer age;
public Client(String name, Integer age) {
this.age = age;
this.name = name;
}
@PreDestroy
public void preDestroy () {
System.out.println("PreDestroy ......");
}
@Override
public String toString() {
return "Client{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void beanDestroy() {
System.out.println("beanDestroy ....");
}
@Override
public void destroy() throws Exception {
System.out.println("disposableDestroy ...");
}
}
java
@Configuration
public class BeanDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(BeanDemo.class);
context.refresh();
Object demo = context.getBean("demo");
System.out.println(demo);
context.close();
}
@Bean(destroyMethod = "beanDestroy")
public Client demo() {
return new Client();
}
}
Bean 垃圾回收
1 关闭上下文
2 调用GC
3 覆盖Object finalize() 方法
java
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(BeanDemo.class);
context.refresh();
Object demo = context.getBean("demo");
System.out.println(demo);
context.close();
Thread.sleep(5000L);
System.gc();
Thread.sleep(5000L);
java
@Override
public void finalize() throws Throwable {
System.out.println("finalize");
}