Spirng-IOC零碎知识点

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");
}