这篇文章主要带大家实现一个简单的Spring框架,包含单例、多例bean的获取,依赖注入、懒加载等功能。为了能让大家更好的理解Spring各个功能的实现,将版本由易到难贴出代码,文章内容会持续更新,感兴趣的小伙伴可以持续关注一下。
目录
一、创建Java项目
二、开始实现Spring
1、BeanFactory接口
2、ApplicationContext接口
3、ApplicationContext接口的实现类(Spring容器)
4、Spring容器代码初步实现
创建配置类
创建自定义的组件注解
@Component
@ComponentScan
创建bean的定义
实现Spring的组件扫描功能
创建一个单例bean——UserService
组件扫描功能测试
5、懒加载和bean的作用域
创建自定义注解
@Lazy
@Scope
创建一个非单例bean——UserMapper
根据bean名称获取bean功能测试
6、自动获取bean的名称
7、依赖注入@Autowired
创建@Autowired注解
UserService中注入UserMapper
添加依赖注入的代码
测试依赖注入
8、InitializingBean
创建InitializingBean接口
创建InitializingBean接口的实现类
9、@Configuration + @Bean
创建@Configuration注解
创建@Bean注解
创建一个UserController
创建配置类MyConfig
测试获取bean
9、未完待续
一、创建Java项目
首先,需要创建一个Java工程,名字就叫spring。
创建完成后,如下图,再依次创建三级包
二、开始实现Spring
Spring中最重要也是最基础的类就是Spring容器,Spring容器用于创建管理对象,为了方便实现类型转换功能,给接口设置一个参数化类型(泛型)。
1、BeanFactory接口
BeanFactory是spring容器的顶级接口,创建该接口,在该接口中定义三个重载的获取bean的方法。
package com.example.spring;/*** @author heyunlin* @version 1.0*/
public interface BeanFactory<T> {Object getBean(String beanName);T getBean(Class<T> type);T getBean(String beanName, Class<T> type);
}
2、ApplicationContext接口
ApplicationContext接口扩展自BeanFactory接口
package com.example.spring;/*** @author heyunlin* @version 1.0*/
public interface ApplicationContext<T> extends BeanFactory<T> {}
3、ApplicationContext接口的实现类(Spring容器)
创建一个ApplicationContext接口的实现类,实现接口中定义的所有抽象方法,这就是我们要直接用的Spring容器。
package com.example.spring;/*** @author heyunlin* @version 1.0*/
public class AnnotationConfigApplicationContext<T> implements ApplicationContext<T> {@Overridepublic Object getBean(String beanName) {return null;}@Overridepublic T getBean(Class<T> type) {return null;}@Overridepublic T getBean(String beanName, Class<T> type) {return null;}}
4、Spring容器代码初步实现
首先,组件扫描需要一个扫描路径,可以通过配置类上的@ComponentScan注解指定,如果不指定,则默认为配置类所在的包。
创建配置类
在当前包下创建一个类,配置包扫描路径。
package com.example.spring;/*** @author heyunlin* @version 1.0*/
@ComponentScan("com.example.spring")
public class SpringConfig {}
创建自定义的组件注解
@Component
package com.example.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author heyunlin* @version 1.0*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {String value() default "";
}
@ComponentScan
package com.example.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author heyunlin* @version 1.0*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {String value() default "";
}
创建bean的定义
创建一个BeanDefinition用来保存bean的基本信息,按alt + insert键创建getter/setter和toString方法。
package com.example.spring;/*** @author heyunlin* @version 1.0*/
public class BeanDefinition {/*** bean的类型*/private Class type;/*** bean的作用域*/private String scope;/*** 是否懒加载*/private boolean lazy;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;}public boolean isLazy() {return lazy;}public void setLazy(boolean lazy) {this.lazy = lazy;}@Overridepublic String toString() {return "BeanDefinition{" +"type=" + type +", scope='" + scope + '\'' +", lazy=" + lazy +'}';}}
实现Spring的组件扫描功能
以下代码只是完成了Spring的简单bean扫描功能,可以根据自己的见解进行代码实现。唯一复杂一点的地方是递归的过程,对递归不是很了解的需要去提前学习一下。
修改AnnotationConfigApplicationContext:
1、添加一个属性clazz,用于保存实例化时传递的配置类对象参数;
2、Spring容器中创建用于保存bean的定义BeanDefinition的map;
3、在初始化spring容器时,从指定的路径下开始扫描,地柜扫描当前目录及其子目录,把@Component注解标注的类加载出来,以bean名称为key,bean的信息封装成的BeanDefinition为value保存到一个map中;
- 在这里只专注于扫描组件 ,只需要@Component和@ComponentScan两个注解;
- 没有非常复杂的逻辑,暂时不考虑是否懒加载的单例和作用域,默认设置为false和单例;
package com.example.spring;import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;/*** @author heyunlin* @version 1.0*/
public class AnnotationConfigApplicationContext<T> implements ApplicationContext<T> {private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();public final Class<T> clazz;public AnnotationConfigApplicationContext(Class<T> clazz) throws ClassNotFoundException {this.clazz = clazz;// 扫描组件componentScan(clazz);}public Map<String, BeanDefinition> getBeanDefinitionMap() {return beanDefinitionMap;}/*** 扫描组件* @param clazz 配置类的类对象* @throws ClassNotFoundException 类找不到*/private void componentScan(Class<T> clazz) throws ClassNotFoundException {// 如果类上使用了@ComponentScan注解if (clazz.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);String value = componentScan.value();if (!"".equals(value)) {String path = value;path = path.replace(".", "/");URL resource = clazz.getClassLoader().getResource(path);File file = new File(resource.getFile());loopFor(file);}}}/*** 递归遍历指定文件/文件夹* @param file 文件/文件夹* @throws ClassNotFoundException 类找不到*/private void loopFor(File file) throws ClassNotFoundException {if (file.isDirectory()) {for (File listFile : file.listFiles()) {if (listFile.isDirectory()) {loopFor(listFile);continue;}toBeanDefinitionMap(listFile);}} else if (file.isFile()) {toBeanDefinitionMap(file);}}/*** 解析bean,并保存到Map<String, BeanDefinition>* @param file 解析的class文件* @throws ClassNotFoundException 类找不到*/private void toBeanDefinitionMap(File file) throws ClassNotFoundException {// 获取类的绝对路径String absolutePath = file.getAbsolutePath();// 处理得到类的全限定名absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));absolutePath = absolutePath.replace("\\", ".");// 通过类加载器加载Class<?> loadClass = clazz.getClassLoader().loadClass(absolutePath);String beanName;if (loadClass.isAnnotationPresent(Component.class)) {// 获取@Component注解上配置的组件名Component component = loadClass.getAnnotation(Component.class);beanName = component.value();// 是否懒加载boolean lazy = false;// 作用域String scope = "singleton";// 保存bean的定义BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(loadClass);beanDefinition.setLazy(lazy);beanDefinition.setScope(scope);beanDefinitionMap.put(beanName, beanDefinition);}}@Overridepublic Object getBean(String beanName) {return null;}@Overridepublic T getBean(Class<T> type) {return null;}@Overridepublic T getBean(String beanName, Class<T> type) {return null;}}
创建一个单例bean——UserService
package com.example.spring;/*** @author heyunlin* @version 1.0*/
@Component("userService")
public class UserService {}
组件扫描功能测试
为了方便获取已经加载到的bean的定义,在AnnotationConfigApplicationContext中临时添加getter方法
public Map<String, BeanDefinition> getBeanDefinitionMap() {return beanDefinitionMap; }
测试代码:SpringExample
package com.example.spring;import java.util.Map;/*** @author heyunlin* @version 1.0*/
public class SpringExample {public static void main(String[] args) throws ClassNotFoundException {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);Map<String, BeanDefinition> beanDefinitionMap = applicationContext.getBeanDefinitionMap();for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {System.out.println(entry.getKey() + ":" + entry.getValue());}}}
运行SpringExample的main方法,发现成功扫描到了bean
5、懒加载和bean的作用域
上面一节内容已经实现了了一个简单的组件扫描功能,这节内容主要是实现单例bean的懒加载和非懒加载,以及根据bean的作用域singleton和prototype来决定bean的创建、获取方式。
创建自定义注解
@Lazy
package com.example.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author heyunlin* @version 1.0*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Lazy {}
@Scope
package com.example.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author heyunlin* @version 1.0*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {String value() default "singleton";
}
当bean上面使用了@Lazy注解时,并且bean的作用域是单例,将执行懒加载的流程,第一次使用bean的时候才创建bean。
那么,如何保证单例bean是“单例”的呢?
其实这个问题很简单,通过一个map缓存单例的bean,当调用getBean()方法获取bean时,判断bean的作用域如果是单例,就从单例池中获取,不用再次创建。
因此,需要先创建一个map保存单例的bean,以bean的名称为key,单例bean对象为value存储起来。
/*** 单例对象池*/ private final Map<String, Object> singletonObjects = new HashMap<>();
在Spring容器初始化过程中,把非懒加载单例的单例bean缓存到singletonObjects中,而懒加载的单例bean是在第一次获取bean的时候创建bean,然后保存到singletonObjects。
package com.example.spring;import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;/*** @author heyunlin* @version 1.0*/
public class AnnotationConfigApplicationContext<T> implements ApplicationContext<T> {/*** 配置类的类对象*/public final Class<T> clazz;/*** 单例对象池*/private final Map<String, Object> singletonObjects = new HashMap<>();private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();public AnnotationConfigApplicationContext(Class<T> clazz) throws ClassNotFoundException {this.clazz = clazz;// 扫描组件componentScan(clazz);}public Map<String, BeanDefinition> getBeanDefinitionMap() {return beanDefinitionMap;}/*** 扫描组件* @param clazz 配置类的类对象* @throws ClassNotFoundException 类找不到*/private void componentScan(Class<T> clazz) throws ClassNotFoundException {// 如果类上使用了@ComponentScan注解if (clazz.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);String value = componentScan.value();if (!"".equals(value)) {String path = value;path = path.replace(".", "/");URL resource = clazz.getClassLoader().getResource(path);File file = new File(resource.getFile());loopFor(file);}}}/*** 递归遍历指定文件/文件夹* @param file 文件/文件夹* @throws ClassNotFoundException 类找不到*/private void loopFor(File file) throws ClassNotFoundException {if (file.isDirectory()) {for (File listFile : file.listFiles()) {if (listFile.isDirectory()) {loopFor(listFile);continue;}toBeanDefinitionMap(listFile);}} else if (file.isFile()) {toBeanDefinitionMap(file);}}/*** 解析bean,并保存到Map<String, BeanDefinition>* @param file 解析的class文件* @throws ClassNotFoundException 类找不到*/private void toBeanDefinitionMap(File file) throws ClassNotFoundException {// 获取类的绝对路径String absolutePath = file.getAbsolutePath();// 处理得到类的全限定名absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));absolutePath = absolutePath.replace("\\", ".");// 通过类加载器加载Class<?> loadClass = clazz.getClassLoader().loadClass(absolutePath);String beanName;if (loadClass.isAnnotationPresent(Component.class)) {// 获取@Component注解上配置的组件名Component component = loadClass.getAnnotation(Component.class);beanName = component.value();// 是否懒加载boolean lazy = false;// 作用域String scope = "singleton";// 新增的代码-------------------start// 类上使用了@Scope注解if (loadClass.isAnnotationPresent(Scope.class)) {// 获取@Scope注解Scope annotation = loadClass.getAnnotation(Scope.class);// 单例beanif (isSingleton(annotation.value())) {lazy = loadClass.isAnnotationPresent(Lazy.class);} else {// 非单例scope = annotation.value();}} else {// 类上没有使用@Scope注解,默认是单例的lazy = loadClass.isAnnotationPresent(Lazy.class);}// 新增的代码-------------------end// 保存bean的定义BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(loadClass);beanDefinition.setLazy(lazy);beanDefinition.setScope(scope);beanDefinitionMap.put(beanName, beanDefinition);}}// 新增的代码-------------------start/*** 判断作用域是否单例* @param scope bean的作用域* @return boolean 如果是单例,返回true,否则返回false*/private boolean isSingleton(String scope) {return "singleton".equals(scope);}// 新增的代码-------------------end@Overridepublic Object getBean(String beanName) {// 修改的代码-------------------startBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//if (!beanDefinitionMap.containsKey(beanName)) {if (beanDefinition == null) {throw new NullPointerException();}return getBean(beanName, beanDefinition);// 修改的代码-------------------end}@Overridepublic T getBean(Class<T> type) {return null;}@Overridepublic T getBean(String beanName, Class<T> type) {return null;}// 新增的代码-------------------start/*** 统一获取bean的方法* @param beanName bean名称* @param beanDefinition BeanDefinition* @return Object 符合条件的bean对象*/private Object getBean(String beanName, BeanDefinition beanDefinition) {String scope = beanDefinition.getScope();// bean的作用域是单例if (isSingleton(scope)) {Object object = singletonObjects.get(beanName);// 懒加载的单例beanif (object == null) {Object bean = createBean(beanDefinition);singletonObjects.put(beanName, bean);}return singletonObjects.get(beanName);}return createBean(beanDefinition);}/*** 创建bean对象* @param beanDefinition bean的定义* @return Object 创建好的bean对象*/private Object createBean(BeanDefinition beanDefinition) {Object bean = null;Class beanType = beanDefinition.getType();try {// 调用无参构造方法初始化Constructor constructor = beanType.getConstructor();bean = constructor.newInstance();} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {e.printStackTrace();}return bean;}// 新增的代码-------------------end}
如上,已经实现了通过bean名称获取bean的方法,接下来简单测试一下
创建一个非单例bean——UserMapper
package com.example.spring;/*** @author heyunlin* @version 1.0*/
@Component("userMapper")
@Scope("prototype")
public class UserMapper {}
根据bean名称获取bean功能测试
package com.example.spring;/*** @author heyunlin* @version 1.0*/
public class SpringExample {public static void main(String[] args) throws ClassNotFoundException {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);Object userService1 = applicationContext.getBean("userService");Object userService2 = applicationContext.getBean("userService");System.out.println(userService1);System.out.println(userService2);System.out.println("----------------------------------------------------");Object userMapper3 = applicationContext.getBean("userMapper");Object userMapper4 = applicationContext.getBean("userMapper");System.out.println(userMapper3);System.out.println(userMapper4);}}
如图,获取到的两个Userservice是同一个对象,而UserMapper因为是非单例的,每次获取bean都会创建一次。
6、自动获取bean的名称
上面我们都是通过@Component注解里的value属性获取bean的名称,每次都要写value属性值。我们平时使用@Component注解一般都不会写组件名,会根据类名来获取。
bean名称的默认取值:
1、当类名是以大驼峰命名法,即首单词的首字母大写,其他字母小写,之后每个单词首字母大写(如UserService、UserMapper),组件名为类名首字母大写;
2、当类名是连续的大写字母开头,(如IUserService),组件名为类名;
根据这个规则来对现有代码进行改造,新增一个getBeanName()方法。
package com.example.spring;import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;/*** @author heyunlin* @version 1.0*/
public class AnnotationConfigApplicationContext<T> implements ApplicationContext<T> {/*** 配置类的类对象*/public final Class<T> clazz;/*** 单例对象池*/private final Map<String, Object> singletonObjects = new HashMap<>();private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();public AnnotationConfigApplicationContext(Class<T> clazz) throws ClassNotFoundException {this.clazz = clazz;// 扫描组件componentScan(clazz);}public Map<String, BeanDefinition> getBeanDefinitionMap() {return beanDefinitionMap;}/*** 扫描组件* @param clazz 配置类的类对象* @throws ClassNotFoundException 类找不到*/private void componentScan(Class<T> clazz) throws ClassNotFoundException {// 如果类上使用了@ComponentScan注解if (clazz.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);String value = componentScan.value();if (!"".equals(value)) {String path = value;path = path.replace(".", "/");URL resource = clazz.getClassLoader().getResource(path);File file = new File(resource.getFile());loopFor(file);}}}/*** 递归遍历指定文件/文件夹* @param file 文件/文件夹* @throws ClassNotFoundException 类找不到*/private void loopFor(File file) throws ClassNotFoundException {if (file.isDirectory()) {for (File listFile : file.listFiles()) {if (listFile.isDirectory()) {loopFor(listFile);continue;}toBeanDefinitionMap(listFile);}} else if (file.isFile()) {toBeanDefinitionMap(file);}}/*** 解析bean,并保存到Map<String, BeanDefinition>* @param file 解析的class文件* @throws ClassNotFoundException 类找不到*/private void toBeanDefinitionMap(File file) throws ClassNotFoundException {// 获取类的绝对路径String absolutePath = file.getAbsolutePath();// 处理得到类的全限定名absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));absolutePath = absolutePath.replace("\\", ".");// 通过类加载器加载Class<?> loadClass = clazz.getClassLoader().loadClass(absolutePath);String beanName;if (loadClass.isAnnotationPresent(Component.class)) {// 获取@Component注解上配置的组件名Component component = loadClass.getAnnotation(Component.class);beanName = component.value();/******************新增代码*****************/if ("".equals(beanName)) {beanName = getBeanName(loadClass);}/******************新增代码*****************/// 是否懒加载boolean lazy = false;// 作用域String scope = "singleton";// 类上使用了@Scope注解if (loadClass.isAnnotationPresent(Scope.class)) {// 获取@Scope注解Scope annotation = loadClass.getAnnotation(Scope.class);// 单例beanif (isSingleton(annotation.value())) {lazy = loadClass.isAnnotationPresent(Lazy.class);} else {// 非单例scope = annotation.value();}} else {// 类上没有使用@Scope注解,默认是单例的lazy = loadClass.isAnnotationPresent(Lazy.class);}// 保存bean的定义BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(loadClass);beanDefinition.setLazy(lazy);beanDefinition.setScope(scope);beanDefinitionMap.put(beanName, beanDefinition);}}/******************新增代码*****************//*** 根据类对象获取beanName* @param clazz bean的Class对象* @return String bean名称*/private String getBeanName(Class<?> clazz) {String beanName = clazz.getSimpleName();// 判断是否以双大写字母开头String className = beanName.replaceAll("([A-Z])([A-Z])", "$1_$2");// 正常的大驼峰命名:bean名称为类名首字母大写if (className.indexOf("_") != 1) {beanName = beanName.substring(0, 1).toLowerCase().concat(beanName.substring(1));}
// else { // 否则,bean名称为类名
// beanName = beanName;
// }return beanName;}/******************新增代码*****************//*** 判断作用域是否单例* @param scope bean的作用域* @return boolean 如果是单例,返回true,否则返回false*/private boolean isSingleton(String scope) {return "singleton".equals(scope);}@Overridepublic Object getBean(String beanName) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);//if (!beanDefinitionMap.containsKey(beanName)) {if (beanDefinition == null) {throw new NullPointerException();}return getBean(beanName, beanDefinition);}@Overridepublic T getBean(Class<T> type) {return null;}@Overridepublic T getBean(String beanName, Class<T> type) {return null;}/*** 统一获取bean的方法* @param beanName bean名称* @param beanDefinition BeanDefinition* @return Object 符合条件的bean对象*/private Object getBean(String beanName, BeanDefinition beanDefinition) {String scope = beanDefinition.getScope();// bean的作用域是单例if (isSingleton(scope)) {Object object = singletonObjects.get(beanName);// 懒加载的单例beanif (object == null) {Object bean = createBean(beanDefinition);singletonObjects.put(beanName, bean);}return singletonObjects.get(beanName);}return createBean(beanDefinition);}/*** 创建bean对象* @param beanDefinition bean的定义* @return Object 创建好的bean对象*/private Object createBean(BeanDefinition beanDefinition) {Object bean = null;Class beanType = beanDefinition.getType();try {// 调用无参构造方法初始化Constructor constructor = beanType.getConstructor();bean = constructor.newInstance();} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {e.printStackTrace();}return bean;}}
然后删除两个Bean上的@Component的value属性值
package com.example.spring;/*** @author heyunlin* @version 1.0*/
@Component
@Scope("prototype")
public class UserMapper {}
package com.example.spring;/*** @author heyunlin* @version 1.0*/
@Component
public class UserService {}
再次运行测试代码,发现一样能够获取到两个bean,且运行结果和上一节相同。
7、依赖注入@Autowired
这节主要实现简单的字段注入(属性注入),在bean初始化完成后对bean上使用了@Autowired注解的字段设置值,当然了,前提是注入的字段类型是一个bean类型。
创建@Autowired注解
package com.example.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author heyunlin* @version 1.0*/
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {boolean required() default true;
}
UserService中注入UserMapper
package com.example.spring;/*** @author heyunlin* @version 1.0*/
@Component
public class UserService {@Autowiredprivate UserMapper userMapper;
}
添加依赖注入的代码
完善代码,只需要修改AnnotationConfigApplicationContext的createBean()方法
/*** 创建bean对象* @param beanDefinition bean的定义* @return Object 创建好的bean对象*/private Object createBean(BeanDefinition beanDefinition) {Object bean = null;Class beanType = beanDefinition.getType();try {// 调用无参构造方法初始化Constructor constructor = beanType.getConstructor();bean = constructor.newInstance();// 新增:处理字段注入// 获取bean的所有自定义属性Field[] fields = beanType.getDeclaredFields();// 给@Autowired注解标注的属性设置值for (Field field : fields) {if (field.isAnnotationPresent(Autowired.class)) {// 获取bean,并设置到@Autowired注入的属性中Object autowiredBean = getBean(field.getName());field.setAccessible(true);field.set(bean, autowiredBean);}}} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {e.printStackTrace();}return bean;}
测试依赖注入
还是之前的代码,通过debug运行main方法,发现UserMapper确实有值。
完善依赖注入代码
上面的代码只是简单的通过bean名称获取bean,当找不到对应bean名称的bean时,会尝试通过类型查找,因此,在这里需要实现通过bean类型获取bean的方法。
实现通过bean类型获取bean
@Overridepublic T getBean(Class<T> type) {if (type == null) {throw new IllegalStateException("bean类型不能为空!");}// 保存指定类型的bean的个数AtomicInteger count = new AtomicInteger();// 保存同一类型的beanMap<String, BeanDefinition> objectMap = new HashMap<>();for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {String beanName = entry.getKey();BeanDefinition beanDefinition = entry.getValue();Class beanType = beanDefinition.getType();if (beanType.equals(type)) {count.addAndGet(1);objectMap.put(beanName, beanDefinition);}}if (count.get() == 0 || count.get() > 1) {throw new IllegalStateException();}return (T) getBean((String) objectMap.keySet().toArray()[0], (BeanDefinition) objectMap.values().toArray()[0]);}
修改通过bean名称获取bean的方法
@Overridepublic Object getBean(String beanName) {if (!beanDefinitionMap.containsKey(beanName)) {return null;}return getBean(beanName, beanDefinitionMap.get(beanName));}
这样的话,当我们通过bean名称获取到的bean为null时,在尝试通过类型查找,当通过类型查找到多个bean时会抛出异常。
/*** 创建bean对象* @param beanDefinition bean的定义* @return Object 创建好的bean对象*/private Object createBean(BeanDefinition beanDefinition) {Object bean = null;Class beanType = beanDefinition.getType();// 获取所有构造方法Constructor[] constructors = beanType.getConstructors();try {/*** 推断构造方法* 1、没有提供构造方法:调用默认的无参构造* 2、提供了构造方法:* - 构造方法个数为1* - 构造方法参数个数为0:无参构造* - 构造方法参数个数不为0:传入多个为空的参数* - 构造方法个数 > 1:推断失败,抛出异常*/if (isEmpty(constructors)) {// 无参构造方法Constructor constructor = beanType.getConstructor();bean = constructor.newInstance();} else if (constructors.length == 1) {Constructor constructor = constructors[0];// 得到构造方法参数个数int parameterCount = constructor.getParameterCount();if (parameterCount == 0) {// 无参构造方法bean = constructor.newInstance();} else {// 多个参数的构造方法Object[] array = new Object[parameterCount];bean = constructor.newInstance(array);}} else {throw new IllegalStateException();}// 获取bean的所有自定义属性Field[] fields = beanType.getDeclaredFields();// 处理字段注入for (Field field : fields) {if (field.isAnnotationPresent(Autowired.class)) {// 获取bean// 通过bean名称获取beanObject autowiredBean = getBean(field.getName());// 没有找到对应名称的bean,尝试通过bean类型查找if (autowiredBean == null) {autowiredBean = getBean(beanType);}// 并设置到@Autowired注入的属性中field.setAccessible(true);field.set(bean, autowiredBean);}}// 调用InitializingBean的afterPropertiesSet()方法if (bean instanceof InitializingBean) {Method afterPropertiesSet = beanType.getDeclaredMethod("afterPropertiesSet");afterPropertiesSet.invoke(bean);}} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {e.printStackTrace();}return bean;}
8、InitializingBean
InitializingBean是Spring bean生命周期的重要钩子方法之一,在Spring初始化完成后调用。
我们创建一个InitializingBean的实现类,同时使用@Component将该实现类声明为bean。
创建InitializingBean接口
package com.example.spring;/*** @author heyunlin* @version 1.0*/
public interface InitializingBean {void afterPropertiesSet();
}
创建InitializingBean接口的实现类
package com.example.spring;import com.example.spring.annotation.Component;/*** @author heyunlin* @version 1.0*/
@Component
public class InitializingBeanImpl implements InitializingBean {@Overridepublic void afterPropertiesSet() {System.out.println("执行InitializingBean.afterPropertiesSet()");}}
在创建bean的时候判断该bean是否InitializingBean接口的实现类,如果是,调用其afterPropertiesSet()方法,代码很简单。
/*** 创建bean对象* @param beanDefinition bean的定义* @return Object 创建好的bean对象*/private Object createBean(BeanDefinition beanDefinition) {Object bean = null;Class beanType = beanDefinition.getType();try {// 调用无参构造方法初始化Constructor constructor = beanType.getConstructor();bean = constructor.newInstance();// 获取bean的所有自定义属性Field[] fields = beanType.getDeclaredFields();// 处理字段注入for (Field field : fields) {if (field.isAnnotationPresent(Autowired.class)) {// 获取bean,并设置到@Autowired注入的属性中Object autowiredBean = getBean(field.getName());field.setAccessible(true);field.set(bean, autowiredBean);}}// 新增:调用InitializingBean的afterPropertiesSet()方法if (bean instanceof InitializingBean) {Method afterPropertiesSet = beanType.getDeclaredMethod("afterPropertiesSet");afterPropertiesSet.invoke(bean);}} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {e.printStackTrace();}return bean;}
9、@Configuration + @Bean
上面的代码完成了@Component注解的隐式声明的bean的扫描,接下来完成@Bean注解配置的bean扫描。(@Configuration注解标注的类中,@Bean注解标注方法的返回值是bean的类型,方法名就是bean的名称)
创建@Configuration注解
package com.example.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author heyunlin* @version 1.0*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Configuration {}
创建@Bean注解
package com.example.spring;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author heyunlin* @version 1.0*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {String value() default "";
}
创建一个UserController
package com.example.spring;/*** @author heyunlin* @version 1.0*/
public class UserController {}
创建配置类MyConfig
package com.example.spring;/*** @author heyunlin* @version 1.0*/
@Configuration
public class MyConfig {@Beanpublic UserController userController() {return new UserController();}}
测试获取bean
package com.example.spring;/*** @author heyunlin* @version 1.0*/
public class SpringExample {public static void main(String[] args) throws ClassNotFoundException {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);Object userController1 = applicationContext.getBean("userController");Object userController2 = applicationContext.getBean("userController");System.out.println(userController1);System.out.println(userController2);}}
运行代码,发现确实获取到了两个相同的bean
9、未完待续
文章和代码持续更新中,敬请期待~
手写Spring Framework源码https://gitee.com/he-yunlin/spring.git