相关系列
在以上详细讲解了spring底层核心原理,今天我们就来手写一个spring框架。主流程是:
1、编写启动类
2、依据ComponentScan设置的value属性进行扫描路径下的文件
3、根据扫描得到的文件,判断是否加了Component注解,如果加了,则添加到beanDefinitionMap
4、编写DzendApplicationContext中的getBean方法,判断是单例bean还是原型bean,单例bean则到singletonObjects中去获取,如果为null,则创建;如果是原型bean,则创建;把刚获取的bean返回,就拿到了bean
第一步编写启动类Test.java
这个类是应用程序的主入口,用于程序的启动
package com.dzend;
import com.dzend.service.UserService;
import com.spring.DzendApplicationContext;
public class Test {
public static void main(String[] args) {
//扫描 ->创建对象
DzendApplicationContext applicationContext = new DzendApplicationContext(AppConfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
}
}
第二步编写配置类AppConfig.java
是一个配置类,用以配置要扫描的文件路径。
package com.dzend;
import com.spring.ComponentScan;
@ComponentScan("com.dzend.service")
public class AppConfig {
}
第三步,根据第二步用了@ComponentScan注解,创建ComponentScan注解
用来配置扫描路径的注解。
package com.spring;
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 ComponentScan {
String value() default "";
}
Component.java
用于配置要加入到BeanDefinitionMap 中,也就是管理这个service类的,比如缓存。
package com.spring;
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 Component {
String value() default "";
}
Scope.java
用于标注是单例bean,还是原型bean。在创建bean的时候,如果是单例bean,则从singletonObjects对象获取,如果值为null,则创建这个对象,并放入到 singletonObjects对象中。如果为原型bean,则每次获取的时候都动态的创建新类并返回。
package com.spring;
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)
public @interface Scope {
String value();
}
第四步编写DzendApplicationContext.java文件
这是一个核心文件,用来工作的,核心就是扫描路径,创建 singletonObjects。
package com.spring;
import java.beans.Introspector;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DzendApplicationContext {
private Class<?> configClass;
private Map<String,BeanDefinition> beanDefinitionMap= new HashMap<>();
private Map<String, Object> singletonObjects=new HashMap<>();
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
public DzendApplicationContext(Class configClass) {
this.configClass=configClass;
scan(configClass);
}
private void scan(Class configClass) {
if(configClass.isAnnotationPresent(ComponentScan.class)){
ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = componentScan.value();
path = path.replace(".","/");
ClassLoader classLoader = DzendApplicationContext.class.getClassLoader();
URL resource = classLoader.getResource(path);
try {
path = URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
File file = null;
try {
file = new File( URLDecoder.decode(resource.getFile(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
if(file.isDirectory()){
for (File f : file.listFiles()) {
String absolutePath = f.getAbsolutePath();
absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
absolutePath=absolutePath.replace("\\",".");
try {
Class<?> clazz = classLoader.loadClass(absolutePath);
if (clazz.isAnnotationPresent(Component.class)) {
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance();
beanPostProcessorList.add(instance);
}
Component componentAnnotaion = clazz.getAnnotation(Component.class);
String beanName= componentAnnotaion.value();
if("".equals(beanName)){
beanName = Introspector.decapitalize(clazz.getSimpleName());
}
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(clazz);
if (clazz.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
String value = scopeAnnotation.value();
beanDefinition.setScope(value);
}else{
beanDefinition.setScope("singleton");
}
beanDefinitionMap.put(beanName,beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
}
}
public Object getBean(String beanName){
if(!beanDefinitionMap.containsKey(beanName)){
throw new NullPointerException();
}
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if(beanDefinition.getScope().equals("singleton")){
Object singletonObject = singletonObjects.get(beanName);
if(singletonObject == null){
singletonObject = createBean(beanName,beanDefinition);
singletonObjects.put(beanName,singletonObject);
}
return singletonObject;
}else{
//原型
Object prototypeBean = createBean(beanName, beanDefinition);
return prototypeBean;
}
}
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class clazz = beanDefinition.getType();
Object instance = null;
try {
instance = clazz.getConstructor().newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
return instance;
}
}
编写相关service类
定义相关的业务类,并加上Component注解用来标注是不是要放入spring容器中缓存。
OrderService.java
package com.dzend.service;
import com.spring.Autowired;
import com.spring.Component;
@Component(value = "orderService")
public class OrderService {
public void test(){
System.out.println("Hello world!!!");
}
}
UserService.java
package com.dzend.service;
import com.spring.Autowired;
import com.spring.BeanNameAware;
import com.spring.Component;
@Component(value = "userService")
public class UserService {
@Autowired
private OrderService orderService;
public OrderService getOrderService() {
return orderService;
}
}