结合面试经常问得有关spring得问题。
来进行深度剖析spring
什么是spring?
spring是一个开源框架,是为了解决企业应用开发得复杂性而创建得。
从简单性、可测试性和松耦合的角度来说,任何Java应用都可以从Spring中受益。
说到spring,就不得不说IOC和AOP
IOC:
从字面上理解就是控制反转,也就是将管理对象得权力交给spring。
其实可以将IOC看成一个map,其中beanname是key,bean实例对象是value。(可以使用这种方法实现一个简单的ioc容器)
//还可以讲讲IOC容器
//讲讲IOC的优点
//讲讲几种注入的方式
先从IOC容器开始讲解,spring中IOC容器实现:beanFactory和applicationContext
BeanFactory是容器基础设计,ApplicationContext增加一些扩展功能,应该说后者是前者的高级形态。
beanFactory:可以说说整个beanFactory的生命流程
1.beanFactory加载bean的配置文件,将读取的到的bean装成BeanDefinition对象
2.然后再将封装好的BeanDefinition对象注册到容器中
然后再根据beanfactory的生命流程继续往下说
applicationContext
spring依赖注入得三个方式
构造器注入
Setter方法注入
接口注入
//注解注入
spring中bean的作用域:
singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。
全局作用域与Servlet中的session作用域效果相同。
bean的生命周期:
定义
初始化
使用
销毁
1.bean容器在配置文件中找到bean的定义
2.创建bean的实例(通过java反射机制)
3.如果涉及到属性值,则使用set方法进行赋值
4.如果实现了beanNameAware接口,(则调用setBeanName()方法),传入beanName
6.如果实现了beanFactoryAware接口,(则调用SetBeanClassLoader())传入ClassLoader对象的实例。
7.如果实现了ApplicationContextAware接口,则传入ApplicationContext对象的实例
8.与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。
---------------------分界线---------------
9.如果有加载和BeanPostProcessor对象相关的则执行postProcessorBeforeInitalization()方法
--------------------分界线------------------
10.如果实现了InitalizingBean接口,则执行他的setproporiteidsAfter()
11.如果bean在配置文件中含有init-method属性,则执行对应的方法
--------------------分界线----------------------
12.如果有加载和BeanPostProcessor对象相关的则执行postProcessorAfterInitalization()方法
---------------------分界线--------------------
13.如果实现了DisposableBean接口,则要执行相应的destory()方法
14.如果bean在配置文件中定义中含有destory-method属性,则执行相应的方法
单例管理的对象:
当scope="singleton",或者为默认情况下,bean会在容器进启动的时候(容器初始化)进行初始化。但是可以使用lazy-init=“true”的方法进行延迟初始化,也就是当第一次请求该bean的时候才会进行初始化
在默认情况下,在spring读取xml文件的时候就会创建对象,在创建对象的时候先调用构造器,然后执行init-method的相应方法。当对象被销毁的时候会调用destory-method()方法
非单例管理的对象:
当scope="prototype"的时候,spring也会延迟初始化 bean,也就是说spring在读取xml文件的时候并不会立刻就创建bean对象,只有在请求该bean的时候才会进行初始化。在第一次请求每一个prototype的bean的时候,spring都会让构造器去创建对象然后调用init-mehod属性指定的方法。
同时对于destory-method对象的方法对于非单例管理的对象是没有用的,也就是说spring只负责创建bean对象的实例给我们,然后就不再管这个对象了。
//
有时候会经常被问到让你手写一个ioc容器
手撕IOC容器:
1.先来个简单的使用map来实现的
//有一个弊端就是:
classA里面有个属性是classB,在bean中写的话必须先写classB的bean,后写classA的bean
public class SimpleIOC {
private Map<String, Object> beanMap=new HashMap<>();
public SimpleIOC(String location) throws Exception{
loadBean(location);
}
public Object getBean(String name) {
Object bean = beanMap.get(name);
if (bean==null) {
throw new IllegalArgumentException("there is no bean with beanName:"+name);
}
return bean;
}
private void loadBean(String location) throws Exception {
//加载xml配置文件
InputStream inputStream=new FileInputStream(location);
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder=factory.newDocumentBuilder();
Document doc=docBuilder.parse(inputStream);
Element root = doc.getDocumentElement();
NodeList nodes=root.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
String id = ele.getAttribute("id");
String className = ele.getAttribute("class");
//加载beanclass
Class beanClass = null ;
beanClass=Class.forName(className);
//创建bean
Object bean=beanClass.newInstance();
// 遍历 <property> 标签
NodeList propertyNodes = ele.getElementsByTagName("property");
for (int j = 0; j < propertyNodes.getLength(); j++) {
Node propertyNode = propertyNodes.item(i);
if (propertyNode instanceof Element) {
Element pele = (Element) propertyNode;
String name = pele.getAttribute("name");
String value = pele.getAttribute("value");
// 利用反射将 bean 相关字段设为可以访问
Field declareField = bean.getClass().getDeclaredField(name);
declareField.setAccessible(true);
//可能是value,也可能是ref
if (null!=value && value.length() > 0) {
//将相关字段赋值
declareField.set(bean,value);
}else {
String ref=pele.getAttribute("ref");
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("ref config error");
}
// 将引用填充到相关字段中
declareField.set(bean, getBean(ref));
}
}
}
//将bean注册到bean容器中
beanMap.put(id, bean);
}
}
}
}
///测试类
public class SimpleIOCTest {
public static void main(String[] args) {
try {
SimpleIOC ioc = new SimpleIOC("IOC.xml");
Food food =(Food) ioc.getBean("food");
System.out.println("food的name:"+food.getName());
System.out.println("food的color:"+food.getApple().getColor());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
spring中bean得创建是典型得工程模式。其中BeanFactory是最顶层得接口,也是Spring IoC 容器的核心接口。但是BeanFactory只是对IOC得基本容器行为做了定义,并不关心bean是如何加载得。他只负责生产和管理bean得一个工厂,
spring中BeanFactory得源码:
public interface BeanFactory {
//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";
//根据bean的名字,获取在IOC容器中得到bean实例
Object getBean(String name) throws BeansException;
//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
Object getBean(String name, Class requiredType) throws BeansException;
//提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);
//根据bean名字得到bean实例,并同时判断这个bean是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//得到bean实例的Class类型
Class getType(String name) throws NoSuchBeanDefinitionException;
//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}