标题中的咖啡罐指的是Spring容器,容器里装的固然就是被称做Bean的豆子。本文咱们会以一个最基本的例子来熟悉Spring的容器管理和扩展点。
阅读PDF版本java
首先咱们来聊聊这个问题,为何咱们要用Spring来管理对象(的生命周期和对象之间的关系)而不是本身new一个对象呢?你们可能会回答是方便,为了解耦。我我的以为除了这两个缘由以外,还有就是给予了咱们更多可能性。若是咱们以容器为依托来管理全部的框架、业务对象,那么不只仅咱们能够无侵入调整对象的关系,还有可能无侵入随时调整对象的属性甚至悄悄进行对象的替换。这就给了咱们无限多的可能性,大大方便了框架的开发者在程序背后实现一些扩展。不只仅Spring Core自己以及Spring Boot大量依赖Spring这套容器体系,一些外部框架也由于这个缘由能够和Spring进行无缝整合。
Spring能够有三种方式来配置Bean,分别是最先期的XML方式、后来的注解方式以及如今最流行的Java代码配置方式。spring
在前文parent模块(空的一个SpringBoot应用程序)的基础上,咱们先来建立一个beans模块:apache
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>me.josephzhu</groupId> <artifactId>spring101-beans</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring101-beans</name> <description></description> <parent> <groupId>me.josephzhu</groupId> <artifactId>spring101</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
而后来建立咱们的豆子:c#
package me.josephzhu.spring101beans; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Component public class MyService implements InitializingBean, DisposableBean { public int increaseCounter() { this.counter++; return counter; } public int getCounter() { return counter; } public void setCounter(int counter) { this.counter = counter; } private int counter=0; public MyService(){ counter++; System.out.println(this + "#constructor:" + counter); } public String hello(){ return this + "#hello:" + counter; } @PreDestroy public void preDestroy() { System.out.println(this + "#preDestroy:" + counter); } @Override public void afterPropertiesSet() { counter++; System.out.println(this + "#afterPropertiesSet:" + counter); } @PostConstruct public void postConstruct(){ counter++; System.out.println(this + "#postConstruct:" + counter); } @Override public void destroy() { System.out.println(this + "#destroy:" + counter); } }
这里能够看到,咱们的服务中有一个counter字段,默认是0。这个类咱们实现了InitializingBean接口和DisposableBean接口,同时还建立了两个方法分别加上了@PostConstruct和@PreDestroy注解。这两套实现方式均可以在对象的额外初始化功能和释放功能,注解的实现不依赖Spring的接口,侵入性弱一点。
接下去,咱们建立一个Main类来测试一下:app
package me.josephzhu.spring101beans; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import javax.annotation.Resource; @SpringBootApplication public class Spring101BeansApplication implements CommandLineRunner { @Autowired private ApplicationContext applicationContext; @Resource private MyService helloService; @Autowired private MyService service; public static void main(String[] args) { SpringApplication.run(Spring101BeansApplication.class, args); } @Override public void run(String... args) throws Exception { System.out.println("===================="); applicationContext.getBeansOfType(MyService.class).forEach((name, service)->{ System.out.println(name + ":" + service); }); System.out.println("===================="); System.out.println(helloService.hello()); System.out.println(service.hello()); } }
ApplicationContext直接注入便可,不必定须要用ApplicationContextAware方式来获取。执行程序后能够看到输出以下:框架
me.josephzhu.spring101beans.MyService@7fb4f2a9#constructor:1 me.josephzhu.spring101beans.MyService@7fb4f2a9#postConstruct:2 me.josephzhu.spring101beans.MyService@7fb4f2a9#afterPropertiesSet:3 ==================== myService:me.josephzhu.spring101beans.MyService@7fb4f2a9 ==================== me.josephzhu.spring101beans.MyService@7fb4f2a9#hello:3 me.josephzhu.spring101beans.MyService@7fb4f2a9#hello:3 me.josephzhu.spring101beans.MyService@7fb4f2a9#preDestroy:3 me.josephzhu.spring101beans.MyService@7fb4f2a9#destroy:3
这里咱们使用@Resource注解和@Autowired注解分别引用了两次对象,能够看到因为Bean默认配置为singleton单例,因此容器中MyService类型的对象只有一份,代码输出也能够证实这点。此外,咱们也经过输出看到了构造方法以及两套Bean回调的次序是:maven
从刚才的输出中能够看到,在刚才的例子中,咱们为Bean打上了@Component注解,容器为咱们建立了名为myService的MyService类型的Bean。如今咱们再来用Java代码方式来建立相同类型的Bean,建立以下的文件:ide
package me.josephzhu.spring101beans; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration public class ApplicationConfig { @Bean(initMethod = "init") public MyService helloService(){ MyService myService = new MyService(); myService.increaseCounter(); return myService; } }
这里能够看到在定义Bean的时候咱们关联了一个initMethod,所以咱们须要修改Bean加上这个方法:spring-boot
public void init() { counter++; System.out.println(this + "#init:" + counter); }
如今咱们运行代码看看结果,获得了以下错误:post
Field service in me.josephzhu.spring101beans.Spring101BeansApplication required a single bean, but 2 were found: - myService: defined in file [/Users/zyhome/IdeaProjects/spring101/spring101-beans/target/classes/me/josephzhu/spring101beans/MyService.class] - helloService: defined by method 'helloService' in class path resource [me/josephzhu/spring101beans/ApplicationConfig.class]
出现错误的缘由是@Autowired了一个MyService,@Resource注解由于使用Bean的名称来查找Bean,因此并不会出错,而@Autowired由于根据Bean的类型来查抄Bean找到了两个匹配全部出错了,解决方式很简单,咱们在多个Bean里选一个做为主Bean。咱们修改一下MyService加上注解:
@Component @Primary public class MyService implements InitializingBean, DisposableBean
这样,咱们的@Resource根据名字匹配到的是咱们@Configuration出来的Bean,而@Autowired根据类型+Primary匹配到了@Component注解定义的Bean,从新运行代码来看看是否是这样:
me.josephzhu.spring101beans.MyService@6cd24612#constructor:1 me.josephzhu.spring101beans.MyService@6cd24612#postConstruct:3 me.josephzhu.spring101beans.MyService@6cd24612#afterPropertiesSet:4 me.josephzhu.spring101beans.MyService@6cd24612#init:5 me.josephzhu.spring101beans.MyService@7486b455#constructor:1 me.josephzhu.spring101beans.MyService@7486b455#postConstruct:2 me.josephzhu.spring101beans.MyService@7486b455#afterPropertiesSet:3 ==================== myService:me.josephzhu.spring101beans.MyService@7486b455 helloService:me.josephzhu.spring101beans.MyService@6cd24612 ==================== me.josephzhu.spring101beans.MyService@6cd24612#hello:5 me.josephzhu.spring101beans.MyService@7486b455#hello:3 me.josephzhu.spring101beans.MyService@7486b455#preDestroy:3 me.josephzhu.spring101beans.MyService@7486b455#destroy:3 me.josephzhu.spring101beans.MyService@6cd24612#preDestroy:5 me.josephzhu.spring101beans.MyService@6cd24612#destroy:5
从输出中咱们注意到几点:
@Autowired @Qualifier("helloService") private MyService service;
咱们来继续探索Spring容器提供给咱们的两个有关Bean的重要扩展点。
好,咱们如今来实现这两种类型的处理器,首先是用于修改Bean定义的处理器:
package me.josephzhu.spring101beans; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; @Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("helloService"); if (beanDefinition != null) { beanDefinition.setScope("prototype"); beanDefinition.getPropertyValues().add("counter", 10); } System.out.println("MyBeanFactoryPostProcessor"); } }
这里,咱们首先找到了咱们的helloService(Java代码配置的那个Bean),而后修改了它的属性和Scope(还记得吗,在以前的图中咱们能够看到,这两项都是Bean的定义,定义至关于类描述,实例固然就是类实例了)。
而后,咱们再来建立一个修改Bean实例的处理器:
package me.josephzhu.spring101beans; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyService) { System.out.println(bean + "#postProcessAfterInitialization:" + ((MyService)bean).increaseCounter()); } return bean; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyService) { System.out.println(bean + "#postProcessBeforeInitialization:" + ((MyService)bean).increaseCounter()); } return bean; } }
实现比较简单,在这个处理器的两个接口咱们都调用了一次增长计数器的操做。咱们运行代码来看一下这两个处理器执行的顺序是否符合刚才那个图的预期:
MyBeanFactoryPostProcessor me.josephzhu.spring101beans.MyService@41330d4f#constructor:1 me.josephzhu.spring101beans.MyService@41330d4f#postProcessBeforeInitialization:11 me.josephzhu.spring101beans.MyService@41330d4f#postConstruct:12 me.josephzhu.spring101beans.MyService@41330d4f#afterPropertiesSet:13 me.josephzhu.spring101beans.MyService@41330d4f#init:14 me.josephzhu.spring101beans.MyService@41330d4f#postProcessAfterInitialization:15 me.josephzhu.spring101beans.MyService@6f36c2f0#constructor:1 me.josephzhu.spring101beans.MyService@6f36c2f0#postProcessBeforeInitialization:11 me.josephzhu.spring101beans.MyService@6f36c2f0#postConstruct:12 me.josephzhu.spring101beans.MyService@6f36c2f0#afterPropertiesSet:13 me.josephzhu.spring101beans.MyService@6f36c2f0#init:14 me.josephzhu.spring101beans.MyService@6f36c2f0#postProcessAfterInitialization:15 me.josephzhu.spring101beans.MyService@3b35a229#constructor:1 me.josephzhu.spring101beans.MyService@3b35a229#postProcessBeforeInitialization:2 me.josephzhu.spring101beans.MyService@3b35a229#postConstruct:3 me.josephzhu.spring101beans.MyService@3b35a229#afterPropertiesSet:4 me.josephzhu.spring101beans.MyService@3b35a229#postProcessAfterInitialization:5 ==================== me.josephzhu.spring101beans.MyService@6692b6c6#constructor:1 me.josephzhu.spring101beans.MyService@6692b6c6#postProcessBeforeInitialization:11 me.josephzhu.spring101beans.MyService@6692b6c6#postConstruct:12 me.josephzhu.spring101beans.MyService@6692b6c6#afterPropertiesSet:13 me.josephzhu.spring101beans.MyService@6692b6c6#init:14 me.josephzhu.spring101beans.MyService@6692b6c6#postProcessAfterInitialization:15 myService:me.josephzhu.spring101beans.MyService@3b35a229 helloService:me.josephzhu.spring101beans.MyService@6692b6c6 ==================== me.josephzhu.spring101beans.MyService@41330d4f#hello:15 me.josephzhu.spring101beans.MyService@6f36c2f0#hello:15 me.josephzhu.spring101beans.MyService@3b35a229#preDestroy:5 me.josephzhu.spring101beans.MyService@3b35a229#destroy:5
这个输出结果有点长,第一行就输出了MyBeanFactoryPostProcessor这是预料之中,Bean定义的修改确定是最早发生的。咱们看下输出的规律,一、十一、十二、1三、1四、15出现了三次,之因此从1跳到了11是由于咱们的BeanFactoryPostProcessor修改了其中的counter属性的值为10。这说明了,咱们的helloService的初始化进行了三次:
这里的输出说明了几点:
最后,咱们能够修改BeanFactoryPostProcessor中的代码把prototype修改成singleton看看是否咱们的helloService这个Bean恢复为了单例:
MyBeanFactoryPostProcessor me.josephzhu.spring101beans.MyService@51891008#constructor:1 me.josephzhu.spring101beans.MyService@51891008#postProcessBeforeInitialization:11 me.josephzhu.spring101beans.MyService@51891008#postConstruct:12 me.josephzhu.spring101beans.MyService@51891008#afterPropertiesSet:13 me.josephzhu.spring101beans.MyService@51891008#init:14 me.josephzhu.spring101beans.MyService@51891008#postProcessAfterInitialization:15 me.josephzhu.spring101beans.MyService@49c90a9c#constructor:1 me.josephzhu.spring101beans.MyService@49c90a9c#postProcessBeforeInitialization:2 me.josephzhu.spring101beans.MyService@49c90a9c#postConstruct:3 me.josephzhu.spring101beans.MyService@49c90a9c#afterPropertiesSet:4 me.josephzhu.spring101beans.MyService@49c90a9c#postProcessAfterInitialization:5 ==================== myService:me.josephzhu.spring101beans.MyService@49c90a9c helloService:me.josephzhu.spring101beans.MyService@51891008 ==================== me.josephzhu.spring101beans.MyService@51891008#hello:15 me.josephzhu.spring101beans.MyService@51891008#hello:15 me.josephzhu.spring101beans.MyService@49c90a9c#preDestroy:5 me.josephzhu.spring101beans.MyService@49c90a9c#destroy:5 me.josephzhu.spring101beans.MyService@51891008#preDestroy:15 me.josephzhu.spring101beans.MyService@51891008#destroy:15
本次输出结果的hello()方法明显是同一个bean,结果中也没出现三次一、十一、十二、1三、1四、15。
本文以探索的形式讨论了下面的一些知识点: