Spirng IOC
依赖注入
- 根据名称注入
<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/utilhttp://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>
注入的时候也可以直接注入:
<bean id="list" class="com.liyong.learn.inject.ListFoo" autowire="byType"></bean>
<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="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/beanshttps://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>
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对象
- 注入类型
- 实时注入
- 延迟注入
public class ListFoo {private Collection<Foo> list;private BeanFactory beanFactory;private ObjectFactory<ApplicationContext> objectFactory;
}
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 并不是beanFactorySystem.out.println(beanFactory == listFoo.getBeanFactory());// 输出为 DefaultListableBeanFactorySystem.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?
// 为什么这个表达式不会成立呢?因为他们是两个不同的对象 他们只是复用同一个接口
System.out.println(beanFactory == listFoo.getBeanFactory());
ApplicationContext是BeanFactory的一个超集,提供了更多企业级的功能。通过源码也可以看到我们下面的ApplicationContext是通过组合进来了一个BeanFactory。
BeanFactory 与 ApplicationContext的对比
官网推荐我们使用ApplicationContext,因为BeanFactory 有的能力BeanFactory都有
ApplicationContext 除了IOC容器角色还提供那些特性?
- 面向切面AOP
- 配置元信息
- 资源管理
- 事件
- 国际化
- 注解
- Environment抽象
使用BeanFactory 与 ApplicationContext
1 XML的方式使用
直接使用,但是这样的话就不会提供事件机制比如BeanPostProcessor
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 注解的方式使用
@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);}@Beanpublic Bar initBar() {return new Bar();}
}
SpringIOC容器生命周期
1 启动
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 停止
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
// 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
<!-- 将 foo -> 映射别名 aliasFoo -->
<alias name="foo" alias="aliasFoo"></alias>
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
@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();}@Componentstatic class Config {@Beanpublic 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(配置类的方式)
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)
通过静态方法
- 常规方式
<bean id="developer" class="com.liyong.learn.createbean.HighDeveloper" factory-method="createDeveloper"></bean>
在类里面定义静态的构造Bean的方法如下,省略get,set
public class HighDeveloper {private String name;private Integer salary;public HighDeveloper() {}public HighDeveloper(String name, Integer salary) {this.name = name;this.salary = salary;}@Overridepublic 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工厂方法
<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>
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来指定实现工厂。
<bean id="developer-by-factory-bean" class="com.liyong.learn.createbean.DeveloperFactoryBean"></bean>
HighDeveloper developerFcBean = beanFactory.getBean("developer-by-factory-bean", HighDeveloper.class);
通过ServiceLoaderFactoryBean的方式
public static void main(String[] args) {BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean-create.xml");// 基于xml配置的beanServiceLoader<DeveloperCreateFactory> bean = beanFactory.getBean(ServiceLoader.class);Iterator<DeveloperCreateFactory> it = bean.iterator();while (it.hasNext()) {DeveloperCreateFactory next = it.next();System.out.println(next.createDeveloper());}// ServiceLoader 加载的beanServiceLoader<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
<bean id="developerFactoryServiceLoader" class="org.springframework.beans.factory.serviceloader.ServiceLoaderFactoryBean"><property name="serviceType" value="com.liyong.learn.createbean.DeveloperCreateFactory"></property>
</bean>
基于ServiceLoader也就是SPI机制,需要再这个目录下放置文件:
AutowireCapableBeanFactory
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的时候回去调用这个初始化方法
@PostConstruct
public void init() {System.out.println("init ing");
}
@Bean 指定initMethod
@Bean(initMethod = "initMethod")
public HighDeveloper highDeveloper() {return new HighDeveloper();
}
// 在类中添加方法
public void initMethod() {System.out.println("@Bean inti ing");}
通过InitializingBean 的实现
public class HighDeveloper implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("InitializingBean init ing");}
三者的顺序:PostConstruct -> InitializingBean -> @Bean
通过AbstractBeanDefinition的方式
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)
不使用延迟初始化
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();}
使用延迟初始化
@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()
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;}@PreDestroypublic void preDestroy () {System.out.println("PreDestroy ......");}@Overridepublic String toString() {return "Client{" +"name='" + name + '\'' +", age=" + age +'}';}public void beanDestroy() {System.out.println("beanDestroy ....");}@Overridepublic void destroy() throws Exception {System.out.println("disposableDestroy ...");}
}
@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() 方法
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);
@Override
public void finalize() throws Throwable {System.out.println("finalize");
}