01SpringIOC
- 格式:ppt
- 大小:194.00 KB
- 文档页数:30


SpringIOC容器启动流程源码解析(⼀)——容器概念详解及源码初探⽬录1.1 IOC容器到底是什么IOC和AOP是Spring框架的核⼼功能,⽽IOC⼜是AOP实现的基础,因⽽可以说IOC是整个Spring框架的基⽯。
那么什么是IOC?IOC即控制反转,通俗的说就是让Spring框架来帮助我们完成对象的依赖管理和⽣命周期控制等等⼯作。
从⾯向对象的⾓度来说,具有这种⾏为,完成这种⼯作的主体就可以形象的称之为IOC容器。
从代码⾓度来看,IOC容器不过是Spring中定义的具有IOC基本功能的⼀些类的统称,这些类都遵循⼀些共同的接⼝规范,所以我们可以说实现某些接⼝的具体的实现类就是IOC容器。
⽽IOC容器的启动流程,说⽩了就是创建并初始化⼀个该实现类的实例的过程,在这个过程中要进⾏诸如配置⽂件的加载解析,核⼼组件的注册,bean 实例的创建等⼀系列繁琐复杂的操作,因⽽整个过程显得相对漫长,逻辑也相对复杂。
1.2 BeanFactory和ApplicationContext的联系以及区别前⾯说到Spring中为容器类定义了⼀些接⼝规范,如下图所⽰具体⽽⾔,Spring中的容器类可以分为两⼤类。
⼀类是由BeanFactory接⼝定义的核⼼容器。
BeanFactory位于整个容器类体系结构的顶端,其基本实现类为DefaultListableBeanFactory。
之所以称其为核⼼容器,是因为该类容器实现IOC的核⼼功能:⽐如配置⽂件的加载解析,Bean依赖的注⼊以及⽣命周期的管理等。
BeanFactory作为Spring框架的基础设施,⾯向Spring框架本⾝,⼀般不会被⽤户直接使⽤。
另⼀类则是由ApplicationContext接⼝定义的容器,通常译为应⽤上下⽂,不过称其为应⽤容器可能更形象些。
它在BeanFactory提供的核⼼IOC功能之上作了扩展。
通常ApplicationContext的实现类内部都持有⼀个BeanFactory的实例,IOC容器的核⼼功能会交由它去完成。
spring的ioc和aop的原理Spring的IOC和AOP的原理。
Spring框架是一个轻量级的Java开发框架,它提供了很多方便的功能和特性,其中最核心的就是IOC(Inversion of Control)和AOP(Aspect Oriented Programming)。
这两个特性是Spring框架的灵魂所在,也是Spring框架如此受欢迎的重要原因之一。
首先,让我们来了解一下IOC的原理。
IOC是一种设计模式,它的核心思想是将对象的创建和对象之间的依赖关系的管理交给容器来完成,而不是由程序员手动管理。
在传统的开发模式中,程序员需要手动创建对象并管理对象之间的依赖关系,这样会导致代码的耦合度很高,不利于代码的维护和扩展。
而通过IOC容器,我们只需要在配置文件中声明对象和对象之间的依赖关系,容器会根据配置文件来创建对象并管理对象之间的依赖关系,从而实现了对象之间的解耦。
在Spring框架中,IOC容器负责管理所有的Bean对象,它通过BeanFactory和ApplicationContext两个接口来实现。
BeanFactory是IOC容器的基本接口,它提供了对Bean的配置、实例化和管理的基本功能。
而ApplicationContext接口则是BeanFactory的扩展,它提供了更多的功能,如国际化支持、事件传播、资源加载等。
Spring框架提供了多种实现IOC容器的方式,如XML配置、注解配置和Java配置等,开发者可以根据自己的需求来选择合适的方式来实现IOC容器。
接下来,让我们来了解一下AOP的原理。
AOP是一种编程范式,它的核心思想是将程序的业务逻辑和横切关注点(如日志、事务、安全等)分离开来,从而实现了业务逻辑和横切关注点的解耦。
在传统的开发模式中,横切关注点往往会散布在业务逻辑代码中,导致了代码的重复和混乱。
而通过AOP,我们可以将横切关注点抽象成切面,并通过切点和通知来实现对业务逻辑的增强,从而实现了业务逻辑和横切关注点的分离。
大家会比较奇怪,这个FileSystemXmlApplicationContext在什么地方定义了需要的BeanDefinitionReader呢?我们看看它的基类AbstractRefreshableApplicationContext:public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {XmlBeanDefinitionParser parser =(XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);return parser.registerBeanDefinitions(this, doc, resource);}// 这里定义Bean定义信息的解析器,这个解析器根据Spring的Bean定义信息的XML格式和规则对Bean定义进行解析,// 得到BeanDefinition向IOC容器进行注册BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();int countBefore = getBeanFactory().getBeanDefinitionCount();documentReader.registerBeanDefinitions(doc, createReaderContext(resource));return getBeanFactory().getBeanDefinitionCount() - countBefore;}这里看到的BeanDefinitionDocumentReader是一个根据Spring的Bean定义规则解析Bean定义的主要类之一://这个函数刻画了主要的解析过程public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");//这里得到Bean定义信息DOM结构的根节点Element root = doc.getDocumentElement();//这个BeanDefinitionParserDelegate是一个重要的辅助类,它实现了对具体Bean元素在Bean定义信息的解析BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);preProcessXml(root);//这是主要的解析过程parseBeanDefinitions(root, delegate);postProcessXml(root);}下面的函数对DOM形式的定义信息进行逐个的解析,protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {if (delegate.isDefaultNamespace(root.getNamespaceURI())) {NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element) node;String namespaceUri = ele.getNamespaceURI();if (delegate.isDefaultNamespace(namespaceUri)) {//这里对Spring定义的默认元素进行解析,包括Bean,Import等等parseDefaultElement(ele, delegate);}else {delegate.parseCustomElement(ele);}}}} else {delegate.parseCustomElement(root);}}}}if (containingBean == null) {checkNameUniqueness(beanName, aliases, ele);}//这里对Bean定义进行符合Spring定义规则的解析AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);if (beanDefinition != null) {if (!StringUtils.hasText(beanName)) {beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, getReaderContext().getReader().getBeanFactory(), (containingBean != null));if (logger.isDebugEnabled()) {logger.debug("Neither XML 'id' nor 'name' specified - " +"using generated bean name [" + beanName + "]");}}//返回的BeanDefintion会被注册到IOC容器里去String[] aliasesArray = StringUtils.toStringArray(aliases);return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}return null;}下面这个方法比较长,包含了对Spring定义Bean的各种属性值得解析处理,比如class,scope等等。
Spring的IOC实现原理什么是SpringIOCspring ioc指的是控制反转,IOC容器负责实例化、定位、配置应⽤程序中的对象及建⽴这些对象间的依赖。
交由Spring容器统⼀进⾏管理,从⽽实现松耦合“控制反转”,不是什么技术,⽽是⼀种设计思想。
在Java开发中,Ioc意味着将你设计好的对象交给容器控制,⽽不是传统的在你的对象内部直接控制。
如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些⽅⾯反转了”,那我们来深⼊分析⼀下:●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进⾏创建对象,是程序主动去创建依赖对象;⽽IoC是有专门⼀个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括⽐如⽂件等)。
●为何是反转,哪些⽅⾯反转了:有反转就有正转,传统应⽤程序是由我们⾃⼰在对象中主动控制去直接获取依赖对象,也就是正转;⽽反转则是由容器来帮忙创建及注⼊依赖对象;为何是反转?因为由容器帮我们查找及注⼊依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些⽅⾯反转了?依赖对象的获取被反转了。
IOC实现原理使⽤反射机制+XML技术理解了这些基本概念后,我们通过⼀个简单的⽰意图来简单描述⼀下整个流程,从⽰意图可以看出,当web容器启动的时候,spring的全局bean的管理器会去xml配置⽂件中扫描的包下⾯获取到所有的类,并根据你使⽤的注解,进⾏对应的封装,封装到全局的bean容器中进⾏管理,⼀旦容器初始化完毕,beanID以及bean实例化的类对象信息就全部存在了,现在我们需要在某个service⾥⾯调⽤另⼀个bean的某个⽅法的时候,我们只需要依赖注⼊进来另⼀个bean的Id即可,调⽤的时候,spring会去初始化完成的bean容器中获取即可,如果存在就把依赖的bean的类的实例化对象返回给你,你就可以调⽤依赖的bean的相关⽅法或属性等;下⾯通过实例代码来模拟⼀下整个IOC的原理和执⾏流程,1、demo结构如下,2、pom依赖⽂件,这⾥只需要spring的基本依赖即可,<properties><spring.version>5.1.2.RELEASE</spring.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.6.1</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.2</version></dependency></dependencies>3、⾸先我们⾃定义两个注解,我们知道在业务类中经常使⽤ @Service来标记这个类是bean管理的类,⽽@Autowired或者@Resource⽤于bean之间相互依赖使⽤,// ⾃定义注解 service 注⼊bean容器@Target({ ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)public @interface SelfService {}//模拟@Autowired注解@Target({ TYPE, FIELD, METHOD })@Retention(RUNTIME)public @interface SelfAutowired {}4、模拟spring的bean容器类,想必使⽤过spring框架在进⾏整合测试的时候,都使⽤到下⾯这段代码,这段代码的作⽤其实就是模拟在spring启动加载的时候,通过这个类去初始化⼀个bean 的容器管理类,所有的bean的信息解析和保存都会在这个类⾥⾯进⾏,下⾯我们写⼀个这样的类来还原⼀下这个过程,SelfPathXmlApplicationContext app = new SelfPathXmlApplicationContext("com.congge.service.impl");1⾃定义spring的bean容器类,/**⾃定义bean管理器@author asus*/public class SelfPathXmlApplicationContext {private String packageName;// 封装所有的bean容器private ConcurrentHashMap<String, Object> beans = null;/*** 该类被创建出来的时候加载* @param packageName* @throws Exception*/public SelfPathXmlApplicationContext(String packageName) throws Exception {beans = new ConcurrentHashMap<String, Object>();this.packageName = packageName;initBeans();initEntryField();}/*** 初始化bean的实例对象的所有属性* @throws Exception*/private void initEntryField() throws Exception {// 1.遍历所有的bean容器对象for (Entry<String, Object> entry : beans.entrySet()) {// 2.判断属性上⾯是否有加注解Object bean = entry.getValue();attriAssign(bean);}}/*** 根据beanId获取bean名称* @param beanId* @return* @throws Exception*/public Object getBean(String beanId) throws Exception {if (StringUtils.isEmpty(beanId)) {throw new Exception("beanId参数不能为空");}// 1.从spring容器获取beanObject object = beans.get(beanId);// attriAssign(object);return object;}/*** 获取扫描包下⾯的所有bean*/private void initBeans() throws Exception {// 1.使⽤java的反射机制扫包,[获取当前包下所有的类]List<Class<?>> classes = ClassParseUtil.getClasses(packageName);// 2、判断类上⾯是否存在注⼊的bean的注解ConcurrentHashMap<String, Object> classExisAnnotation = findClassExisAnnotation(classes);if (classExisAnnotation == null || classExisAnnotation.isEmpty()) {throw new Exception("该包下没有任何类加上注解");}}/*** 判断类上是否存在注⼊⾃定义的bean的注解* @param classes* @return* @throws Exception*/public ConcurrentHashMap<String, Object> findClassExisAnnotation(List<Class<?>> classes) throws Exception { for (Class<?> classInfo : classes) {// 判断类上是否有注解 [获取⾃定义的service注解]SelfService annotation = classInfo.getAnnotation(SelfService.class);if (annotation != null) {// 获取当前类的类名String className = classInfo.getSimpleName();String beanId = toLowerCaseFirstOne(className);Object newInstance = newInstance(classInfo);beans.put(beanId, newInstance);}}return beans;}// ⾸字母转⼩写public static String toLowerCaseFirstOne(String s) {if (Character.isLowerCase(s.charAt(0)))return s;elsereturn (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();}/*** 通过class名称获取类的实例化对象* @param classInfo* @return* @throws ClassNotFoundException* @throws InstantiationException* @throws IllegalAccessException*/public Object newInstance(Class<?> classInfo)throws ClassNotFoundException, InstantiationException, IllegalAccessException {return classInfo.newInstance();}/*** 依赖注⼊注解原理* @param object* @throws Exception*/public void attriAssign(Object object) throws Exception {// 1.使⽤反射机制,获取当前类的所有属性Class<? extends Object> classInfo = object.getClass();Field[] declaredFields = classInfo.getDeclaredFields();// 2.判断当前类属性是否存在注解for (Field field : declaredFields) {SelfAutowired extResource = field.getAnnotation(SelfAutowired.class);if (extResource != null) {// 获取属性名称String beanId = field.getName();Object bean = getBean(beanId);if (bean != null) {// 3.默认使⽤属性名称,查找bean容器对象 1参数当前对象 2参数给属性赋值field.setAccessible(true); // 允许访问私有属性field.set(object, bean);}}}}}当这个类被初始化的时候,通过构造函数⾥⾯的两个⽅法,通过外部传⼊指定的包名,解析该包下⾯的所有类和相关注解,其实现原理主要是使⽤了反射,通过⼀个⼯具类获取到了所有的类的实例化集合后,我们对这个集合进⾏遍历,具体的执⾏⽅法可以看findClassExisAnnotation 这个⽅法,在findClassExisAnnotation这个⽅法⾥⾯,可以看到,我们使⽤⾃定义的注解去到这个实例类对象去匹配,如果匹配到了相应的注解,就把这个bean封装到全局的集合中,这⾥使⽤了concurrentHashMap进⾏封装,这⼀步完成之后,包含了⾃定义注解[@Service]的相关类对象就存在了内存集合中了,如果在web容器启动完毕之后,使⽤⾃⼰的bean的时候就可以直接通过getBean这个⽅法去容器⾥⾯直接获取就可以了,通过这个⽅法就可以拿到当前beanId对应的类的实例化对象,可以使⽤⾥⾯的相关⽅法,但是到这⾥并没有完事啊,假如在我们某个标注了@Service的类⾥⾯有下⾯这样的注解,即依赖了其他的某个bean,⽐如,在我们的userService类⾥⾯依赖了orderService,就形成了所谓的依赖注⼊,同样,依赖注⼊也是通过上⾯相同的⽅式,在initBean()⽅法结束之后⽴即执⾏,我们来看看这个⽅法,在initEntryField()这个⽅法⾥,要做的事情就是遍历上述初始化好的所有bean,然后再去每个bean的实例对象中解析并封装属性相关的对应信息,下⾯看⼀下initEntryField()这个⽅法,通过这个⽅法,就可以将某个bean中依赖的其他bean信息进⾏封装,/*** 依赖注⼊注解实现原理* @param object* @throws Exception*/public void attriAssign(Object object) throws Exception {// 1.使⽤反射机制,获取当前类的所有属性Class<? extends Object> classInfo = object.getClass();Field[] declaredFields = classInfo.getDeclaredFields();// 2.判断当前类属性是否存在注解for (Field field : declaredFields) {SelfAutowired extResource = field.getAnnotation(SelfAutowired.class);if (extResource != null) {// 获取属性名称String beanId = field.getName();Object bean = getBean(beanId);if (bean != null) {// 3.默认使⽤属性名称,查找bean容器对象 1参数当前对象 2参数给属性赋值field.setAccessible(true); // 允许访问私有属性field.set(object, bean);}}}}最后我们写⼀个测试类来验证⼀下,直接运⾏下⾯的main函数,public class Test1 {public static void main(String[] args) throws Exception {SelfPathXmlApplicationContext app = new SelfPathXmlApplicationContext("com.congge.service.impl"); UserServiceImpl userServiceImpl = (UserServiceImpl) app.getBean("userServiceImpl");String result = userServiceImpl.add();System.out.println("获取到了orderService的执⾏结果是 : " + result);System.out.println("当前的bean的实例对象是: " + userServiceImpl);}}可以看到我们⾃定义的bean容器已经⽣效了。