1.IOC/DI
控制反转/依赖注入
控制反转:将创建对象的权利返给容器,让容器去控制这个对象的创建
依赖注入:在需要用到某些对象的时候,将该对象注入到需要的地方
注入方式:
- @authwired
- xml配置
- 构造器注入 (✔️)
- 静态工厂
- 实例工厂
选择 构造器注入的 原因:
1.提前避免循环依赖 2.确保依赖不为空
IOC:
重点总结:在spring内部的生成的bean的实例都是放在一个 concurrentHashmap里面的,初始化之后,会生成非lazy的单例的bean,需要的时候再取出来。
核心代码
bean的创建:
Spring如何注册一个bean
AbstractApplicationContext类里的refresh() 是bean的生命周期
prepareRefresh(); //初始化前的准备,检查系统配置的变量
obtainFreshBeanFactory(); //读取xml
prepareBeanFactory(beanFactory);//对bean进行功能填充 比如qualifier和autowired的注解
postProcessBeanFactory(beanFactory);//扩展点
invokeBeanFactoryPostProcessors(beanFactory);//激活beanfactory的处理器,这里也会进行注册beanDefinition
registerBeanPostProcessors(beanFactory);//注册拦截bean创建的bean的处理器
initMessageSource();//国际化处理 为上下文初始化message源
initApplicationEventMulticaster();//初始化应用消息广播器
onRefresh();//留给子类初始化其他bean
registerListeners();// 在所有注册的Bean中查找Listener Bean,注册到消息广播器中
finishBeanFactoryInitialization(beanFactory);// 初始化剩下的单实例
finishRefresh();// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent进行通知
其中重要的2个阶段:
一.注册beanDefinition阶段 —— 在 invokeBeanFactoryPostProcessors()里
1.BeanDefinition接口 里面有一个bean基本上所有的信息,作用域,注解,方法,类名等等
2.已经注册成功的bean会放在一个map里 beanDefinitionMap
这个map是一个ConcurrentHashMap
<String,BeanDeifinition>
3.判断是否放进去了,没有的话,先判断是否第一次开始bean的创建
不是第一次创建则对map上锁,然后把当前beanDefinition放入beanDefinitionMap里面去。
这个时候并没有生成bean的实例
生成beanDefinition之后,
二. 初始化单例 的bean ——finishBeanFactoryInitialization
在doCreateBean里是正式实例化一个bean,单例则是获取。
调用AbstractAutowireCapableBeanFactory.createBeanInstance方法
实例化的时候其实是用了BeanUtils.instantiateClass()里下面这段代码的反射的方式,【但是默认使用Cglib生成代理生成】
ctor.newInstance(args))—— 通过反射获取构造函数去实例化一个类
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
然后把这个不完整的bean包装在BeanWrapper 里 往上抛出。
在回到doCreateBean里的populateBean()方法里。通过beanDefinition给这个bean设置属性包括【依赖】。通过注解注入的依赖和xml的依赖分别对应不同的地方实现。比如我们加了@autowired 那么就会在
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
这里去找到相关依赖的bean给放进bw.wrappedInstance里面。这个wrapperdInstance就是对应的我们上一层的bean的地址。
实例化成功之后就放入singletonObjects 里。
我们通过getBean或者注解拿到bean的时候,如果当前bean是单例且不是 lazy的 那么就是从singletonObjects
这个currentHashMap一个取出来的过程,如果是lazy则会在取用的时候去实例化bean
bean的获取
1.ApplicationContextAware的applicationContext.getBean()(✔️)
2.通过Spring提供的ContextLoader
3.通过Spring提供的工具类获取ApplicationContext对象
ps:关于 ApplicaionContext:
实现ApplicationContextAware接口,就可以使用ApplicationContext
ApplicationContext有哪些功能:
1.applicationContext继承了beanFactory,可以得到beanFactory的功能
2.可以发布事件/消息发布
3.提供加载指定资源的方法
AOP
面向切面编程
可以理解成在所有方法的里面加了一个过滤器 ,这个部分和业务逻辑分开,单独实现,可以作用于很多方法里。
spring代理生成方式:
1.JDK动态代理
java动态代理是利用【反射】机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
2.Cglib动态代理
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成【子类】来处理。
- 1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
如何强制使用CGLIB实现AOP?
(1)添加CGLIB库,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
spring 实现AOP四种方式
Spring提供了4种实现AOP的方式:
1.经典的基于代理的AOP
实现接口 MethodBeforeAdvice, AfterReturningAdvice + 配置里面加入aop的代理配置
2.@AspectJ注解驱动的切面
3.纯POJO切面
其实就是纯粹通过<aop:config>标签配置
4.注入式AspectJ切面
springMVC
spring的一个子模块
spring如何实现远程调用:
1.(主)httpInvoker - Spring httpInvoker使用标准java序列化机制,通过Http暴露业务服务。如果你的参数和返回值是比较复杂的,通过httpInvoker有巨大的优势。
即调用者与被调用者都必须是使用Spring框架的应用
2.RMI - RMI服务是典型的面向接口编程,只有在远程接口里定义的方法才会作为远程服务,远程方法的返回值和参数都必须实现Serializable接口,因为远程在网络上传输只能传输字节流,因此,要求参数、返回值都可以转换成字节流-即实现序列化。
原理:RMI的具体实现,依然是依赖于底层的Socket编程。RMI依赖于TCP/IP传输协议,服务器端skeleton建立ServerSocket监听请求,而客户端建立Socket请求边接。RMI实现网络传输的多线程、IO等底层细节。
网友评论