在阅读本博文前, 要对模拟基于XML方式的SpringIOC有所了解
可以先看这篇博文: Spring源码_模拟基于XML方式的SpringIOC(模拟属性注入, 构造方法注入, 自动注入)
1.思路分析
- 对于注解方式, 无非就是在类上声明注解,告诉bean工厂这个类需要注册到容器中, 并且维护好bean之间的关系;
- 让工厂去维护bean对象,我们需要告诉工厂哪些包下的哪些类需要注册, 因此需要配置包扫描; 常用的开启包扫描方式有两种: 一种是XML配置方式,另一种是Java Config方式
- 既然配置了包扫描, 那我们可以扫描这下包下面所有添加注解的Class信息, 得到Class信息后就可以对对象进行实例化了
- 在实例化之前我们需要先将所有添加注解的Class信息加入一个标识为未注册bean的map容器中, 这样我们可以解决依赖顺序问题, 当维护一个bean对象的依赖时, 如果该依赖还没有被实例化, 那么可以先对该依赖实现实例化, 并且在维护依赖之前我们需要先对该实例进行实例化, 这样可以解决循环依赖问题(A里面依赖B,B里面依赖A)
- 在维护依赖时, 获取到bean对象所有的依赖属性后, 我们只需要对添加自动注入标签的属性进行注入
- 属性注入时也分为ByType和ByName两种方式
[项目源码已托管到github上,需要的自行下载] -->下载源码
2. 模拟基于注解方式的SpringIOC
2.1创建自定义注解
包扫描注解:
package com.anno.spring;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
public String value();
}
类声明注解:
package com.anno.spring;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnno {
public String value();
}
自动注入注解:
package com.anno.spring;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAutowire {
public String value() default "byType";
}
2.2创建自定义异常
不能解析属性或标签异常:
package com.anno.expections;
public class CannotParseExecption extends RuntimeException{
public CannotParseExecption(String msg) {
super(msg);
}
}
查找到多个预期对象异常:
package com.anno.expections;
public class TooManyExpectClassrExecption extends RuntimeException{
public TooManyExpectClassrExecption(String msg) {
super(msg);
}
}
未使用注解标识的类异常:
package com.anno.expections;
public class UndeclaredClassExecption extends RuntimeException{
public UndeclaredClassExecption(String msg) {
super(msg);
}
}
2.3创建dao层接口
package com.anno.dao;
public interface UserDao {
public void query();
}
2.4创建dao层实现类
package com.anno.dao;
import com.anno.spring.CustomAnno;
@CustomAnno("userDao")
public class UserDaoImpl implements UserDao {
@Override
public void query() {
System.out.println("模拟查询");
}
}
2.5创建service层接口
package com.anno.service;
public interface UserService {
public void query();
}
2.6创建service层实现类
package com.anno.service;
import com.anno.dao.UserDao;
import com.anno.spring.CustomAutowire;
import com.anno.spring.CustomAnno;
@CustomAnno("userService")
public class UserServiceImpl implements UserService {
@CustomAutowire
private UserDao userDao;
public void query() {
userDao.query();
}
}
2.7 创建XML配置文件(配置包扫描)
<?xml version='1.0' encoding='UTF-8'?>
<beans>
<!--XML配置模式开启包扫描-->
<component-scan base-package="com.anno"></component-scan>
</beans>
2.8 创建Java Config类(配置包扫描)
package com.anno.spring;
/**
* Java config方式开启包扫描
*/
@ComponentScan("com.anno")
public class AppConfig {
}
2.9创建AnnotationConfigApplicationContext类
内容很多, 都是我根据自己的思路一行一行写出来的, 请耐心研读 (旨在体会Spring注解方式的思路, 所以只考虑标准情况下)
package com.anno.spring;
import com.anno.expections.CannotParseExecption;
import com.anno.expections.TooManyExpectClassrExecption;
import com.anno.expections.UndeclaredClassExecption;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class AnnotationConfigApplicationContext {
//存储已注册的bean对象
private Map<String, Object> registedMap = null;
//存储未注册的bean对象
private Map<String, Class> noRegisterMap = null;
public AnnotationConfigApplicationContext(String classpath) {
//通过配置文件获取包扫描信息,并解析
this();
parseXml(classpath);
}
public AnnotationConfigApplicationContext(Class clazz) {
//通过Java Config获取包扫描信息,并解析
this();
register(clazz);
}
public AnnotationConfigApplicationContext() {
if (registedMap == null) {
registedMap = new HashMap<>();
}
if (noRegisterMap == null) {
noRegisterMap = new HashMap<>();
}
}
public Object getBean(String beanName){
if(beanName != null){
return registedMap.get(beanName);
}
return null;
}
public void register(Class clazz) {
Annotation[] annotations = clazz.getAnnotations();
String packagePath = null;
//遍历该类上的所有注解, 找到@CustomAnno注解,并获取到需要扫描的包路径
for (Annotation annotation : annotations) {
if (annotation instanceof CustomAnno) {
CustomAnno customAnno = (CustomAnno) annotation;
packagePath = customAnno.value();
break;
}
}
scan(packagePath);
}
public void parseXml(String path) {
File file = new File(this.getClass().getResource("/").getPath() + "//" + path);
//需要导入dom4j jar包
SAXReader reader = new SAXReader();
try {
//使用dom4j解析配置文件
Document document = reader.read(file);
//获取根节点<beans>
Element elementRoot = document.getRootElement();
//获取根节点的子节点(可能配置多个包扫描标签)
for (Iterator<Element> beanFir = elementRoot.elementIterator(); beanFir.hasNext(); ) {
Element elementChild = beanFir.next();
//注解扫描
if ("component-scan".equals(elementChild.getName())) {
String packagePath = elementChild.attribute("base-package").getValue();
//获取到包路径信息,开始进行包扫描
scan(packagePath);
} else {
System.out.println("这里仅考虑注解模式,其他标签不再解析...");
}
}
} catch (Exception e) {
e.printStackTrace();
}
//当所有的需要扫描的包都扫描完毕之后开始注册
//对noRegisterMap容器中的使用@CustomAnno注解的类进行实例化
doRegister();
}
//根据包路径扫描并注册使用注解标识的bean
public void scan(String basePackage) {
String rootPath = this.getClass().getResource("/").getPath();
rootPath = rootPath.substring(1, rootPath.length());
String basePackagePath = basePackage.replaceAll("\\.", "\\/");
// 获取包路径的真实路径信息 fileRoot = D:/Desktop/source_code/spring/licSpring/target/classes/com/anno
String fileRoot = rootPath + basePackagePath;
// 对包路径下的信息进行递归扫描
doScan(fileRoot, basePackage);
}
//递归扫描, 扫描出所有的class类, 并封装到容器中
public void doScan(String fileRoot, String basePackage) {
//获取fileRoot路径下的所有文件名称
File file = new File(fileRoot);
String[] fileNames = file.list();
//遍历该目录下所有的文件名; 如果是文件夹,则递归遍历; 如果是class类则尝试注册
for (String fileName : fileNames) {
if (fileName.indexOf(".") < 0) {
//注意每次循环都需要扩充包路径
doScan(fileRoot + "/" + fileName, basePackage + "." + fileName);
} else if (fileName.indexOf(".class") > 0) {
String className = fileName.substring(0, fileName.lastIndexOf(".class"));
String classPath = basePackage + "." + className;
try{
Class clazz = Class.forName(classPath);
if (clazz.isAnnotationPresent(CustomAnno.class)) {
CustomAnno customAnno = (CustomAnno) clazz.getAnnotation(CustomAnno.class);
//获取到注解中声明的beanName
String beanName = customAnno.value();
//先将class信息加入到与注册容器noRegisterMap中(注解中声明的beanName为key, Class对象为value)
noRegisterMap.put(beanName, clazz);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
//注册bean对象
public void doRegister() {
for (String beanName : noRegisterMap.keySet()) {
if(! registedMap.containsKey(beanName))
doRegisterBean(beanName);
}
}
public void doRegisterBean(String beanName) {
//用于暂存实例化的对象
Object currentRegObject = null;
Class clazz = noRegisterMap.get(beanName);
if(clazz == null){
throw new UndeclaredClassExecption("未声明的类...");
}
try {
//获取该类的所有依赖
Field[] fields = clazz.getDeclaredFields();
//在解决依赖注入前先进行实例化,并加入registedMap中, 解决循环依赖问题(不考虑构造函数相关问题...)
currentRegObject = clazz.newInstance();
registedMap.put(beanName, currentRegObject);
for (Field field : fields) {
String fieldClassName = field.getType().getName();
CustomAutowire customAutowire = field.getAnnotation(CustomAutowire.class);
//只对使用@CustomAutowire注解的属性进行自动注入
if (customAutowire != null) {
// CustomAutowire customAutowire = (CustomAutowire) annotatedType.getAnnotation(CustomAutowire.class);
String autowireType = customAutowire.value();
//判断是哪种注入方式(默认为按类型注入)
if ("byType".equalsIgnoreCase(autowireType)) {
Object tempFieldObj = null;
int suitableClassNum = 0;
for (String fieldKey : registedMap.keySet()) {
//获取接口的类路径
String tempFieldClassName = registedMap.get(fieldKey).getClass().getInterfaces()[0].getName();
if (fieldClassName.equals(tempFieldClassName)) {
tempFieldObj = registedMap.get(fieldKey);
suitableClassNum++;
}
}
//如果循环结束还没有找到符合的class, 那么先去实例化依赖的对象
if (suitableClassNum == 0) {
doRegisterBean(field.getName());
} else if (suitableClassNum == 1) {
//查找到符合条件的属性对象,注入
field.setAccessible(true);
field.set(currentRegObject,tempFieldObj);
System.out.println("根据类型注入...");
} else {
throw new TooManyExpectClassrExecption("期望找到一个对象, 但是找到了两个! ");
}
} else if ("byName".equalsIgnoreCase(autowireType)) {
Object tempFieldObj = null;
for (String fieldKey : registedMap.keySet()) {
if(fieldKey.equalsIgnoreCase(field.getName())){
tempFieldObj = registedMap.get(fieldKey);
System.out.println("根据名称注入...");
break;
}
}
//如果依赖的属性还没有进行实例化,则先对依赖的属性进行实例化
if(tempFieldObj == null){
doRegisterBean(field.getName());
}else{
field.setAccessible(true);
field.set(currentRegObject,tempFieldObj);
}
} else {
throw new CannotParseExecption("自动注入类型没见过, 无法解析...");
}
}
}//for
} catch (Exception e) {
e.printStackTrace();
}//catch
}//doRegisterBean
}
2.10 创建测试类
package com.anno.test;
import com.anno.service.UserService;
import com.anno.spring.AnnotationConfigApplicationContext;
import com.anno.spring.AppConfig;
public class SpringTest {
public static void main(String[] args) {
//AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("springAnno.xml");
// ctx.register(AppConfig.class);
UserService userService = (UserService) ctx.getBean("userService");
userService.query();
}
}