前言
本篇文章是基于上次对Spring IOC和AOP的仿写扩展的,最好先看一下之前的那篇文章:仿写Spring IOC和AOP,仿写思路和代码讲解
上次仿写的IOC是基于配置文件实现注入的,这次拓展了基于注解的注入,同时调整了实现IOC的整体架构,改动的地方还是很多的,所以我会重新讲解实现IOC的整体思路和代码实现
项目已发布到GitHub:Imitate-Spring-IOC-and-AOP
整体思路
先来说一下整体的设计思路:
- 首先要对配置文件进行扫描
- 如果扫描到了
<component-scan base-package="com.pojo"/>
,说明开启了对注解的支持(这里的路径只是打个比方,每个人都不一样) - 获取该路径下的所有类,如果这个类有Component注解,说明这个类可以被扫描到,把这个类封装为一个BeanDefinition,以后要用到 (BeanDefinition中有两个属性,包括类对象和作用域)
- 如果扫描到了
<bean></bean>
,获取标签上的的class属性,就可以得到类对象,也封装为一个BeanDefinition,以后要用到 - 创建一个HashMap中作为BeanDefinition容器,对象名BeanName作为key,每个BeanDefinition作为value
- 调用getBean方法的时候传入BeanName,如果容器中包含这个BeanName,从BeanDefinition容器拿到对应的BeanDefinition
- 如果BeanDefinition的Scope值为singleton,说明Bean的作用域是单例,就根据BeanName从Bean的单例池中获取Bean对象 (单例池也是一个HashMap),如果获取不到,说明是第一次获取这个对象,就使用反射通过BeanDefinition中的类对象属性实例化一个对象,再使用反射注入属性,最后放到单例池中,最后返回这个已经注入好的对象
- 如果BeanDefinition的Scope值为prototype,说明要重新创建一个对象,不放到单例池中,直接通过反射创建对象并注入属性再返回
以上就是整体的思路,可能还是没有头绪,不要慌,下面是各个模块的讲解
项目结构
anno包下放着有关IOC的一些注解
config包下是使用注解注入时需要的配置类
context包下是对应着基于注解注入和配置文件注入的context
scan包下是IOC的核心实现类
pojo包下是测试用的实体类
Resource包下是配置文件
代码实现
注解
没什么好说的,不知道IOC各个注解作用的可以去搜索一下
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value() default "";
}
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {
String value() default "";
}
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Resource {
String name() default "";
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
String value() default "";
}
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
String value();
}
BeanDefinition
包含了要Bean对象的类对象type和Bean的作用域scope
/**
* @Description: //BeanDefinition,包含了类对象、作用域
* @Author: zzy
* @Date: 2022/3/29 13:49
*/
public class BeanDefinition {
private Class type;
private String scope;
public BeanDefinition(Class type, String scope) {
this.type = type;
this.scope = scope;
}
public Class getType() {
return type;
}
public void setType(Class type) {
this.type = type;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
工具类Utils
包换了两个方法,scanBefore方法作用是扫描配置文件得到存放着标签的List,getFileFromPath方法的作用是由路径packagePath得到这个路径对应的文件夹或文件(路径类似com.zzy.pojo,是一个字符串)
/**
* @Description: 工具类
* @Author: zzy
* @Date: 2022/3/29 21:03
*/
class Utils {
/**
* @MethodName: ScanBeforeList
* @Description: 扫描配置文件
* @Author: zzy
* @Date: 2022/3/29 15:01
* @Param: [classPath]
* @Return: org.w3c.dom.NodeList
*/
static NodeList scanBefore(String classPath) throws Exception {
InputStream inputStream = new FileInputStream(classPath);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
Element root = doc.getDocumentElement();
return root.getChildNodes();
}
/**
* @MethodName: getFileFromPath
* @Description: 由包路径获取到文件
* @Author: zzy
* @Date: 2022/3/29 15:07
* @Param: [packagePath]
* @Return: java.io.File
*/
static File getFileFromPath(String packagePath, ClassLoader classLoader){
packagePath = packagePath.replace(".", "/");
URL resource = classLoader.getResource(packagePath);
assert resource != null;
String f1 = resource.getFile();
return new File(f1);
}
}
实体类User和Book
测试用的实体类,设置了int String int[] String[] 引用对象这几个属性,还使用了@Component、@Value、@Autowired和@Qualifier注解
/**
* @author 张梓毅
*/
@Component("user")
public class User {
@Value("zzy")
private String name;
@Value("20")
private int age;
private int[] scores;
private String[] subjects;
@Autowired
@Qualifier("java")
private Book book;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int[] getScores() {
return scores;
}
public void setScores(int[] scores) {
this.scores = scores;
}
public String[] getSubjects() {
return subjects;
}
public void setSubjects(String[] subjects) {
this.subjects = subjects;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", scores=" + Arrays.toString(scores) +
", subjects=" + Arrays.toString(subjects) +
", book=" + book +
'}';
}
}
@Component("java")
public class Book {
@Value("1999")
private int number;
public Book() {
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@Override
public String toString() {
return "Book{" +
"number=" + number +
'}';
}
}
配置文件applicationContext.xml
使用<component-scan base-package="MySpring.pojo"/>
开启对component-scan的支持,扫描base-package属性下的包路径,还利用配置文件对数组属性进行注入
<beans>
<!-- 添加component-scan支持,可以扫描某个包下所有类的@Component注解-->
<component-scan base-package="MySpring.pojo"/>
<bean id="user" class="MySpring.pojo.User">
<property name="scores">
<array>
<value>98</value>
<value>66</value>
<value>0</value>
</array>
</property>
<property name="subjects">
<array>
<value>数学</value>
<value>英语</value>
<value>语文</value>
</array>
</property>
</bean>
</beans>
IOC核心实现类Scan
着重讲一下这个类,为了观看方便,先把方法本体去掉,只有方法名,后面我会依次讲解,可以先熟悉熟悉大体结构
/**
* @Description: IOC核心实现类
* @Author: zzy
* @Date: 2022/3/29 21:11
*/
public class Scan {
/**
* 作为BeanDefinition容器
*/
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
/**
* 作为Bean单例池
*/
private Map<String, Object> singletonMap = new HashMap<>();
/**
* @MethodName: putBeanDefinition
* @Description: 将扫描到的BeanDefinition放到容器中
* @Author: zzy
* @Date: 2022/3/29 15:03
* @Param: [classPath]
* @Return: void
*/
public void putBeanDefinitionFromXml(String classPath) {
}
/**
* @MethodName: putBeanDefinitionByAnnoFromXml
* @Description: 通过XML中的ComponentScan扫描对应包下的所有类,封装为beanDefinition放到beanDefinitionMap中
* @Author: zzy
* @Date: 2022/3/29 20:06
* @Param: [element]
* @Return: void
*/
public void putBeanDefinitionByAnno(Element element,Class<?> configClass){
}
/**
* @MethodName: putBeanDefinitionByXml
* @Description: 扫描XML配置文件下的所有bean,封装为beanDefinition放到beanDefinitionMap中
* @Author: zzy
* @Date: 2022/3/29 20:09
* @Param: [element]
* @Return: void
*/
private void putBeanDefinitionByXml(Element element) {
}
/**
* @MethodName: getBeanByScope
* @Description: 获取bean对象
* @Author: zzy
* @Date: 2022/3/29 15:12
* @Param: [name]
* @Return: java.lang.Object
*/
public Object getBeanByName(String beanName) {
}
/**
* @MethodName: createBean
* @Description: 实例化Bean对象
* @Author: zzy
* @Date: 2022/3/29 15:31
* @Param: [beanName, beanDefinition]
* @Return: java.lang.Object
*/
private Object createBean(BeanDefinition beanDefinition) {
}
/**
* @MethodName: InjectField
* @Description: 注入属性
* @Author: zzy
* @Date: 2022/3/29 16:06
* @Param: [beanClass, bean]
* @Return: void
*/
private void InjectField(Class beanClass, Object bean) {
}
}
首先要有两个HashMap,beanDefinitionMap用来存放BeanDefinition,作为BeanDefinition容器,singletonMap用来存放单例Bean对象,作为Bean对象单例池
/**
* 作为BeanDefinition容器
*/
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
/**
* 作为Bean单例池
*/
private Map<String, Object> singletonMap = new HashMap<>();
putBeanDefinitionFromXml方法:
这个方法的作用是从XML配置文件中扫描每个标签,先调用Utils工具类中的scanBefore方法得到标签List,遍历List,判断这个标签是component-scan还是bean,如果是component-scan标签,调用putBeanDefinitionByAnno方法,如果是bean标签,调用putBeanDefinitionByXml方法
/**
* @MethodName: putBeanDefinition
* @Description: 将扫描到的BeanDefinition放到容器中
* @Author: zzy
* @Date: 2022/3/29 15:03
* @Param: [classPath]
* @Return: void
*/
public void putBeanDefinitionFromXml(String classPath) throws Exception {
NodeList nodes = Utils.scanBefore(classPath);
//遍历nodes
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
Element element = (Element) node;
//如果开启了component-scan的支持
if ("component-scan".equals(node.getNodeName())) {
putBeanDefinitionByAnno(element,null);
} else {
putBeanDefinitionByXml(element);
}
}
}
}
putBeanDefinitionByAnno方法:
这个方法的作用是通过配置文件中的component-scan标签中的base-package属性扫描对应路径下的文件夹或文件
这个方法有两个参数,第一个参数是为了支持XML配置文件中的component-scan标签用的,第二个参数是为了支持config类中的@ComponentScan注解用的,因为如果使用的是XML配置文件注入的话会传入配置文件路径,如果使用的是config类中的@ComponentScan注解会传入config的类对象,如果哪个不为空就对应何种方式
之后会调用Utils工具类中的getFileFromPath文件获取路径下的文件夹或文件,如果是文件夹就扫描这个文件夹下的带有@Component注解的类
将扫描到的类封装为BeanDefinition,全部放到BeanDefinitionMap中
/**
* @MethodName: putBeanDefinitionByAnnoFromXml
* @Description: 通过XML中的ComponentScan扫描对应包下的所有类,封装为beanDefinition放到beanDefinitionMap中
* @Author: zzy
* @Date: 2022/3/29 20:06
* @Param: [element]
* @Return: void
*/
public void putBeanDefinitionByAnno(Element element,Class<?> configClass){
String packagePath = "";
if(element != null){
packagePath = element.getAttribute("base-package");
}
if(configClass != null){
if(!configClass.isAnnotationPresent(ComponentScan.class)){
throw new IllegalArgumentException("选择的Config类不正确!");
}
packagePath = configClass.getAnnotation(ComponentScan.class).value();
}
ClassLoader classLoader = Scan.class.getClassLoader();
File file = Utils.getFileFromPath(packagePath, classLoader);
//如果是文件夹,说明要获取到这个文件夹下的所有类
if (file.isDirectory()) {
for (File f : Objects.requireNonNull(file.listFiles())) {
try {
//获取扫描的包下的类
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("MySpring"), absolutePath.indexOf(".class"));
absolutePath = absolutePath.replace("\\", ".");
Class<?> beanClass = classLoader.loadClass(absolutePath);
String scopeStr = "singleton";
//通过反射获取到@Scope注解的value,判断是singleton还是prototype
if (beanClass.isAnnotationPresent(Scope.class) && "prototype".equals(beanClass.getAnnotation(Scope.class).value())) {
scopeStr = "prototype";
}
//如果这个类加了@Component注解
if (beanClass.isAnnotationPresent(Component.class)) {
Component componentAnnotation = beanClass.getAnnotation(Component.class);
String beanName = componentAnnotation.value();
//如果没有给@Component注解value值,默认为首字母小写
if ("".equals(beanName)) {
beanName = (char) (32 + beanClass.getSimpleName().charAt(0)) + beanClass.getSimpleName().substring(1);
}
//封装为BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition(beanClass, scopeStr);
//将BeanDefinition加到BeanDefinitionMap中
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
putBeanDefinitionByXml方法:
这个方法的作用是获取扫描到的dean标签的id和class属性,通过反射得到Class对象,封装为一个BeanDefinition并放到BeanDefinitionMap中
但是这还没有完,因为注入的属性值都存在于配置文件中,所以为了让之后注入的时候不会再扫描一遍配置文件,会在这个时候实例化Bean对象并把属性注入进去,然后把实例化的Bean对象放到singletonMap单例池中(注意要判断一下单例池是不是已经有这个bean对象了,如果没有才这样做。这里有些人可能会觉得如果这个时候就放进入,那么当类中其他属性是通过注解注入的话就不能注入了,这一点我也考虑到了,我会在之后从单例池中获取的时候会再通过注解注入一遍)
/**
* @MethodName: putBeanDefinitionByXml
* @Description: 扫描XML配置文件下的所有bean,封装为beanDefinition放到beanDefinitionMap中
* @Author: zzy
* @Date: 2022/3/29 20:09
* @Param: [element]
* @Return: void
*/
private void putBeanDefinitionByXml(Element element) throws Exception {
//得到bean的id和class
String beanName = element.getAttribute("id");
String className = element.getAttribute("class");
//根据class路径得到Class对象
Class<?> beanClass = null;
try {
beanClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return;
}
String scopeStr = "singleton";
//通过反射获取到@Scope注解的value,判断是singleton还是prototype
if (beanClass.isAnnotationPresent(Scope.class) && "prototype".equals(beanClass.getAnnotation(Scope.class).value())) {
scopeStr = "prototype";
}
//封装为BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition(beanClass, scopeStr);
//放到BeanDefinitionMap中
beanDefinitionMap.put(beanName, beanDefinition);
//如果单例池中有这个bean了,就不用再实例化了
if(singletonMap.containsKey(beanName)){
return;
}
//利用反射实例化bean对象
Object bean = beanClass.newInstance();
NodeList propertyList = element.getElementsByTagName("property");
for (int j = 0; j < propertyList.getLength(); j++) {
Node property = propertyList.item(j);
if (property instanceof Element) {
//得到bean的name
String name = ((Element) property).getAttribute("name");
//可能是数组注入
Node arrayNode = ((Element) property).getElementsByTagName("array").item(0);
//利用反射得到这个属性和可访问权限
Field field = bean.getClass().getDeclaredField(name);
field.setAccessible(true);
//利用反射得到set方法和可访问权限
Method method = bean.getClass().getDeclaredMethod("set" + (char) (name.charAt(0) - 32) + name.substring(1), field.getType());
method.setAccessible(true);
//判断是不是数组注入
if (arrayNode == null) {
//得到每个property中的value和ref
String value = ((Element) property).getAttribute("value");
String ref = ((Element) property).getAttribute("ref");
//判断是value还是ref
if (!"".equals(value) && "".equals(ref)) {
if ("java.lang.String".equals(field.getType().getName())) {
method.invoke(bean, value);
} else if ("int".equals(field.getType().getName())) {
method.invoke(bean, Integer.parseInt(value));
}
} else if (!"".equals(ref) && "".equals(value)) {
method.invoke(bean, getBeanByName(ref));
} else {
throw new IllegalArgumentException("id为" + beanName + "的bean配置有误");
}
} else {
//标识是int数组还是String数组
boolean flag = false;
//得到多个value标签
NodeList valueList = ((Element) arrayNode).getElementsByTagName("value");
//根据数组类型创建数组
Object arr = Array.newInstance(field.getType().getComponentType(), valueList.getLength());
//如果是int数组,把标识改为true
if ("int".equals(field.getType().getComponentType().getName())) {
flag = true;
}
//遍历每个value节点
for (int k = 0; k < valueList.getLength(); k++) {
Node valueNode = valueList.item(k);
//获取value
String value = valueNode.getTextContent();
if ("".equals(value)) {
throw new IllegalArgumentException("id为" + beanName + "的bean配置有误");
}
if (flag) {
Array.set(arr, k, Integer.parseInt(value));
} else {
Array.set(arr, k, value);
}
}
method.invoke(bean, arr);
}
}
}
singletonMap.put(beanName, bean);
}
getBeanByName方法
这个方法的作用是通过对象名beanName类获取到bean对象,需要考虑单例和多例的情况,单例就直接从单例池中拿,如果单例池中没有说明这个类还没有进行Bean的实例化,就实例化Bean对象加到单例池中再返回,多例的话之间实例化Bean对象,不需要加到单例池中,直接返回即可
/**
* @MethodName: getBeanByScope
* @Description: 获取bean对象
* @Author: zzy
* @Date: 2022/3/29 15:12
* @Param: [name]
* @Return: java.lang.Object
*/
public Object getBeanByName(String beanName) throws IllegalAccessException {
//如果beanDefinitionMap包含key为beanName的键值对
if (beanDefinitionMap.containsKey(beanName)) {
//获取beanDefinition
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判断是不是单例,如果是singleton
if ("singleton".equals(beanDefinition.getScope())) {
//从单例池中获取bean对象
Object singletonBean = singletonMap.get(beanName);
//如果这个bean对象还没有加进去,就去实例化这个bean
if (singletonBean == null) {
//调用createBean方法来实例化Bean对象,注入也会在这个方法中完成
singletonBean = createBean(beanDefinition);
//放到单例池中
singletonMap.put(beanName, singletonBean);
} else {
//如果单例池中存在bean对象,考虑到是通过配置文件注入的,还需要再注入一次
InjectField(beanDefinition.getType(), singletonBean);
}
//返回单例Bean对象
return singletonBean;
} else if ("prototype".equals(beanDefinition.getScope())) {
//如果是多例,就直接实例化bean对象,返回,注入也会在这个方法中完成
return createBean(beanDefinition);
}
}
return null;
}
createBean方法:
这个方法的作用是利用反射实例化bean对象,调用InjectField方法注入属性后再返回
/**
* @MethodName: createBean
* @Description: 实例化Bean对象
* @Author: zzy
* @Date: 2022/3/29 15:31
* @Param: [beanName, beanDefinition]
* @Return: java.lang.Object
*/
private Object createBean(BeanDefinition beanDefinition) throws IllegalAccessException {
//通过反射实例化bean对象
Class beanClass = beanDefinition.getType();
Object bean = null;
try {
bean = beanClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//返回之前调用InjectField方法注入属性
InjectField(beanClass, bean);
return bean;
}
InjectField方法:
这个方法的作用是通过注解来注入,利用反射获取Field来注入,必须是带有@Component的类才能注入,为什么没有配置文件注入呢,因为putBeanDefinitionByXml方法中已经完成了配置文件注入
/**
* @MethodName: InjectField
* @Description: 注入属性
* @Author: zzy
* @Date: 2022/3/29 16:06
* @Param: [beanClass, bean]
* @Return: void
*/
private void InjectField(Class beanClass, Object bean) throws IllegalAccessException {
//如果这个类有@Component注解,说明要进行注入
if (beanClass.isAnnotationPresent(Component.class)) {
//反射获取Field
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
//根据@Value的值来注入
if (field.isAnnotationPresent(Value.class)) {
Value value = field.getAnnotation(Value.class);
String beanValue = value.value();
if ("java.lang.String".equals(field.getType().getName())) {
field.set(bean, beanValue);
} else if ("int".equals(field.getType().getName())) {
field.set(bean, Integer.parseInt(beanValue));
}
}
//根据@Autowired和@Qualifier来注入引用对象
if (field.isAnnotationPresent(Autowired.class)) {
String beanName = "";
if (field.isAnnotationPresent(Qualifier.class)) {
beanName = field.getAnnotation(Qualifier.class).value();
}
if ("".equals(beanName)) {
beanName = field.getName();
}
field.set(bean, getBeanByName(beanName));
}
//根据@Resource注解的name属性来注入
if (field.isAnnotationPresent(Resource.class)) {
String beanName = field.getAnnotation(Resource.class).name();
field.set(bean, getBeanByName(beanName));
}
}
}
}
至此,Scan类就已经完成了,整理一下全部代码:
package MySpring.MyIOC.scan;
import MySpring.MyIOC.anno.*;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @Description: IOC核心实现类
* @Author: zzy
* @Date: 2022/3/29 21:11
*/
public class Scan {
/**
* 作为BeanDefinition容器
*/
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
/**
* 作为Bean单例池
*/
private Map<String, Object> singletonMap = new HashMap<>();
/**
* @MethodName: putBeanDefinition
* @Description: 将扫描到的BeanDefinition放到容器中
* @Author: zzy
* @Date: 2022/3/29 15:03
* @Param: [classPath]
* @Return: void
*/
public void putBeanDefinitionFromXml(String classPath) throws Exception {
NodeList nodes = Utils.scanBefore(classPath);
//遍历nodes
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node instanceof Element) {
Element element = (Element) node;
//如果开启了注解的支持
if ("component-scan".equals(node.getNodeName())) {
putBeanDefinitionByAnno(element, null);
} else {
putBeanDefinitionByXml(element);
}
}
}
}
/**
* @MethodName: putBeanDefinitionByAnnoFromXml
* @Description: 通过XML中的ComponentScan扫描对应包下的所有类,封装为beanDefinition放到beanDefinitionMap中
* @Author: zzy
* @Date: 2022/3/29 20:06
* @Param: [element]
* @Return: void
*/
public void putBeanDefinitionByAnno(Element element, Class<?> configClass) {
String packagePath = "";
if (element != null) {
packagePath = element.getAttribute("base-package");
}
if (configClass != null) {
if (!configClass.isAnnotationPresent(ComponentScan.class)) {
throw new IllegalArgumentException("选择的Config类不正确!");
}
packagePath = configClass.getAnnotation(ComponentScan.class).value();
}
ClassLoader classLoader = Scan.class.getClassLoader();
File file = Utils.getFileFromPath(packagePath, classLoader);
//如果是文件夹,说明要获取到这个文件夹下的所有类
if (file.isDirectory()) {
for (File f : Objects.requireNonNull(file.listFiles())) {
try {
//获取扫描的包下的类
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("MySpring"), absolutePath.indexOf(".class"));
absolutePath = absolutePath.replace("\\", ".");
Class<?> beanClass = classLoader.loadClass(absolutePath);
String scopeStr = "singleton";
//通过反射获取到@Scope注解的value,判断是singleton还是prototype
if (beanClass.isAnnotationPresent(Scope.class) && "prototype".equals(beanClass.getAnnotation(Scope.class).value())) {
scopeStr = "prototype";
}
//如果这个类加了@Component注解
if (beanClass.isAnnotationPresent(Component.class)) {
Component componentAnnotation = beanClass.getAnnotation(Component.class);
String beanName = componentAnnotation.value();
//如果没有给@Component注解value值,默认为首字母小写
if ("".equals(beanName)) {
beanName = (char) (32 + beanClass.getSimpleName().charAt(0)) + beanClass.getSimpleName().substring(1);
}
//封装为BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition(beanClass, scopeStr);
//将BeanDefinition加到BeanDefinitionMap中
beanDefinitionMap.put(beanName, beanDefinition);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* @MethodName: putBeanDefinitionByXml
* @Description: 扫描XML配置文件下的所有bean,封装为beanDefinition放到beanDefinitionMap中
* @Author: zzy
* @Date: 2022/3/29 20:09
* @Param: [element]
* @Return: void
*/
private void putBeanDefinitionByXml(Element element) throws Exception {
//得到bean的id和class
String beanName = element.getAttribute("id");
String className = element.getAttribute("class");
//根据class路径得到Class对象
Class beanClass = null;
try {
beanClass = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return;
}
//封装为BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition(beanClass, "singleton");
//放到BeanDefinitionMap中
beanDefinitionMap.put(beanName, beanDefinition);
//利用反射实例化bean对象
Object bean = beanClass.newInstance();
NodeList propertyList = element.getElementsByTagName("property");
for (int j = 0; j < propertyList.getLength(); j++) {
Node property = propertyList.item(j);
if (property instanceof Element) {
//得到bean的name
String name = ((Element) property).getAttribute("name");
//可能是数组注入
Node arrayNode = ((Element) property).getElementsByTagName("array").item(0);
//利用反射得到这个属性和可访问权限
Field field = bean.getClass().getDeclaredField(name);
field.setAccessible(true);
//利用反射得到set方法和可访问权限
Method method = bean.getClass().getDeclaredMethod("set" + (char) (name.charAt(0) - 32) + name.substring(1), field.getType());
method.setAccessible(true);
//判断是不是数组注入
if (arrayNode == null) {
//得到每个property中的value和ref
String value = ((Element) property).getAttribute("value");
String ref = ((Element) property).getAttribute("ref");
//判断是value还是ref
if (!"".equals(value) && "".equals(ref)) {
if ("java.lang.String".equals(field.getType().getName())) {
method.invoke(bean, value);
} else if ("int".equals(field.getType().getName())) {
method.invoke(bean, Integer.parseInt(value));
}
} else if (!"".equals(ref) && "".equals(value)) {
method.invoke(bean, getBeanByName(ref));
} else {
throw new IllegalArgumentException("id为" + beanName + "的bean配置有误");
}
} else {
//标识是int数组还是String数组
boolean flag = false;
//得到多个value标签
NodeList valueList = ((Element) arrayNode).getElementsByTagName("value");
//根据数组类型创建数组
Object arr = Array.newInstance(field.getType().getComponentType(), valueList.getLength());
//如果是int数组,把标识改为true
if ("int".equals(field.getType().getComponentType().getName())) {
flag = true;
}
//遍历每个value节点
for (int k = 0; k < valueList.getLength(); k++) {
Node valueNode = valueList.item(k);
//获取value
String value = valueNode.getTextContent();
if ("".equals(value)) {
throw new IllegalArgumentException("id为" + beanName + "的bean配置有误");
}
if (flag) {
Array.set(arr, k, Integer.parseInt(value));
} else {
Array.set(arr, k, value);
}
}
method.invoke(bean, arr);
}
}
}
singletonMap.put(beanName, bean);
}
/**
* @MethodName: createBean
* @Description: 实例化Bean对象
* @Author: zzy
* @Date: 2022/3/29 15:31
* @Param: [beanName, beanDefinition]
* @Return: java.lang.Object
*/
private Object createBean(BeanDefinition beanDefinition) throws IllegalAccessException {
//通过反射实例化bean对象
Class beanClass = beanDefinition.getType();
Object bean = null;
try {
bean = beanClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//返回之前调用InjectField方法注入属性
InjectField(beanClass, bean);
return bean;
}
/**
* @MethodName: InjectField
* @Description: 注入属性
* @Author: zzy
* @Date: 2022/3/29 16:06
* @Param: [beanClass, bean]
* @Return: void
*/
private void InjectField(Class beanClass, Object bean) throws IllegalAccessException {
//如果这个类有@Component注解,说明要进行注入
if (beanClass.isAnnotationPresent(Component.class)) {
//反射获取Field
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
//根据@Value的值来注入
if (field.isAnnotationPresent(Value.class)) {
Value value = field.getAnnotation(Value.class);
String beanValue = value.value();
if ("java.lang.String".equals(field.getType().getName())) {
field.set(bean, beanValue);
} else if ("int".equals(field.getType().getName())) {
field.set(bean, Integer.parseInt(beanValue));
}
}
//根据@Autowired和@Qualifier来注入引用对象
if (field.isAnnotationPresent(Autowired.class)) {
String beanName = "";
if (field.isAnnotationPresent(Qualifier.class)) {
beanName = field.getAnnotation(Qualifier.class).value();
}
if ("".equals(beanName)) {
beanName = field.getName();
}
field.set(bean, getBeanByName(beanName));
}
//根据@Resource注解的name属性来注入
if (field.isAnnotationPresent(Resource.class)) {
String beanName = field.getAnnotation(Resource.class).name();
field.set(bean, getBeanByName(beanName));
}
}
}
}
/**
* @MethodName: getBeanByScope
* @Description: 获取bean对象
* @Author: zzy
* @Date: 2022/3/29 15:12
* @Param: [name]
* @Return: java.lang.Object
*/
public Object getBeanByName(String beanName) throws IllegalAccessException {
//如果beanDefinitionMap包含key为beanName的键值对
if (beanDefinitionMap.containsKey(beanName)) {
//获取beanDefinition
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判断是不是单例,如果是singleton
if ("singleton".equals(beanDefinition.getScope())) {
//从单例池中获取bean对象
Object singletonBean = singletonMap.get(beanName);
//如果这个bean对象还没有加进去,就去实例化这个bean
if (singletonBean == null) {
//调用createBean方法来实例化Bean对象,注入也会在这个方法中完成
singletonBean = createBean(beanDefinition);
//放到单例池中
singletonMap.put(beanName, singletonBean);
} else {
//如果单例池中存在bean对象,考虑到是通过配置文件注入的,还需要再注入一次
InjectField(beanDefinition.getType(), singletonBean);
}
//返回单例Bean对象
return singletonBean;
} else if ("prototype".equals(beanDefinition.getScope())) {
//如果是多例,就直接实例化bean对象,返回,注入也会在这个方法中完成
return createBean(beanDefinition);
}
}
return null;
}
}
还有两个context类没有说,下面来说一下
ClassPathXmlApplicationContext类
这个类只需要看第一个getBean方法就可以了,第二个getBean方法是为IOC提供AOP服务的方法,如果想知道怎么实现AOP的朋友可以看我开头发的那个文章,这里再发一下:仿写Spring IOC和AOP,仿写思路和代码讲解
这个类是基于配置文件注入的context类,第一个getBean方法就是就是调用一下Scan类的putBeanDefinitionFromXml方法,然后调用getBeanByName方法来返回Bean对象
/**
* @MethodName:
* @Description: 基于配置文件和注解的IOC
* @Author: zzy
* @Date: 2022/3/29 13:41
* @Param:
* @Return:
*/
public class ClassPathXmlApplicationContext extends Scan {
private String classpath;
public ClassPathXmlApplicationContext(String classpath) {
this.classpath = Objects.requireNonNull(Scan.class.getClassLoader().getResource(classpath)).getFile();
}
/**
* @MethodName: getBean
* @Description: 提供IOC服务的方法
* @Author: zzy
* @Date: 2022/3/29 23:54
* @Param: [beanName]
* @Return: java.lang.Object
*/
public Object getBean(String beanName) throws Exception {
putBeanDefinitionFromXml(classpath);
return super.getBeanByName(beanName);
}
/**
* @MethodName: getBean
* @Description: 为IOC提供AOP服务的方法
* @Author: zzy
* @Date: 2022/3/29 23:54
* @Param: [beanName, cl]
* @Return: java.lang.Object
*/
public Object getBean(String beanName, Class cl) throws Exception {
String pointcutName = null;
Method pointcutMethod = null;
Method beforeMethod = null;
Method afterMethod = null;
String beforeName = null;
String afterName = null;
//通过反射获取注解
Aspect aspect = (Aspect) cl.getAnnotation(Aspect.class);
if (aspect == null) {
throw new IllegalArgumentException("该类不是切面类");
}
Method[] methods = cl.getDeclaredMethods();
for (Method method : methods) {
Pointcut pointcut = method.getAnnotation(Pointcut.class);
if (pointcut != null) {
pointcutMethod = method;
pointcutName = pointcut.value();
break;
}
}
for (Method method : methods) {
Boolean flag = null;
String value = null;
Before before = method.getAnnotation(Before.class);
After after = method.getAnnotation(After.class);
if (before != null) {
flag = true;
value = before.value();
beforeMethod = method;
}else if(after != null){
flag = false;
value = after.value();
afterMethod = method;
}
if (flag != null && pointcutMethod != null && (value.equals(pointcutMethod.getName() + "()"))) {
if(flag){
beforeName = pointcutName;
}else{
afterName = pointcutName;
}
}else{
if(flag != null){
if (flag) {
beforeName = value;
}else{
afterName = value;
}
}
}
}
putBeanDefinitionFromXml(classpath);
Object bean = super.getBeanByName(beanName);
Advice advice = new Advice(bean,cl.newInstance(),beforeMethod,beforeName,afterMethod,afterName);
Object beanPorxy = AopProxy.getProxy(bean,advice);
return beanPorxy;
}
}
AnnotationConfigApplicationContext类
这个类和ClassPathXmlApplicationContext思路一样,但是这个类是基于注解的context,不会使用到配置文件,所以不会再调用putBeanDefinitionFromXml方法,而是调用putBeanDefinitionByAnno方法
/**
* @MethodName:
* @Description: 基于注解的IOC
* @Author: zzy
* @Date: 2022/3/29 20:17
* @Param:
* @Return:
*/
public class AnnotationConfigApplicationContext extends Scan {
private Class<?> configClass;
public AnnotationConfigApplicationContext(Class<?> configClass) {
this.configClass = configClass;
}
public Object getBean(String beanName) throws Exception {
putBeanDefinitionByAnno(null,configClass);
return super.getBeanByName(beanName);
}
public Object getBean(String beanName, Class cl) throws Exception {
String pointcutName = null;
Method pointcutMethod = null;
Method beforeMethod = null;
Method afterMethod = null;
String beforeName = null;
String afterName = null;
//通过反射获取注解
Aspect aspect = (Aspect) cl.getAnnotation(Aspect.class);
if (aspect == null) {
throw new IllegalArgumentException("该类不是切面类");
}
Method[] methods = cl.getDeclaredMethods();
for (Method method : methods) {
Pointcut pointcut = method.getAnnotation(Pointcut.class);
if (pointcut != null) {
pointcutMethod = method;
pointcutName = pointcut.value();
break;
}
}
for (Method method : methods) {
Boolean flag = null;
String value = null;
Before before = method.getAnnotation(Before.class);
After after = method.getAnnotation(After.class);
if (before != null) {
flag = true;
value = before.value();
beforeMethod = method;
}else if(after != null){
flag = false;
value = after.value();
afterMethod = method;
}
if (flag != null && pointcutMethod != null && (value.equals(pointcutMethod.getName() + "()"))) {
if(flag){
beforeName = pointcutName;
}else{
afterName = pointcutName;
}
}else{
if(flag != null){
if (flag) {
beforeName = value;
}else{
afterName = value;
}
}
}
}
putBeanDefinitionByAnno(null,configClass);
Object bean = super.getBeanByName(beanName);
Advice advice = new Advice(bean,cl.newInstance(),beforeMethod,beforeName,afterMethod,afterName);
Object beanPorxy = AopProxy.getProxy(bean,advice);
return beanPorxy;
}
}
IOC测试
我这里测试了ClassPathXmlApplicationContext,对User类进行依赖注入
public class MyTest {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("MySpring/Resource/applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user.toString());
}
}
测试结果:
可以发现属性都注入进去了
再测试一下是否是单例对象
public class MyTest {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("MySpring/Resource/applicationContext.xml");
User user1 = (User) context.getBean("user");
System.out.println(user1.toString());
User user2 = (User) context.getBean("user");
System.out.println(user2.toString());
System.out.println(user1 == user2);
}
}
测试结果:
是单例对象
再测试一下多例模式
public class MyTest {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("MySpring/Resource/applicationContext.xml");
User user1 = (User) context.getBean("user");
System.out.println(user1.toString());
User user2 = (User) context.getBean("user");
System.out.println(user2.toString());
System.out.println(user1 == user2);
}
}
测试结果:
就测试这么多吧,本人能力有限,可能做的并不太好,以后会慢慢补充更多功能