这是我参与更文挑战的第24天
Spring源码解析
1. 源码编译(先看5编译错误总结,接着4,然后遇到错误对号入座)
idea:2018(后来换成2020)
Gradle:4.9
spring-framework5.0.x
(只针对5.0.x,其他版本不敢确定)
-
使用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/…)
在这之前确保这两插件已经安装
- Spring AOP/@AspectJ
- AspectJ Support
为了自己方便,截取了博主的图片
然后build project,还报错,
这个错误可以先不用管,这个包的问题解决。但是在自己建的项目中,还是没有@Repository的注解。后面使用alt+enter键,选中了spring-context-main这个包,然后就行了,但是图没截取出来,后面复现也没成功。类似maven的添加依赖包。我截取了一张导入后的包,可以试试手动导入这个包
然后使用AnnotationConfigApplicationContext 变量ctx调用register爆红、报错,忘了截图了(整个人都要疯了)记得错是无法使用类的错误或者找不到spring-context下的什么包。
4. 重新编译
-
下载源码:还是上述地址
-
修改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 复制代码
-
修改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" } } 复制代码
-
在cmd界面下进入源码包运行gradlew.bar报错
解决:
-
再次在cmd中编译运行gradlew.bat,成功
-
根据官方文档: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. 编译错误总结
- 运行gradlew.bat,因为没有修改spring-beans的gradle配置导致错误,这个很明显,也很容易百度到,可以看出错1;(尽量不要包含中文路径或空格啥的,鬼知道会出什么错误)
- 根据文档预编译,导致的错误,会出现这样的情况:
- getArchiveFile无法找到,符号错误啥的, required spring-core或者bean什么的。但是奇怪的是我后面重新下载编译好后,过程中没有出现这两种情况,甚至都没有搜索到getArchiveFile这个方法。也是无语。不过可以避免(猜测),可能还是网络原因导致编译下载出问题报错,或者spring-beans的配置不单单是简单注释,可能还要加上我配置那几句(也是在网上无意看到的。)。
- spring-aspejct需要aspejct编译工具,确保IDEA的插件齐全(前面也有提到,补充几个Kotlin,Gradle),这里还要注意的是,尽量将idea升高一点儿,总感觉2018的插件存在问题,没验证过。我后来升级到2020版(好像插件都带着)。
- 然后像什么Annotation…找不到,符号错误等,前面弄好后,基本不会有问题。
-
- 还遇到问题,可以参考这几个网站试试
- IDEA+Gradle构建Spring5源码阅读环境教程(优化构建速度) – 知乎
- 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译
- spring源码系列(六)——番外篇如何编译spring的源码_java_lyvee的专栏-CSDN博客
- spring5.3.x源码阅读环境搭建 – 小黑电脑
- 记录编译spring-framework源码的几个坑
- 编译Spring – 简书
- Spring源码 Gradle编译Spring源码 – 但行好事-莫问前程 – 博客园
- Spring5源码阅读环境搭建-gradle构建编译_zhoong-CSDN博客
- spring5源码编译_chuyang0312的博客-CSDN博客
- spring学习-01编译spring5.0源码(亲测可用)_安静的小海豹的博客-CSDN博客
- 还有一种,去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公司,你会怎么开发?
-
那些类需要依赖,如何告诉编译器这些类是被需要的?bean标签
-
在有了依赖后,如何将userdao这个类给到service呢(毕竟拥有了才能使用,只依赖不行啊)?
- 提供set/get方法
- 构造时提供
-
但是这种方法是由程序员自己实现,并没有依赖第三方。而依赖第三方实现的这种方式就称之为注入。set/构造,但是对于容器来说,没有那么智能,需要我们告诉它由那种方法注入。
-
在这之后又该怎么维护依赖关系呢?setter/structure
-
那么是如何体setter/structure。property标签
-
在添加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); } } 复制代码
小结:
- 遍历配置文件,将扫描到的类名通过反射放入Map集合中,即class.forname()实例化对象;
- 依赖注入:判断对象是否有属性,有则注入
3. 模拟annotation
BeanFactory和FactoryBean:
-
如果你的类实现了FactoryBean,那么spring容器中存在两个对象,一个交getObject(),返回的对象,还有一个是当前对象。
通过name获取beanName。不使用name作为beanName有两个原因:name可能会以&字符开头,表明调用者想获取FactoryBean本身,而非FactoryBean实现类所创建的bean。
-
应用场景:
当一个类拥有很多属性,而且依赖很多,甚至有些依赖第三方库的类,其中的属性多到爆炸,如果让你维护,你怎么去维护。比如拿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 的环境有几种方法
-
xml:ClassPathXmlApplicationContext (最终完成类的扫描,可以完成单独bean的注册,即声明和注册)
-
annotation:必须要依赖与1或者2,才能完成对注解扫描,不然spring是不认识的
-
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注解)