Spring源码解析

这是我参与更文挑战的第24天

Spring源码解析

1. 源码编译(先看5编译错误总结,接着4,然后遇到错误对号入座)

idea:2018(后来换成2020)

Gradle:4.9

spring-framework5.0.x

(只针对5.0.x,其他版本不敢确定)

  1. 下载源码:github.com/spring-proj…

  2. 使用idea导入工程:

    在这里插入图片描述

1. 出错1:

在这里插入图片描述

注释掉使用原有的开发环境

在这里插入图片描述

2. 问题2:

缺少jar包

在这里插入图片描述

3. 问题3

即使在源码目录下运行gradlew :spring-oxm:compileTestJava gradlew :spring-core:compileTestJava这两个命令显示build成功,但是在idea导入的时候还存在问题:

在这里插入图片描述

在这里插入图片描述

后面将版本改掉,改为4.9版本,又出现这个getArchiveFile问题

在这里插入图片描述

最终百度了很多,有的说改这三个文件的配置,build.gradle,gradle.properties,settings.gradle但是都是对下载源的配置,并没有解决上述问题,

修改的如下:

settings.gradle

pluginManagement {
	repositories {
		maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }  //阿里云
		maven { url 'https://maven.aliyun.com/repository/spring-plugin'}   //
		maven { url "https://maven.aliyun.com/repository/public" }         //
		maven { url "https://maven.aliyun.com/repository/central" }        //
		gradlePluginPortal()
		maven { url 'https://repo.spring.io/plugins-release' }
	}
}

plugins {
	id "com.gradle.enterprise" version "3.2"
	id "io.spring.gradle-enterprise-conventions" version "0.0.2"
}
复制代码

build.gradle

resolutionStrategy {
	cacheChangingModulesFor 0, "seconds"
}
repositories {
	maven { url "https://maven.aliyun.com/repository/apache-snapshots" }
	maven { url "https://maven.aliyun.com/repository/spring" }
	maven { url "https://maven.aliyun.com/repository/google" }
	maven { url 'https://maven.aliyun.com/repository/central'}
	maven { url "https://maven.aliyun.com/repository/public" }
	mavenCentral()
	maven { url "https://repo.spring.io/libs-spring-framework-build" }
}
复制代码

gradle.properties

## 这里要对应自己的版本
version=5.3.2-SNAPSHOT
## 设置此参数主要是编译下载包会占用大量的内存,可能会内存溢出
org.gradle.jvmargs=-Xmx2048M
## 开启 Gradle 缓存
org.gradle.caching=true
## 开启并行编译
org.gradle.parallel=true
## 启用新的孵化模式
org.gradle.configureondemand=true
## 开启守护进程 通过开启守护进程,下一次构建的时候,将会连接这个守护进程进行构建,而不是重新fork一个gradle构建进程
org.gradle.daemon=true
复制代码

最后我在git的release中下载了5.0.19.zip,没有下载5.0.x版本的,然后直接解压导入,注释了编译的环境,即出错1中提到的,然后编译如下图:(也不知道编译成功没有)

在这里插入图片描述

这个编译过程我花了接近一个小时。

接着运行spring-core,报错如下:

在这里插入图片描述

解决:在idea的terminal运行一下命令:

gradle objenesisRepackJar
gradle cglibRepackJar
复制代码

再次运行编译报错如下:(视频讲解说先不管)

在这里插入图片描述

编译spring-aop

报错如下:(没事)

在这里插入图片描述

测试:

创建Gradle模块

类似与创建spring工程

在这里插入图片描述

配置依赖

gradle

dependencies {
    compile(project(":spring-context")) //添加依赖
}
复制代码

然后参考spring-test中的文件目录构建自己的测试项目

然后右键构建,如下图:

然后在类上添加注解的时候,发现注解报错,无法resolve,于是重建了一下工程,发现报了这样的错

在这里插入图片描述

是因为spring需要依赖aspectj这个包,所以,需要安装这个包

下载地址:aspectj-1.9.2.jar (eclipse.org)

然后安装:spring源码下载安装,导入idea以及编译报错问题详细解决过程_a704397849的博客-CSDN博客](blog.csdn.net/a704397849/…)

在这之前确保这两插件已经安装

  1. Spring AOP/@AspectJ
  2. AspectJ Support

为了自己方便,截取了博主的图片

在这里插入图片描述

然后build project,还报错,

在这里插入图片描述

这个错误可以先不用管,这个包的问题解决。但是在自己建的项目中,还是没有@Repository的注解。后面使用alt+enter键,选中了spring-context-main这个包,然后就行了,但是图没截取出来,后面复现也没成功。类似maven的添加依赖包。我截取了一张导入后的包,可以试试手动导入这个包

然后使用AnnotationConfigApplicationContext 变量ctx调用register爆红、报错,忘了截图了(整个人都要疯了)记得错是无法使用类的错误或者找不到spring-context下的什么包。
在这里插入图片描述

4. 重新编译

  1. 下载源码:还是上述地址

  2. 修改spring-framework-5.0.x\gradle\wrapper 下的gradle-wrapper.properties配置,使用本地的gradle

    distributionBase=GRADLE_USER_HOME
    distributionPath=wrapper/dists
    zipStoreBase=GRADLE_USER_HOME
    zipStorePath=wrapper/dists
    # 改成你本地的包,省的每次构建下载,速度太慢
    distributionUrl=file:///E:/My_File_Code/Source_Study/Spring-5-0-x/gradle-4.9-bin.zip
    #distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip
    
    复制代码
  3. 修改build.gradle

    repositories {
        maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
        maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
        gradlePluginPortal()
        maven { url "https://repo.spring.io/libs-spring-framework-build" }
        maven { url "https://repo.spring.io/milestone" }      
    
        # spring插件
        maven { url "https://repo.spring.io/plugins-release" }
    }
    复制代码
  4. 在cmd界面下进入源码包运行gradlew.bar报错

    在这里插入图片描述

    解决:

    在这里插入图片描述

  5. 再次在cmd中编译运行gradlew.bat,成功

    在这里插入图片描述

  6. 根据官方文档:import-into-idea.md操作(但是有的教程说可以不操作,直接导入)。我还是按照文档来

    # 预编译spring-oxm spring-core
    gradlew :spring-oxm:compileTestJava
    gradlew :spring-core:compileTestJava
    复制代码

一直报错,不是Gradle版本高,就是低于5.0,不知道在哪里可以更改gradle的版本。

在这里插入图片描述

后来突然在CSDN找到一段描述bbs.csdn.net/topics/3947…

在spring-beans.gradle中将后面三部分修改如下:

spring-beans.gradle

compileGroovy {
	sourceCompatibility = 1.8
	targetCompatibility = 1.8
	options.compilerArgs += "-Werror"
}

// This module also builds Kotlin code and the compileKotlin task naturally depends on
// compileJava. We need to redefine dependencies to break task cycles.
// compileKotlin.dependsOn(compileGroovy)
// 把这几个添加进去
def deps = compileGroovy.taskDependencies.immutableValues + compileGroovy.taskDependencies.mutableValues
compileGroovy.dependsOn = deps - "compileJava"
compileKotlin.dependsOn(compileGroovy)
compileKotlin.classpath += files(compileGroovy.destinationDir)
复制代码

然后在cmd中重新运行,我失败了7次,最后才成功,他是有的包无法下载,所以导致失败。有点儿耐心。
在这里插入图片描述

(或者试试:然后按照网上说的,直接导入idea,前面导入的过程已经有了,不再叙述。)

在这里插入图片描述

接着导入idea,至于配置前面也有讲到,就不叙述了。

然后就是等着咯。(可以LeetCode上刷刷题)
半个小时后报错:网络原因(建议早上6点多起来编译,感觉会快点儿,晚上还是不要编译了)看到那5、6分钟就编译好的,我无比绝望
在这里插入图片描述

因为网速的原因,我整整重新reload好多次,8-9个小时,人已疯。最终编译成功,但是在build-gradle中DependencyResolveDetails爆红,根据提示是因为gradle-api-4.9.jar没导入,然后将gradle-api-4.9.jar(就在gradle安装目录下,可以搜索包名)导入即可。project Structure-》library 点击添加,这个应该都会。导入后,重新reload,解决。

在这里插入图片描述

接下来开始编译spring-oxm模块

在这里插入图片描述

执行完了后,接着编译spring-context,spring-bean,方法和spring-oxm一样。这里没遇到问题

接着编译spring-aop,前面提到过,编译AOP需要aspecjt。怎么添加已经提到过,不再赘述。然后编译和前面步骤类似。也不再贴图。

(这里编译的方式是由两种,一种是上面介绍的,一种就是点开spring-aspecjt模块,src-》test-》java,然后右键java,点击run Test…..也可以编译)

五分钟后编译完成

在这里插入图片描述

添加模块测试:步骤不再叙述。这次添加依赖后,AnnotationConfigApplicationContext调用没有再出现无法访问包的情况。

代码块如下:

appconfig.java

@Configuration
@ComponentScan("com.anzhi")
public class Appconfig {
}
复制代码

IndexDao.java

@Repository
public class IndexDao {
	public void query(){
		System.out.println("querry");
	}
}
复制代码

TestSpring.java

public class TestSpring {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx =
				new AnnotationConfigApplicationContext(Appconfig.class);

		IndexDao indexDao = ctx.getBean(IndexDao.class);
		indexDao.query();
	}
}
复制代码

build.gradle

dependencies {
	//和maven一样添加依赖
    compile(project(":spring-context")) 
    testCompile group: 'junit', name: 'junit', version: '4.12'
}
复制代码

结果如下:

在这里插入图片描述

5. 编译错误总结

  1. 运行gradlew.bat,因为没有修改spring-beans的gradle配置导致错误,这个很明显,也很容易百度到,可以看出错1;(尽量不要包含中文路径或空格啥的,鬼知道会出什么错误)
  2. 根据文档预编译,导致的错误,会出现这样的情况:
    1. getArchiveFile无法找到,符号错误啥的, required spring-core或者bean什么的。但是奇怪的是我后面重新下载编译好后,过程中没有出现这两种情况,甚至都没有搜索到getArchiveFile这个方法。也是无语。不过可以避免(猜测),可能还是网络原因导致编译下载出问题报错,或者spring-beans的配置不单单是简单注释,可能还要加上我配置那几句(也是在网上无意看到的。)。
  3. spring-aspejct需要aspejct编译工具,确保IDEA的插件齐全(前面也有提到,补充几个Kotlin,Gradle),这里还要注意的是,尽量将idea升高一点儿,总感觉2018的插件存在问题,没验证过。我后来升级到2020版(好像插件都带着)。
  4. 然后像什么Annotation…找不到,符号错误等,前面弄好后,基本不会有问题。
    1. 还遇到问题,可以参考这几个网站试试
    2. IDEA+Gradle构建Spring5源码阅读环境教程(优化构建速度) – 知乎
    3. 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译
    4. spring源码系列(六)——番外篇如何编译spring的源码_java_lyvee的专栏-CSDN博客
    5. spring5.3.x源码阅读环境搭建 – 小黑电脑
    6. 记录编译spring-framework源码的几个坑
    7. 编译Spring – 简书
    8. Spring源码 Gradle编译Spring源码 – 但行好事-莫问前程 – 博客园
    9. Spring5源码阅读环境搭建-gradle构建编译_zhoong-CSDN博客
    10. spring5源码编译_chuyang0312的博客-CSDN博客
    11. spring学习-01编译spring5.0源码(亲测可用)_安静的小海豹的博客-CSDN博客
  5. 还有一种,去gitee找找别人编译好的用用,不一定非要自己编译。唉,搭建编译环境有什么好的,这辈子不可能再搭建编译环境了。

2. 模拟spring源码

新建maven项目

dao层

UserDao.java

package dao;

public interface UserDao {
    public void query();
}
复制代码

UserDaoImpl.java

package dao;

public class UserDaoImpl implements UserDao{
    @Override
    public void query() {
        System.out.println("service");
    }
}
复制代码

service层

UserService.java

package service;

public interface UserService {

    public void find();
}
复制代码

UserServiceImpl.java

package service;

import dao.UserDao;

public class UserServiceImpl implements UserService{

    UserDao userDao;


    @Override
    public void find() {
        System.out.println("service");
        userDao.query();

    }

}
复制代码

新建Test类测试

package test;

import service.UserService;
import service.UserServiceImpl;

public class Test {
    public static void main(String[] args) {
        UserService service = new UserServiceImpl();
        service.find();
    }
}
复制代码

发现空指针异常,这是因为userDao没有与service关联,query要依赖 dao包下的UserDao,因此出现空指针异常。

所以需要添加依赖,如果你是spring公司,你会怎么开发?

  1. 那些类需要依赖,如何告诉编译器这些类是被需要的?bean标签

  2. 在有了依赖后,如何将userdao这个类给到service呢(毕竟拥有了才能使用,只依赖不行啊)?

    1. 提供set/get方法
    2. 构造时提供
  3. 但是这种方法是由程序员自己实现,并没有依赖第三方。而依赖第三方实现的这种方式就称之为注入。set/构造,但是对于容器来说,没有那么智能,需要我们告诉它由那种方法注入。

  4. 在这之后又该怎么维护依赖关系呢?setter/structure

  5. 那么是如何体setter/structure。property标签

  6. 在添加property之后,最终需要通过第三方将这个类完整地给到service,那怎么给到呢?

    如下代码实现:

    package utils;
    
    import org.dom4j.Attribute;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import java.io.File;
    import java.io.UnsupportedEncodingException;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    
    public class BeanFactory {
    
    
        //使用map存储对象,只是做个简单模拟,更多的数据类型这里不做扩展
        Map map = new HashMap<String,Object>(); 
    
        public BeanFactory(String xml) throws Exception{
            paresXml(xml);
        }
    
        /*
         *
         * 解析xml文件
         * */
        private void paresXml(String xml) throws Exception{
    
            /*
             * 文件路径源
             * */
    
            String configPath = this.getClass().getResource("/").getPath()+"//"+xml;
            try {
                /*
                * 解决中文乱码问题
                * */
                configPath =  java.net.URLDecoder.decode(configPath,"utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            File file = new File(configPath);
            /*
             * dom4j工具类使用
             * */
            SAXReader reader = new SAXReader();
    
                Document document = reader.read(file);
                Element rootElement = document.getRootElement();
    
                /*
                 * 遍历每一个元素,并将其放入Map中
                 * */
                for(Iterator<Element> itFirlst = rootElement.elementIterator(); itFirlst.hasNext();){
                    /*
                    * 1. 实例化对象,这里实现对每一父标签下的子标签进行解析
                    * */
                    Element elementFirstChil = itFirlst.next();
                    Attribute attributeId = elementFirstChil.attribute("id");
                    String beanId = attributeId.getValue();
                    Attribute attributeClass = elementFirstChil.attribute("class");
                    String clazzName = attributeClass.getValue();
                    Class clazz = Class.forName(clazzName);;
                    Object object = clazz.newInstance();
    
    
                    /*
                    * 维护依赖关系
                    * 看这个对象有没有依赖,判断是否有property,或者判断类是否有属性
                    * 如果有注入
                    * */
    
                    for(Iterator<Element> itSecond = elementFirstChil.elementIterator();itSecond.hasNext();){
                        /*
                        * <bean id="service" class="service.UserServiceImpl">
                        * <!--setter-->
                        * <property ref="dao"></property>
                        * 得到ref的value,通过value得到对象(Map)
                        * 得到name值,然后根据值获取一个filed对象
                        * */
                        Element elementSecondChil = itSecond.next();
                        if(elementSecondChil.getName().equals("property")){
                            String refValue = elementSecondChil.attribute("ref").getValue();
                            Object injetObject = map.get(refValue);
                            String nameValue = elementSecondChil.attribute("ref").getValue();
                            Field field = clazz.getDeclaredField(nameValue);
                            field.setAccessible(true);
                            field.set(object,injetObject);
                        }
                    }
    
                    Field[] fields = clazz.getDeclaredFields();
                    for(Field field:fields){
    
                    }
                    map.put(beanId, object);
                }
            System.out.println(map);
    
        }
    
        public Object getBean(String name){
            return map.get(name);
        }
    
    }
    复制代码

    Test.java

    package test;
    
    import service.UserService;
    import utils.BeanFactory;
    
    public class Test {
        public static void main(String[] args) throws Exception{
    
            BeanFactory beanFactory = new BeanFactory("spring.xml");
    
            //UserService service = new UserServiceImpl();
            UserService service = (UserService) beanFactory.getBean("service");
    
            service.find();
        }
    }
    
    复制代码

    spring.xml配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans>
    
        <bean id="userDao" class="dao.UserDaoImpl"></bean>
        <bean id="service" class="service.UserServiceImpl">
            <!--setter-->
            <property ref="userDao"></property>
        </bean>
    </beans>
    复制代码

    同过type类型注入(提示报错)

    BeanFactory.java

    package utils;
    
    import org.dom4j.Attribute;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import java.io.File;
    import java.io.UnsupportedEncodingException;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    
    public class BeanFactory {
    
    
        Map<String,Object> map = new HashMap<String,Object>();
    
        public BeanFactory(String xml) throws SpringException{
            paresXml(xml);
        }
    
        /*
         *
         * 解析xml文件
         * */
        private void paresXml(String xml) throws SpringException{
    
            /*
             * 文件路径源
             * */
    
            String configPath = this.getClass().getResource("/").getPath()+"//"+xml;
            /*
            * 解决中文乱码问题
            * */
            try {
                configPath =  java.net.URLDecoder.decode(configPath,"utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
    
            File file = new File(configPath);
            /*
             * dom4j工具类使用
             * */
            SAXReader reader = new SAXReader();
    
            Document document = null;
            try {
                document = reader.read(file);
            } catch (DocumentException e) {
                e.printStackTrace();
            }
            Element rootElement = document.getRootElement();
            Attribute attribute = rootElement.attribute("default");
            boolean flag = false;
            if(attribute != null){
                flag = true;
            }
            /*
             * 遍历每一个元素,并将其放入Map中
             * */
            for(Iterator<Element> itFirlst = rootElement.elementIterator(); itFirlst.hasNext();){
                /*
                * 1. 实例化对象,这里实现对每一父标签下的子标签进行解析
                * */
                Element elementFirstChil = itFirlst.next();
                Attribute attributeId = elementFirstChil.attribute("id");
                String beanName = attributeId.getValue();
                Attribute attributeClass = elementFirstChil.attribute("class");
                String clazzName = attributeClass.getValue();
                Class clazz = null;
                try {
                    clazz = Class.forName(clazzName);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
    
    
                /*
                * 2. 维护依赖关系
                * 看这个对象有没有依赖,判断是否有property,或者判断类是否有属性
                * 如果有注入
                * */
                Object object = null;
                for(Iterator<Element> itSecond = elementFirstChil.elementIterator();itSecond.hasNext();){
                    /*
                    * <bean id="service" class="service.UserServiceImpl">
                    * <!--setter-->
                    * <property ref="dao"></property>
                    * 得到ref的value,通过value得到对象(Map)
                    * 得到name值,然后根据值获取一个filed对象
                    * */
                    Element elementSecondChil = itSecond.next();
                    if(elementSecondChil.getName().equals("property")){
                        //由于是setter,没有特殊的构造方法,所以只能直接使用
                        try {
                            object = clazz.newInstance();
                        } catch (Expection e) {
                            e.printStackTrace();
                        }
                        String refValue = elementSecondChil.attribute("ref").getValue();
                        Object injetObject = map.get(refValue);
                        String nameValue = elementSecondChil.attribute("ref").getValue();
                        System.out.println(nameValue);
                        Field field = null;
                        try {
                            field = clazz.getDeclaredField(nameValue);
                            field.setAccessible(true);
                            field.set(object,injetObject);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }else{
                        //证明有特殊构造方法注入
                        String refVlaue = elementSecondChil.attribute("ref").getValue();
                        Object injetObject= map.get(refVlaue) ;
                        Class injectObjectClazz = injetObject.getClass();
                        Constructor constructor = null;
                        try {
                            constructor = clazz.getConstructor(injectObjectClazz.getInterfaces()[0]);
                            object = constructor.newInstance(injetObject);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
    
                if(object==null){
                    if(flag){
                        if(attribute.getValue().equals("byType")){
                            //判断是否有依赖
                            Field[] declaredFields = clazz.getDeclaredFields();
                            for(Field field:declaredFields){
                                //得到属性的类型,比如String ass,那么这里的field.getType()=String.class;
                                Class inObjectClazz = field.getType();
                                /*
                                 * 由于是bytype,所以需要遍历map当中的所有对象
                                 * 判断对象类型是不是和这个inObjectClazz相同
                                 * */
                                int count= 0;
                                Object injectObjec = null;
                                for (String key : map.keySet()) {
                                    Class temp = map.get(key).getClass().getInterfaces()[0];
                                    if(temp.getName().equals(inObjectClazz.getName()) ){
                                        injectObjec = map.get(key);
                                        //记录找到一个,因为可能找到多个
                                        count++;
                                    }
                                }
                                //找到两个冲突,抛出异常
                                if(count>1){
                                    throw new SpringException("需要一个对象,但是找到两个对象");
                                }else{
                                    try {
                                        object = clazz.newInstance();
                                        field.setAccessible(true);
                                        field.set(object, injectObjec);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                }
    
                if(object==null){
                    //没有子标签
                    try {
                        object = clazz.newInstance();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
                map.put(beanName, object);
            }
            System.out.println(map);
    
        }
    
        public Object getBean(String name){
            return map.get(name);
        }
    
    }
    复制代码

小结:

  1. 遍历配置文件,将扫描到的类名通过反射放入Map集合中,即class.forname()实例化对象;
  2. 依赖注入:判断对象是否有属性,有则注入

3. 模拟annotation

BeanFactory和FactoryBean:

  1. 如果你的类实现了FactoryBean,那么spring容器中存在两个对象,一个交getObject(),返回的对象,还有一个是当前对象。

    通过name获取beanName。不使用name作为beanName有两个原因:name可能会以&字符开头,表明调用者想获取FactoryBean本身,而非FactoryBean实现类所创建的bean。

  2. 应用场景:

    当一个类拥有很多属性,而且依赖很多,甚至有些依赖第三方库的类,其中的属性多到爆炸,如果让你维护,你怎么去维护。比如拿mybatis来说

    在这里插入图片描述

    通过xml配置引入SqlSessionFactory,

    在这里插入图片描述

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="SqlSessionFactory" class="SqlSessionFactory">
        	<property></property>
            <property></property>
            ........
        </bean>
        
        
        <bean id="dao" ref="SqlSessionFactory"></bean>
        <!--即使你可以忍受这样的操作方法,但是mybatis是没有办法更改我们自己写的配置文件的呀,同样我们也不能修改它的配置文件-->
    </beans>
    复制代码

    于是有了SqlSessionFactoryBean,它实现了FactoryBean接口的方法,帮助我们完成了配置。

    可以简单模拟一下:

    package dao;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.ImportResource;
    
    @Configuration
    @ComponentScan("dao")
    @ImportResource("classpath:springapplication.xml")
    public class TempDaoFactoryBean {
    
        private String msg;
        private String msg2;
        private String msg3;
    
        public void testBean () {
            System.out.println("FactoryBean");
        }
    
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public String getMsg2() {
            return msg2;
        }
    
        public void setMsg2(String msg2) {
            this.msg2 = msg2;
        }
    
        public String getMsg3() {
            return msg3;
        }
    
        public void setMsg3(String msg3) {
            this.msg3 = msg3;
        }
    
        @Override
        public String toString() {
            return "TempDaoFactoryBean{" +
                    "msg='" + msg + '\'' +
                    ", msg2='" + msg2 + '\'' +
                    ", msg3='" + msg3 + '\'' +
                    '}';
        }
    }
    复制代码
    package dao;
    
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    
    /*@Component("daoFactoryBean")*/
    public class DaoFactoryBean implements FactoryBean{
        private String msg;
    
        public void testBean(){
            System.out.println("daoFactoryBean");
        }
    
        @Override
        public Object getObject() throws Exception {
            TempDaoFactoryBean temp = new TempDaoFactoryBean();
            String[] msgArray = msg.split(",");
            temp.setMsg(msgArray[0]);
            temp.setMsg2(msgArray[1]);
            temp.setMsg3(msgArray[2]);
    
            return temp;
        }
    
        @Override
        public Class<?> getObjectType() {
            return TempDaoFactoryBean.class;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }
    复制代码

    springapplication.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="TempDaoFactoryBean" class="dao.DaoFactoryBean" >
            <property name="msg" value="msg,msg2,msg3"></property>
        </bean>
    </beans>
    复制代码

小结:有上述两个练习代码可以得出:

BeanFactory不归属Spring容器,可以创造出一个bean对象,而FactoryBean本身就是一个Bean对象,归属于spring管理,而这个bean对象又可以通过getObject再创造出一个bean出来,这也就验证了前面我们想要从spring取得cc,但是实际取得的是&cc,就是因为getObject本身就是一个bean对象。

1. 初始化 spring 的环境有几种方法

  1. xml:ClassPathXmlApplicationContext (最终完成类的扫描,可以完成单独bean的注册,即声明和注册)

  2. annotation:必须要依赖与1或者2,才能完成对注解扫描,不然spring是不认识的

  3. javaconfig:AnnotationConfigApplicationContext (同样完成类的扫描,类的定义,但是没有类的注册)

    所谓的初始化spring环境——-》就是将程序员交个spring管理的类实例化

    演示一下3:

    Appconfig.java

    package config;
    
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    
    public class Appconfig {
    }
    复制代码
    package service;
    
    
    import org.springframework.stereotype.Service;
    
    @Service
    public class TestService {
    }
    复制代码
    package test;
    
    import config.Appconfig;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import service.TestService;
    
    public class TestServiceDefinition {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx =
                    new AnnotationConfigApplicationContext(Appconfig.class);
    
            //由于只是声明了TestService,而没有定义,所以最终是找不到的,NoSuchBeanDefinitionException
            System.out.println(ctx.getBean(TestService.class).getClass().getName());
    
        }
    }
    复制代码

    Appconfig.java

    @ComponentScan("com.anzhi")
    复制代码

    TestServiceDefinition.java

    AnnotationConfigApplicationContext ctx =
        new AnnotationConfigApplicationContext();
    
    //register可以注册两种类型的bean,第一种Configuration类型的bean,普通的bean
    //然后在Appconfig中再次扫描ComponentScan
    //register底层未调用refresh
    ctx.register(Appconfig.class);
    //所以需要手动refresh,什么场景应用呢?@Profile先使对象实例化,然后设置对象属性
    //于是又可以这样写
    ctx.refresh();
    复制代码
    AnnotationConfigApplicationContext ctx =
        new AnnotationConfigApplicationContext(); // 如果一开始就把所有的类扫描出来,那么																		  // ctx.getEnvironment().setActiveProfiles();就没有作用了
    //setting 设置条件
    ctx.getEnvironment().setActiveProfiles();
    //register可以注册两种类型的bean,第一种Configuration类型的bean,普通的bean
    //然后在Appconfig中再次扫描ComponentScan
    //register底层未调用refresh
    
    //然后在这里扫描的时候,就不是扫描全部类了 part
    ctx.register(Appconfig.class);
    //所以需要手动refresh,什么场景应用呢?@Profile先使对象实例化,然后设置对象属性
    //于是又可以这样写
    ctx.refresh();
    复制代码

    第二种类型,单类

    package com.anzhi.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    //@ComponentScan("com.anzhi")
    public class Appconfig {
    }
    复制代码

    TestServiceDefinition.java

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx =
            new AnnotationConfigApplicationContext();
    
        ctx.getEnvironment().setActiveProfiles();
    
        //register可以注册两种类型的bean,第一种Configuration类型的bean,普通的bean
        //然后在Appconfig中再次扫描ComponentScan
        ctx.register(Appconfig.class);
        // ctx.register(TestService.class); //第二种,单类注册
        ctx.ctx.scan("com");
        ctx.refresh();
        System.out.println(ctx.getBean(TestService.class).getClass().getName());
    
    }
    复制代码

2. Anno

AnnotationConfigApplicationContext.java

package com.anzhi.utils;

import com.anzhi.anno.AnnoAnzhi;

import javax.xml.bind.Element;
import java.io.File;
import java.lang.reflect.Field;

public class AnnotationConfigApplicationContext {

    public void scan(String basePackage){

        //获取类的路径
        String path = this.getClass().getResource("/").getPath();
        String basePackagePath = basePackage.replaceAll("\\.", "\\\\");
        try {

            path = java.net.URLDecoder.decode(path, "UTF-8");

            File file = new File(path + "//" + basePackagePath);

            String[] namePath = file.list();

            for (String name : namePath){
                name = name.replaceAll(".class", "");
                Class clazz = Class.forName(basePackage+"."+name);
                //判断是否属于@xxxx
                if(clazz.isAnnotationPresent(AnnoAnzhi.class)){
                    AnnoAnzhi anzhi = (AnnoAnzhi) clazz.getAnnotation(AnnoAnzhi.class);
                    System.out.println(anzhi.value());
                    System.out.println(clazz.newInstance());
                }

            }
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}
复制代码

TestAnnoAnzhi.java

package com.anzhi.test;

import com.anzhi.utils.AnnotationConfigApplicationContext;

public class TestAnnoAnzhi {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx =
                new AnnotationConfigApplicationContext();

        ctx.scan("com.anzhi.service");
    }
}
复制代码

(有时间的话,后面写下@Autowired注解)

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享