Spring 解读源码之实际应用
来源:互联网 发布:mac怎么删除桌面图标 编辑:程序博客网 时间:2024/05/08 02:29
在上篇文章中介绍了Spring 解析XML和Annotion标签,地址为http://blog.csdn.net/lgq2626/article/details/78722978,
这篇文章接着源码分析。
上篇文章介绍到了Spring源码的AbstractApplicationContext类的refresh方法,介绍了obtainFreshBeanFactory()调用方法,下面接着分析。
分析invokeBeanFactoryPostProcessors(beanFactory);这个调用方法,我们一直跟到PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors()方法,这个是在初始化Bean之前执行的方法,也就是说可以在进行DI之前修改bean的属性,这是一个应用,还可以动态注入bean。拓展的所有类都必须实现BeanFactoryPostProcessor类,或者实现这个类子类的接口,首先分析源码:
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. boolean reiterate = true; while (reiterate) { reiterate = false; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);//得到所有实现了BeanDefinitionRegistryPostProcessor接口的实现类 for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class); registryPostProcessors.add(pp); processedBeans.add(ppName); pp.postProcessBeanDefinitionRegistry(registry);//调用实现类方法 reiterate = true; } } }
以上的代码块就是源码里面实现了BeanDefinitionRegistryPostProcessor类的调用,下面看实际注入例子
@Componentpublic class TestBeanDefinitionRegister implements BeanDefinitionRegistryPostProcessor{ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("执行到这里"); TestModifyProperty t = beanFactory.getBean(TestModifyProperty.class); t.setI(1);//修改bean属性的值 beanFactory.initializeBean(t, "testModifyProperty");//初始化bean } public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { System.out.println("执行到这里111"); RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(); rootBeanDefinition.setBeanClass(Student.class); rootBeanDefinition.setLazyInit(false); registry.registerBeanDefinition("student", rootBeanDefinition);//此方法就是调用DefaultListableBeanFactory方法里的registerBeanDefinition()方法,在上篇文章注入Autowire标签的时候就是用到的此方法,同样是往list里面和set和map里面各加一个值 }}
源码里面的pp.postProcessBeanDefinitionRegistry(registry);就是调用方法,到这里 动态增加Bean的源码就分析完了,但是有个缺陷就是如果根据配置文件的内容去判断是否可以动态增加bean是做不到的,比如@value标签和@Autowire标签一样,都是在是在AutowiredAnnotationBeanPostProcessor类里面进行解析的,而AutowiredAnnotationBeanPostProcessor类实现的是MergedBeanDefinitionPostProcessor接口,MergedBeanDefinitionPostProcessor接口继承自BeanPostProcessor接口,BeanPostProcessor接口是在BeanFactoryPostProcessor接口之后执行的,所以就取不到咯,这里要特别注意,解决办法可以采用@ConditionalOnProperty标签,具体这里不细说了。
下面接着分析源码registerBeanPostProcessors(beanFactory);方法,这个方法的应用比较少,下面分析源码:
首先分析postProcessBeforeInitialization()方法的调用,postProcessBeforeInitialization方法是在AbstractAutowireCapableBeanFactory类下的doCreateBean()方法里的initializeBean(beanName, exposedObject, mbd);applyBeanPostProcessorsBeforeInitialization()方法里调用的,这个doCreateBean是创建Bean的方法,doCreateBean里面的populateBean(beanName, mbd, instanceWrapper);方法是依赖注入方法,以后分析,而initializeBean()是在依赖注入完毕之后执行的,
先简单分析下initializeBean的执行顺序:
Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);//执行before方法 } try { invokeInitMethods(beanName, wrappedBean, mbd);//执行init-method 方法,相当于配置文件中的init-method配置,此方法不可带参数 } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);//调用到了after方法 }
以上就是BeanFactoryPostProcessor接口和BeanPostProcessor接口的执行顺序,在这里总结一下,首先执行的是BeanFactoryPostProcessor会先执行,在这里可以拓展注入bean,修改bean的属性,然后才会执行BeanPostProcessor接口,BeanPostProcessor接口是在DI注入完毕之后先执行before方法,在执行init-method方法,最后执行after方法,这是执行顺序,还需要注意的是init-method方法里的方法名不可为afterPropertiesSet,因为AbstractAutowireCapableBeanFactory类下的init-method()方法在执行的之前有
if (initMethodName != null && !(isInitializingBean && “afterPropertiesSet”.equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
判断,如果为afterPropertiesSet的话是不执行的。
现在在回到AbstractApplicationContext类的refresh方法,在看registerListeners();方法,这里会用到一个设计模式:观察者模式,简单来说就是当我主类中的某一条数据需要变得时候会同时通知所有的实现了某个接口的某个方法,
先分析下源码:
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);//把当前的listener加入到ListenerRetriever类的applicationListenerBeans容器中 }
registerListeners这个方法在这里就干了这一件事情,那么,执行publishEvent()方法的时候就会for循环去推送当前的信息,源码如下:
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener《?》 listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(new Runnable() { @Override public void run() { invokeListener(listener, event); } }); } else { invokeListener(listener, event); } } }
for循环所有实现了ApplicationListener的接口,然后去调用onApplicationEvent方法。这就是ApplicationLinstener的源码,其实自己手写观察者模式也可以实现的,大概原理就是这样的,实际用处的话比如代码里面好多if else ,如果抽成多个类进行单独处理业务的话就可以定义一个接口,所有实现了这个接口的类都会调用,在类里单独写if,这么着代码就非常清晰了,还有做日志的时候,不同类型的日志收集到不同的file也可以这么做。
下面我贴上ApplicationEvent的demo:
public class TestApplicationEvent extends ApplicationEvent { private String modify; public String getModify() { return modify; } public void setModify(String modify) { this.modify = modify; } public TestApplicationEvent(Object source,String modify) { super(source); this.modify = modify; }}
@Componentpublic class TestApplicationEventProvider implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher = null; public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public void push(){ this.applicationEventPublisher.publishEvent(new TestApplicationEvent(this, "我是一次故意推送")); }}
@Componentpublic class TestApplicationListener implements ApplicationListener<TestApplicationEvent>{ public void onApplicationEvent(TestApplicationEvent event) { System.out.println("我已经接收到了推送内容,内容为:"+event.getModify()); }}
public static void main(String[] args) { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring.xml"); TestApplicationEventProvider test = classPathXmlApplicationContext.getBean(TestApplicationEventProvider.class); test.push(); }
下面分析InitializingBean这个接口,这个接口里面只有一个方法,也就是afterPropertiesSet()方法,具体源码分析:
boolean isInitializingBean = (bean instanceof InitializingBean);//判断是否是InitializingBean对象 if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isDebugEnabled()) { logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { ((InitializingBean) bean).afterPropertiesSet(); return null; } }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet();//调用方法 } }
这个afterPropertiesSet方法和init-method的方法在同一个类中写,如果同时存在的话先执行afterPropertiesSet方法在执行init-method方法,但是init-method的方法名不可为afterPropertiesSet,上文已经说过了,这两个的执行顺序都是在di和postProcessBeforeInitialization之后执行的,所以这个afterPropertiesSet的作用基本和init-method的作用类似,但是如果是用init-method方法执行的话,可以起到对Spring的解耦作用,用InitializingBean方法就做不到了。
以上就是对Spring类的一些拓展,写的不对的地方还望大神指出
- Spring 解读源码之实际应用
- spring源码解读之 JdbcTemplate源码
- spring学习笔记之DispatcherServlet源码解读
- spring学习笔记之AbstractController源码解读
- spring beans源码解读之--XmlBeanFactory
- spring beans源码解读之--总结篇
- Spring源码之SimpleAliasRegistry解读(一)
- spring beans源码解读之--总结篇
- Spring源码解读-Spring IoC容器初始化之资源定位
- Spring源码解读-Spring IoC容器初始化之资源解析
- Spring源码解读-Spring IoC容器初始化之资源注册
- Spring RMI源码解读
- spring IOC源码解读
- spring beans源码解读
- spring 源码解读
- spring源码解读
- Spring源码解读顺序
- spring源码解读感想
- curl 模拟请求(post为例)
- maven--架构理念
- 剑指offer 试题11~20
- http://blog.csdn.net/QCIWYY/article/details/52020958
- vue.js中的vue-cli中各个文件简单介绍
- Spring 解读源码之实际应用
- https://www.zhihu.com/question/19809484
- 谈谈自己
- zephyr_01_环境搭建
- 434 Number of Segments in a String
- Java | 枚举类型
- 【转载】Unity3D之LOD技术详解
- Java compiler level does not match java版本号不对的解决办法
- Eclipse项目导入到IDEA