目录:
- 自定义注解bean
- bean工厂
- 依赖注入&循环依赖
在上一节,自定义注解实现了接口的配置调用,但是我们没有使用到spring的依赖注入及统一bean管理;那么本节我们将来实现这一块的功能
1. 自定义注解@Bean
package com.mp.framework.beans;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyBean {
}
在类上表名有@MyBean注解的,代表我们要把这个bean交由spring来统一管理
2. 自定义注入: @MyAutowired
package com.mp.framework.beans;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
}
在字段上有@MyAutowired注解的,表示我们要做依赖注入,这里要解决的就是循环依赖的问题
3. 核心代码,实现bean的统一管理 MyBeanFactory
package com.mp.framework.beans;
import com.mp.demo.controller.TestController;
import com.mp.framework.annotation.MyController;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* ClassName: MyBeanFactory
* Function: 核心代码,将bean通过map的形式进行保存
* Date: 2020-05-07 15:45
* author mp
* version V1.0
*/
public class MyBeanFactory {
// bean集合
public static Map<Class<?>,Object> beanMaps = new ConcurrentHashMap<>();
// 初始化
public static void init(List<Class<?>> list) {
list.stream().filter(cls -> cls.isAnnotationPresent(MyBean.class) || cls.isAnnotationPresent(MyController.class)) // 只处理 @MyBean 和 @Contrller注解的类
.forEach(createBean);
System.out.println("初始化bean: "+beanMaps);
}
// 创建bean
static Consumer<Class<?>> createBean = cls -> {
// 从bean集合中检查,bean是否已经创建
Object bean = beanMaps.get(cls);
// 获取字段类型,用于判断循环依赖
Field[] fields = cls.getDeclaredFields();
if (bean == null) { // 没有创建
try {
bean = cls.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
beanMaps.put(cls,bean); // 容器管理bean
}
// 检查循环依赖
MyBeanFactory.fieldsBeanTrans.accept(bean,fields);
};
// 检查循环依赖
static BiConsumer<Object,Field[]> fieldsBeanTrans = (bean,fields) -> {
Stream.of(fields).filter(field -> field.isAnnotationPresent(MyAutowired.class))
.forEach(field -> {
Class<?> type = field.getType();
// 检查依赖的bean是否已经创建
Object obj = beanMaps.get(type);
if (obj == null){
// 创建bean
try {
obj = type.newInstance();
beanMaps.put(type,obj);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
field.setAccessible(true);
try {
field.set(bean,obj); // 字段赋值,即为bean设置依赖域,不然autowire 自动注入为null
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
};
// 获取bean
static public Object getBean(Class<?> cls){
return beanMaps.get(cls);
}
}
说明:
(1)依旧利用获取类上注解信息来判断是否需要初始化bean,并统一管理;这里我们只处理@MyBean 和 @MyController两个注解
(2)bean的统一管理,实质是放在一个Map集合中,在初始化bean之前,需要到集合中查询一遍,已确保bean只初始化一次
(3)关于循环依赖,在获取到bean后,通过反射机制,获取到所有的字段,再获取到字段Type,并检查bean集合中是否已经创建该依赖bean;
(4)针对循环依赖,需要设置bean的依赖域,即 field.set(bean,obj),如果不手动设置的话,最后自动引入的话对象字段就为null
4. 接口映射类需要调整MappingHandler, 之前是手动初始化对象,现在改由统一从bean集合中取
package com.mp.framework.handler;
import com.mp.framework.beans.MyBeanFactory;
import org.apache.naming.factory.BeanFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.stream.Stream;
/**
* ClassName: MappingHandler
* Function: TODO
* Date: 2020-05-07 14:12
* author mp
* version V1.0
*/
public class MappingHandler {
private String uri;
private Method method;
private Class<?> cls;
private String[] args;
public MappingHandler(String uri, Method method, Class<?> cls, String[] args) {
this.uri = uri;
this.method = method;
this.cls = cls;
this.args = args;
}
// 执行接口方法
public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
String reqUri = ((HttpServletRequest) req).getRequestURI();
if (!this.uri.equals(reqUri)) return false;
Object[] params = new Object[args.length];
// 获取参数
for (int i=0;i< args.length;i++){
params[i] = req.getParameter(args[i]); // 获取请求参数值
}
// Object obj = this.cls.newInstance();
Object obj = MyBeanFactory.beanMaps.get(this.cls); //统一bean管理
// 执行方法,获取返回值
Object response = this.method.invoke(obj, params);
res.getWriter().write(Optional.ofNullable(response).map(r -> r.toString()).orElse("")); // 针对无返回值类型的接口,需要做null处理
return true;
}
}
5.调整MyApplication启动类
package com.mp.framework.starter;
import com.mp.framework.beans.MyBeanFactory;
import com.mp.framework.core.ClassScanner;
import com.mp.framework.handler.HandlerManager;
import com.mp.framework.web.server.TomcatServer;
import com.mp.framework.web.server.TomcatServerNew;
import org.apache.catalina.LifecycleException;
import java.io.IOException;
import java.util.List;
/**
* ClassName: MyApplication
* Function: TODO
* Date: 2020-05-07 09:44
* author mp
* version V1.0
*/
public class MyApplication {
public static void run(Class<?> cls,String[] args) throws LifecycleException, IOException, ClassNotFoundException {
System.out.println("hello my0-spring application!!!");
// 实例化Tomca t服务
// TomcatServer tomcatServer = new TomcatServer(args);
TomcatServerNew tomcatServer = new TomcatServerNew(args);
tomcatServer.startServer();
// 添加获取类列表和反射调用
List<Class<?>> list = new ClassScanner().doScanner(cls.getPackage().getName());
list.stream().forEach(c -> System.out.println(c.getName()));
// 筛选controller 接口,处理接口映射
HandlerManager.resolveMappingHandler(list);
MyBeanFactory.init(list); // 初始化bean工厂
}
}
MyBeanFactory.init(list); 启动bean统一初始化工作.
6. 重启服务,访问接口 http://localhost:9999/v1/test?name=mp
返回结果: service01: mp
至此: 我们自己写的spring项目已经初具功能,但是还有很大的不足,很多地方考虑不周全,后面我将去读springboot的源码,学习大佬的经验...