动手实现Spring的IOC功能(基于注解)
文件结构
设计技术
主要设计两大技能点:反射,单例模式
实现过程
首先从启动spring开始,启动spring需要一个ApplicationContext的类,这个类中传入一个配置类,这个配置类主要是说明了包扫描的路径
创建一个配备了包扫描路径的类,扫描com/ssm/service下面的文件
AppConfig文件
@IComponentScan("com.ssm.service")
public class AppConfig {
}
还需要实现@IComponentScan的注解
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IComponentScan {
String value() default "";
}
另外再写两个Service,Service中需要三类注解,所有需要自己先实现这三类注解:@IComponent注解,@IAutowired注解和@Scope注解
实现@IComponent注解,主要作用是标注一个类为spring容器的bean,等下我需要讲这个类实例化
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IComponent {
String value() default "";
}
实现@IAutowired注解,主要是在类中引入xxxservice实例
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.METHOD})
//可用在字段和构造方法和普通方法上面
public @interface IAutowired {
}
实现@Scope注解,我主要实现它的两种方式
singleton单例模式:全局有且仅有一个实例,默认为单例模式
prototype原型模式:每次获取bean的时候都会有一个新实例
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
String value() default "singleton";
}
UserService文件如下
@IComponent("userService")
public class UserService {
}
OrderService文件如下
先不用关注实现的接口InitializingBean和BeanNameAware,主要是OrderService中使用到@IAutowired注解拿到UserService对象
import com.spring.*;
/**
* @author ssm
*/
@IComponent("orderService")
@Scope("prototype")
public class OrderService implements InitializingBean, BeanNameAware {
@IAutowired
private UserService userService;
//这个beanName主要想要实现spring能够自动将OrderService这个Bean的名字(即Component里面的名字)赋值给这个beanName
private String beanName;
public void test(){
//这里测试是看输出的这个userservice是否为空,如果不为空就说明我们写的依赖注入功能实现了
System.out.println(userService);
}
@Override
public void afterPropertiesSet() {
//bean在生成的过程当中会调用这个方法
System.out.println("初始化");
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
//测试是否在生成bean时候自动赋值了beanName
System.out.println("beanName: "+beanName);
}
}
最重要的部分是ApplicationContext的内容
我在代码中做了详细的注释,主要就是:
扫描配置类,通过配置类上面的IComponent注解得到扫描的路径,扫描这个路径下的所有文件并且得到每个文件的Class对象,讲Class对象存入List,从List中一个一个解析类,这里的解析类主要是看类上面是否有IComponent注解,解析得到每个类的相关信息(BeanName,BeanClass,Scope),将每个类的相关信息存入beanDefinitionMap,beanDefinitionMap是map结构,key是BeanName,value是BeanDefinition,BeanDefinition是每个Bean相关的信息
生成bean实例化,主要使用了反射调用构造方法,即beanClass.getDeclaredConstructor()生成实例,用beanClass.getDeclaredFields()来判断类中的成员变量上是否有@IAutowired注解,如果有该注解则去调用getBean(beanName)方法,该方法从beanDefinitionMap中取bean的信息来生成bean对象,getBean方法中拿到Bean实例后将其赋值给字段上加了@IAutowired注解的对象
import com.ssm.service.UserService;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author ssm
*/
public class SSMApplicationContext {
//主要保存了bean的信息,比如保存了class,scope,beanName
private ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
//单例池,主要存着实例化出来的单例对象
private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
public SSMApplicationContext(Class configClass){
//构造方法主要是思考spring启动的时候需要做什么?
//1:扫描类
//2:创建bean
// 思考:spring启动是所有bean都要创建吗?主要创建哪种类型的bean呢?
// 是非懒加载的单例!启动spring的时候就创建bean
//2:第二个步骤概括就是要生成单例bean,并且要把生成的bean放入单例池中
//扫描到类之后要干什么?解析这个类,具体解析些什么信息,比如有component注解
//扫描配置文件下的类,得到所有类对象
List<Class> classList = scan(configClass);
for (Class clazz : classList) {
//一个一个解析类,将所有类对象的基本信息存入beanDefinition
//ConcurrentHashMap<String ,BeanDefinition> beanDefinitionMap,key是beanName,value是BeanDefinition(主要是scope和beanClass)
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClass(clazz);
IComponent component = (IComponent) clazz.getAnnotation(IComponent.class);
String beanName = component.value();
if (clazz.isAnnotationPresent(Scope.class)){
Scope scope = (Scope) clazz.getAnnotation(Scope.class);
beanDefinition.setScope(scope.value());
}else {
beanDefinition.setScope("singleton");
}
//判断扫描IComponent的类是不是实现了beanPostProcessor
if (BeanPostProcessor.class.isAssignableFrom(clazz)){
//判断clazz是否是BeanPostProcessor的实现类/子类
try {
BeanPostProcessor bpp = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
beanPostProcessorList.add(bpp);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
beanDefinitionMap.put(beanName,beanDefinition);
}
for (String beanName: beanDefinitionMap.keySet()){
//beanDefinitionMap里面存着bean实例化的基本信息
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("singleton")){
//生成这个bean(和bean的生命周期有关,如下)
Object bean = createBean(beanName,beanDefinition);
singletonObjects.put(beanName,bean);//把创建出来的bean放入单例池中保存起来
}
}
}
private Object createBean(String beanName, BeanDefinition beanDefinition) {
//生成这个bean(和bean的生命周期有关,如下)
Class beanClass = beanDefinition.getBeanClass();
try {
//实例化(用class调用构造方法来进行实例化)
Object bean = beanClass.getDeclaredConstructor().newInstance();
//填充属性
Field[] fields = beanClass.getDeclaredFields();//思考:类中的什么属性是需要填充的
for (Field field : fields) {
if (field.isAnnotationPresent(IAutowired.class)){
//在字段上判断是否加了Autowired注解
//思考:存在加了Autowired注解的字段,那我填充属性,拿什么东西填充呢?填充对象是?
//OrderService类中userService加了注解,那我应该拿一个UserService的对象去给这个加了注解的userservice赋值
// UserService userService = (UserService) getBean(field.getName());
Object userService = getBean(field.getName());
field.setAccessible(true);//反射中必须设置true才可以通过反射访问字段,才能给实例化赋值
field.set(bean,userService);//用userService给这个实例化的bean赋值
}
}
//Aware
if (bean instanceof BeanNameAware){
//bean是否实现了BeanNameAwaren接口
//实现了则将bean进行强制类型转化
((BeanNameAware)bean).setBeanName(beanName);
}
//...可实现程序员定义的逻辑
//可以实现多个BeanPostProcessor,所以循环
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean,beanName);
}
//初始化(instanceof只能针对实例来写,不能用于针对类来写)
if (bean instanceof InitializingBean){
//bean是否实现了InitializingBean接口
//实现了则将bean进行强制类型转化
((InitializingBean)bean).afterPropertiesSet();
}
//...可实现程序员定义的逻辑
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessAfterInitialization(bean,beanName);
}
return bean;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
public Object getBean(String beanName){
//还需要提供一个getBean方法,返回值是Object
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (beanDefinition.getScope().equals("prototype")){
return createBean(beanName,beanDefinition);
}else {
//单例生成,从单例池中拿对象
Object bean = singletonObjects.get(beanName);
if (bean == null){
Object o = createBean(beanName,beanDefinition);
singletonObjects.put(beanName,o);
return o;
}
return bean;
}
}
private List<Class> scan(Class configClass) {
List<Class> classList = new ArrayList<>();
//扫描类,主要是为了得到扫描的路径
//如何得到扫描路径,先拿到Annocation注解,因为我们知道是IComponentScan注解,
// 所以这里进行了强转,然后调用value得到了扫描的路径
IComponentScan iComponentScan = (IComponentScan) configClass.getAnnotation(IComponentScan.class);
String scanPath = iComponentScan.value();
// System.out.println(scanPath); com.ssm.service
//其实扫描是为了扫描目录,但scanPath只是得到了路径,真的目录的格式应该是com/ssm/service,所以需要转换scanPath
scanPath = scanPath.replace(".","/");//粗暴转为目录格式,替换.为/
//思考:如何扫描类(使用类加载器,调用类加载器上的getResource来获得)
ClassLoader classLoader = SSMApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(scanPath);
// System.out.println(resource);
//resource在没有转为文件目录的时候输出是null,转为文件目录以后输出是 file:/C:/Users/SSM/Desktop/exercise/my_spring/target/classes/com/ssm/service
File file = new File(resource.getFile());
File[] files = file.listFiles();//扫描目录下所有的文件,什么格式都会扫描进来
for (File f : files) {
//System.out.println(f);
//C:\Users\SSM\Desktop\exercise\my_spring\target\classes\com\ssm\service\OrderService.class
//C:\Users\SSM\Desktop\exercise\my_spring\target\classes\com\ssm\service\UserService.class
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
//System.out.println(absolutePath);
//再次把斜线变成点
absolutePath = absolutePath.replace("\\",".");//com.ssm.service.OrderService
try {
Class<?> clazz = classLoader.loadClass(absolutePath);//加载类
classList.add(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return classList;
}
}
BeanDefinition文件如下:主要用来存储bean信息的
public class BeanDefinition {
private String scope;
private Class beanClass;
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
}
在上面的ApplicationContext中有以下代码单独摘出来如下:
//Aware
if (bean instanceof BeanNameAware){
//bean是否实现了BeanNameAwaren接口
//实现了则将bean进行强制类型转化
((BeanNameAware)bean).setBeanName(beanName);
}
//...可实现程序员定义的逻辑
//可以实现多个BeanPostProcessor,所以循环
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean,beanName);
System.out.println(bean+"------------------------beanPostProcessor");
}
//初始化(instanceof只能针对实例来写,不能用于针对类来写)
if (bean instanceof InitializingBean){
//bean是否实现了InitializingBean接口
//实现了则将bean进行强制类型转化
((InitializingBean)bean).afterPropertiesSet();
}
//...可实现程序员定义的逻辑
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessAfterInitialization(bean,beanName);
System.out.println(bean+"------------------------beanPostProcessor");
}
这里参考bean的生命周期,这里主要实现了Aware接口,bean初始化,BeanPostProcessor接口,这部分代码省略