这篇文章主要带大家实现一个简单的Spring框架,包含单例、多例bean的获取,依赖注入、懒加载等功能。为了能让大家更好的理解Spring各个功能的实现,将版本由易到难贴出代码,文章内容会持续更新,感兴趣的小伙伴可以持续关注一下。
目录
3、ApplicationContext接口的实现类(Spring容器)
[@Configuration + @Bean](#@Configuration + @Bean)
一、创建Java项目
首先,需要创建一个Java工程,名字就叫spring。
创建完成后,如下图,再依次创建三级包
二、开始实现Spring
Spring中最重要也是最基础的类就是Spring容器,Spring容器用于创建管理对象,为了方便实现类型转换功能,给接口设置一个参数化类型(泛型)。
1、BeanFactory接口
BeanFactory是spring容器的顶级接口,创建该接口,在该接口中定义三个重载的获取bean的方法。
java
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接口
java
package com.example.spring;
/**
* @author heyunlin
* @version 1.0
*/
public interface ApplicationContext<T> extends BeanFactory<T> {
}
3、ApplicationContext接口的实现类(Spring容器)
创建一个ApplicationContext接口的实现类,实现接口中定义的所有抽象方法,这就是我们要直接用的Spring容器。
java
package com.example.spring;
/**
* @author heyunlin
* @version 1.0
*/
public class AnnotationConfigApplicationContext<T> implements ApplicationContext<T> {
@Override
public Object getBean(String beanName) {
return null;
}
@Override
public T getBean(Class<T> type) {
return null;
}
@Override
public T getBean(String beanName, Class<T> type) {
return null;
}
}
4、Spring容器代码初步实现
首先,组件扫描需要一个扫描路径,可以通过配置类上的@ComponentScan注解指定,如果不指定,则默认为配置类所在的包。
创建配置类
在当前包下创建一个类,配置包扫描路径。
java
package com.example.spring;
/**
* @author heyunlin
* @version 1.0
*/
@ComponentScan("com.example.spring")
public class SpringConfig {
}
创建自定义的组件注解
@Component
java
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
java
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方法。
java
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;
}
@Override
public 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和单例;
java
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);
}
}
@Override
public Object getBean(String beanName) {
return null;
}
@Override
public T getBean(Class<T> type) {
return null;
}
@Override
public T getBean(String beanName, Class<T> type) {
return null;
}
}
创建一个单例bean------UserService
java
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
java
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
java
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
java
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。
java
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);
// 单例bean
if (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
@Override
public Object getBean(String beanName) {
// 修改的代码-------------------start
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//if (!beanDefinitionMap.containsKey(beanName)) {
if (beanDefinition == null) {
throw new NullPointerException();
}
return getBean(beanName, beanDefinition);
// 修改的代码-------------------end
}
@Override
public T getBean(Class<T> type) {
return null;
}
@Override
public 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);
// 懒加载的单例bean
if (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
java
package com.example.spring;
/**
* @author heyunlin
* @version 1.0
*/
@Component("userMapper")
@Scope("prototype")
public class UserMapper {
}
根据bean名称获取bean功能测试
java
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);
}
}
InitializingBean
InitializingBean是Spring bean生命周期的重要钩子方法之一,在Spring初始化完成后调用。
我们创建一个InitializingBean的实现类,同时使用@Component将该实现类声明为bean。
创建InitializingBean接口
java
package com.example.spring;
/**
* @author heyunlin
* @version 1.0
*/
public interface InitializingBean {
void afterPropertiesSet();
}
创建InitializingBean接口的实现类
java
package com.example.spring;
import com.example.spring.annotation.Component;
/**
* @author heyunlin
* @version 1.0
*/
@Component
public class InitializingBeanImpl implements InitializingBean {
@Override
public void afterPropertiesSet() {
System.out.println("执行InitializingBean.afterPropertiesSet()");
}
}
在创建bean的时候判断该bean是否InitializingBean接口的实现类,如果是,调用其afterPropertiesSet()方法,代码很简单。
java
/**
* 创建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,并设置到@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;
}
@Configuration + @Bean
上面的代码完成了@Component注解的隐式声明的bean的扫描,接下来完成@Bean注解配置的bean扫描。(@Configuration注解标注的类中,@Bean注解标注方法的返回值是bean的类型,方法名就是bean的名称)
创建@Configuration注解
java
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注解
java
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 "";
}
6、使用Spring获取bean对象
在main方法里测试获取单例和非单例bean,其中通过上面三种方法获取到的UserService都是同一个,下半部分获取到的UserMapper都是不一样的(因为它是prototype)。
java
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.class);
Object userService2 = applicationContext.getBean(UserService.class);
Object userService3 = applicationContext.getBean("userService");
Object userService4 = applicationContext.getBean("userService");
Object userService5 = applicationContext.getBean("userService", UserService.class);
Object userService6 = applicationContext.getBean("userService", UserService.class);
System.out.println(userService1);
System.out.println(userService2);
System.out.println(userService3);
System.out.println(userService4);
System.out.println(userService5);
System.out.println(userService6);
System.out.println("----------------------------------------------------");
Object userMapper1 = applicationContext.getBean(UserMapper.class);
Object userMapper2 = applicationContext.getBean(UserMapper.class);
Object userMapper3 = applicationContext.getBean("userMapper");
Object userMapper4 = applicationContext.getBean("userMapper");
Object userMapper5 = applicationContext.getBean("userMapper", UserMapper.class);
Object userMapper6 = applicationContext.getBean("userMapper", UserMapper.class);
System.out.println(userMapper1);
System.out.println(userMapper2);
System.out.println(userMapper3);
System.out.println(userMapper4);
System.out.println(userMapper5);
System.out.println(userMapper6);
}
}
7、未完待续
文章和代码持续更新中,敬请期待~