通过 XML 配置,让 Spring 容器解析它去加载实例
<bean id="myFactoryBean" class="com.vnjohn.factorybean.MyFactoryBean"/> <bean id="mySmartFactoryBean" class="com.vnjohn.factorybean.MySmartFactoryBean"/>
启动测试类
public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("factoryBean.xml"); MyFactoryBean bean = (MyFactoryBean) ac.getBean( "&myFactoryBean"); System.out.println(bean); User bean1 = (User) ac.getBean("myFactoryBean"); System.out.println(bean1.getUsername()); User bean2 = (User) ac.getBean("myFactoryBean"); System.out.println(bean2.getUsername()); // 若 MyFactoryBean#isSingleton 方法返回 true,以下会返回 true,否则会返回 false System.out.println(bean1==bean2); } }
在微服务组件中 open-feign 引入了 FeignClientFactoryBean 客户端类,来实现自定义的一些操作流程~
FactoryBean、BeanFactory 这两者经常会用来进行对比,都是用来创建对象的,只是其用处、作用不同,如下:
- FactoryBean 接口:不需要遵守 Spring 生命周期顺序实例化类型,getObject—>返回对象、getObjectType—>返回对象类型、isSingleton—>判断是否属于单例;FactoryBean 一共创建了两个对象:第一个是实现 FactoryBean 接口的子类,第二个是通过 getObject 返回的类型,这两个都是交由给 Spring 进行管理的,但需要注意的是,
实现 FactoryBean 接口的对象是存放在一级缓存里,但是调用 getObject 方法的对象如果其【isSingleton】方法返回为 false,则每次进行获取时都要去加载一个新的对象,返回 true 是会存入到 FactoryBeanRegistrySupport#factoryBeanObjectCache 中的
,下次获取直接从缓存中取用即可. - BeanFactory 接口:必须要严格遵守 Spring 生命周期创建对象,从实例化—>初始化、invokeAwareMethod、BPP#before、BPP#after,此流程非常复杂且麻烦
InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor 接口实现了 BeanPostProcessor 接口,简称 BPP;一般的 BPP 都是在初始化前后进行调用的,此接口会提前在 createBean 方法内,判别它的实例化前/后
方法是否会提前进行 Bean 实例创建,通过 InstantiationAwareBeanPostProcessor
类可以提前生成代理对象,就不会再去执行 doCreateBean
方法
之前在介绍 AbstractApplicationContext#refresh
方法内一些核心的方法时,有提到 registerBeanPostProcessors
,它会提前将 Spring 容器中所有 BeanPostProcessor 接口实现类进行注册且创建好实例对象,随即在后续创建业务对象时就可以用到这些 BPP 配置类来进行额外的加载和配置工作了~
关于 InstantiationAwareBeanPostProcessor 方法提前实例化的核心逻辑在 createBean 方法内的 resolveBeforeInstantiation 方法进行实现的,流程图如下:
在 AbstractBeanFactory#addBeanPostProcessor
方法中会判别是否有自定义的 BPP 实现了 InstantiationAwareBeanPostProcessor 接口,若有的话设置 hasInstantiationAwareBeanPostProcessors=true
,后续对所有类型进行该 BPP 调用,若满足对应的类型,那么就提前完成实例化、初始化操作!
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) { Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null"); // 后添加的 BeanPostProcessor 会覆盖之前的,先删除,然后在添加 this.beanPostProcessors.remove(beanPostProcessor); // 此处是为了设置某些状态变量,这些状态变量会影响后续的执行流程,只需要判断是否是指定的类型,然后设置标志位即可 if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { // 该变量表示 Beanfactory 是否已注册过 InstantiationAwareBeanPostProcessor this.hasInstantiationAwareBeanPostProcessors = true; } if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) { // 该变量表示 beanFactory 是否已注册过 DestructionAwareBeanPostProcessor this.hasDestructionAwareBeanPostProcessors = true; } // 将 beanPostProcessor 添加到 beanPostProcessors 缓存中 this.beanPostProcessors.add(beanPostProcessor); }
先调用 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
方法完成实例化前操作,在这里可以完成赋值,基于动态代理实现;后调用 InstantiationAwareBeanPostProcessorAdapter#postProcessAfterInitialization
方法完成初始化后操作
在 postProcessBeforeInstantiation 方法执行期间其实就是提前生成该类的 CGLIB 子类代理对象返回,以下代码是创建代理对象的一般过程
Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(beanClass); enhancer.setCallback(new MyMethodInterceptor()); enhancer.create();
关于扩展此 BPP 类型的整个流程讲解
1、定义一个需要提前被 BPP 实现类实例化、初始化的类型
public class BeforeInstantiation { public void doSomething(){ System.out.println("invoke doSomething...."); } }
2、自定义实现 MethodInterceptor 接口,用于拦截调用的方法
public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("目标方法执行之前:" + method); Object o1 = methodProxy.invokeSuper(o, objects); System.out.println("目标方法执行之后:" + method); return o1; } }
3、自定义扩展实现 InstantiationAwareBeanPostProcessor 接口
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { /** * 初始化之前处理 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("beanName:" + beanName + "----执行postProcessBeforeInitialization方法"); return bean; } /** * 初始化之后处理 */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("beanName:" + beanName + "----执行postProcessAfterInitialization方法"); return bean; } /** * 实例化之前处理 */ @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { System.out.println("beanName:" + beanName + "----执行postProcessBeforeInstantiation方法"); if (beanClass == BeforeInstantiation.class) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(beanClass); enhancer.setCallback(new MyMethodInterceptor()); BeforeInstantiation beforeInstantiation = (BeforeInstantiation) enhancer.create(); System.out.println("创建代理对象:"+beforeInstantiation); return beforeInstantiation; } return null; } /** * 实例化之后处理 */ @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { System.out.println("beanName:" + beanName + "----执行postProcessAfterInstantiation方法"); return false; } /** * 使用注解时,属性注入处理方法 */ @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { System.out.println("beanName:" + beanName + "----执行postProcessProperties方法"); return pvs; } }
4、resolveBeforeInstantiation.xml 配置文件,让 Spring 容器能够解析到
<bean id="beforeInstantiation" class="com.vnjohn.resolveBeforeInstantiation.BeforeInstantiation"/> <bean id="myInstantiationAwareBeanPostProcessor" class="com.vnjohn.resolveBeforeInstantiation.MyInstantiationAwareBeanPostProcessor"/>
5、测试基类
public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("resolveBeforeInstantiation.xml"); BeforeInstantiation bean = ac.getBean(BeforeInstantiation.class); bean.doSomething(); } }
如此处理之后,BeforeInstantiation 这个 Bean 就会提前在 resolveBeforeInstantiation
方法之后就返回了,不会再继续往下面调用 doCreateBean 方法创建实例对象了!
在 Spring 生命周期中,只执行了实例化之前、初始化之后这两个方法,其他的未做任何处理工作
Supplier
由 Supplier 下函数式接口进行对象创建的操作,不会再去通过反射创建对象,它不局限于具体通过什么方法去创建对象实例,相比 FactoryBean 接口而言,它要求的是类必须实现其接口且必须通过 getObject 方法来返回具体的实例对象,其属于接口规范的范畴.
Supplier 属于 AbstractBeanDefinition 类下的一个属性值【Supplier<?> instanceSupplier】若该 BeanDefinition 有设置这个属性才会提前去创建,没有则不管,按照后续正常 Spring 生命周期加载实例的流程去进行获取
下面通过扩展 Spring 来演示如何实现通过这种方式进行 Bean 实例加载
1、普通类,待加载的对象
public class User { private String username; public User() { } public User(String username) { this.username = username; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String toString() { return "User{" + "username='" + username + '\'' + '}'; } }
2、函数式编程接口实现,定义一个返回对象的方法
public class CreateSupplier { public static User createUser(){ return new User("vnjohn"); } }
3、扩展实现 BFPP,主要为了修改某个对象的 BeanDefnition 信息,为它设置 Supplier 属性
// 在执行 BFPP 时会为 beanName 为 user 的 beanDefinition 添加回调方法,返回的是 user 对象 public class SupplierBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinition user = beanFactory.getBeanDefinition("user"); GenericBeanDefinition genericBeanDefinition = (GenericBeanDefinition) user; genericBeanDefinition.setInstanceSupplier(CreateSupplier::createUser); genericBeanDefinition.setBeanClass(User.class); } }
4、 supplier.xml 配置文件,让 Spring 容器能够解析到
<bean id="user" class="com.vnjohn.supplier.User"/> <bean class="com.vnjohn.supplier.SupplierBeanFactoryPostProcessor"/>
5、测试基类
public class TestSupplier { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("supplier.xml"); User bean = ac.getBean(User.class); System.out.println(bean.getUsername()); } }
FactoryMethod
当需要验证不同的重载方法中,参数只有一个时具体加载那个:通过权重值来取用对应的构造方法候选者创建实例
通过 factory-method
方式创建对象有两种方式:
- 通过静态工厂方法创建,要在 bean 标签中指定 factory-method、class 属性为对应的静态工厂 Bean
- 通过实例工厂方法创建,需要在 bean 标签中指定 factory-bean(实例工厂 Bean) 引用、factory- method(实际工厂中具体要调用的方法)
下面通过 Spring 来演示如何通过实例工厂方法、静态工厂方法这两种方式进行 Bean 实例加载
1、实例工厂、静态工厂要创建的对象
public class Person { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "id=" + id + ", name='" + name + '\'' + '}'; } }