Handwritten an HTTP framework: two classes implement basic IOC functions

JavaGuide 2020-11-13 10:05:17
handwritten http framework classes implement


jsoncat: Imitation Spring Boot But it's different from Spring Boot A lightweight of HTTP frame

On National Day , I've already jsoncat Of IoC The function is written , See this article for details 《 Handwriting “SpringBoot” recent developments :IoC The module has been completed 》 .

Today, I'd like to share with you my own writing IoC The idea and specific code implementation .

 Insert picture description here

IoC (Inverse of Control: Inversion of control ) and AOP(Aspect-Oriented Programming: Section oriented programming ) Can be said to be Spring The framework provides two core functions . As long as you know Spring Little buddy , That must be very, very familiar with these two concepts . I don't know you , You can see 《 The interview was asked hundreds of times IoC and AOP , I still don't know ?》 This article is easy to understand .

Considering that this article should be handwritten Spring Framework of the IoC function , therefore , I'd like to briefly introduce IoC . If you don't know IoC The concept , Be sure to understand and then look at the following specific code implementation link .

IoC Introduce

IoC(Inverse of Control: Inversion of control ) It's a kind of design idea , That is to say Hand over control of objects that were created manually in the program Spring Framework to manage . IoC It's also used in other languages , Is not Spring specific .

IoC Containers

**IoC Containers are used to implement IoC The carrier of , Managed objects are stored in IoC In the container .**IoC The container in Spring It's actually a Map(key,value),Map There are various managed objects in .

IoC What problems have been solved

Give the interdependence of objects to IoC Container to manage , And by the IoC The container completes the injection of objects . This can greatly simplify the development of applications , Free applications from complex dependencies . IoC The container is like a factory , When we need to create an object , Just configure the configuration file / Annotations can be , It doesn't matter how objects are created . In a real project Service A class can have hundreds or even thousands of classes at its bottom , If we need to instantiate this Service, You may have to figure this out every time Service Constructors of all underlying classes , It can drive people crazy . If the use of IoC Words , You just need to configure , Then quote where you need it , This greatly increases the maintainability of the project and reduces the development difficulty .

IoC and DI Don't be so stupid as to tell

IoC(Inverse of Control: Inversion of control ) It's a kind of design idea Or a pattern . The design idea is Control of objects created manually in the program , Leave it to Spring Framework to manage . IoC It's also used in other languages , Is not Spring specific .IoC The container is Spring Used to implement IoC The carrier of , IoC The container is actually a Map(key,value),Map There are various managed objects in .

IoC The most common and reasonable implementation is called dependency injection (Dependency Injection, abbreviation DI).

also , Old horse (Martin Fowler) It is mentioned in an article that will IoC Renamed as DI, The text is as follows , Original address :https://martinfowler.com/articles/injection.html .

IoC Realize the idea

Be careful : The following ideas do not address the problem of circular dependency !

Before you start code implementation , Let's talk about implementation IoC The idea of , After getting the train of thought clear , It's very simple to implement .

  1. Scan specific annotations under the specified package, such as @Component Marked class , And save these classes .
  2. Traverses all annotations that are specified, such as @Component Marked class , These will be instantiated through a class Map Save up ,Map Of key For class name ,value For class objects .
  3. Once again, go through all the annotations that are specified, such as @Component Marked class , And get all the fields in the class , If the class is @Autowired Note the words marked , On the first 4 Step .
  4. By field name key, from bean Get the corresponding object in the container value.
  5. Determine whether the acquired object is an interface . If it's an interface , You need to get the implementation class corresponding to the interface , Then, the instantiated object of the specified implementation class is assigned to the specified object through reflection . If it's not an interface , It directly assigns the acquired object to the specified object through reflection .

IoC Implement core code

Core notes

@Autowired : Object of comment

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

}

@Component : Declare that the object is IoC Container management


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {

String name() default "";
}

@Qualifier: Specify the injected bean( When the interface has multiple implementation classes, you need to use )

@Target({
ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {

String value() default "";
}

Tool class

Simply encapsulate a reflection tool class . The tool class contains 3 The method that will be used later :

  1. scanAnnotatedClass() : Scan the class marked by the specified annotation under the specified package ( Use Reflections This reflection framework can solve the problem of scanning the class with specified annotation in one line of code ).
  2. newInstance() : Pass in Class You can return Class The corresponding object .
  3. setField() : Assign a value to the specified field of the object .
@Slf4j
public class ReflectionUtil {

/**
* scan the classes marked by the specified annotation in the specified package
*
* @param packageName specified package name
* @param annotation specified annotation
* @return the classes marked by the specified annotation in the specified package
*/
public static Set<Class<?>> scanAnnotatedClass(String packageName, Class<? extends Annotation> annotation) {

Reflections reflections = new Reflections(packageName, new TypeAnnotationsScanner());
Set<Class<?>> annotatedClass = reflections.getTypesAnnotatedWith(annotation, true);
log.info("The number of class Annotated with @RestController :[{}]", annotatedClass.size());
return annotatedClass;
}
/**
* create object instance through class
*
* @param cls target class
* @return object created by the target class
*/
public static Object newInstance(Class<?> cls) {

Object instance = null;
try {

instance = cls.newInstance();
} catch (InstantiationException | IllegalAccessException e) {

log.error("new instance failed", e);
}
return instance;
}
/**
* set the value of a field in the object
*
* @param obj target object
* @param field target field
* @param value the value assigned to the field
*/
public static void setField(Object obj, Field field, Object value) {

field.setAccessible(true);
try {

field.set(obj, value);
} catch (IllegalAccessException e) {

log.error("set field failed", e);
e.printStackTrace();
}
}
}

Write code according to the idea

Be careful : The following code does not address the problem of resolving circular dependencies ! Here are IoC The core code of the implementation , Full code address :https://github.com/Snailclimb/jsoncat .

1. Scan specific annotations under the specified package, such as @Component Marked class , And save these classes .

Scan specified annotations @RestController and @Component And save it :

public class ClassFactory {

public static final Map<Class<? extends Annotation>, Set<Class<?>>> CLASSES = new ConcurrentHashMap<>();
//1. Scan specific annotations under the specified package, such as `@Component` Marked class , And save these classes 
public static void loadClass(String packageName) {

Set<Class<?>> restControllerSets = ReflectionUtil.scanAnnotatedClass(packageName, RestController.class);
Set<Class<?>> componentSets = ReflectionUtil.scanAnnotatedClass(packageName, Component.class);
CLASSES.put(RestController.class, restControllerSets);
CLASSES.put(Component.class, componentSets);
}
}

2. Traverses all annotations that are specified, such as @Component Marked class , These will be instantiated through a class Map Save up ,Map Of key For class name ,value For class objects .

public final class BeanFactory {

public static final Map<String, Object> BEANS = new ConcurrentHashMap<>(128);
public static void loadBeans() {

// 2. Traverses all annotations that are specified, such as @Component Marked class , These will be instantiated through a class Map Save up ,Map Of key For class name ,value For class objects 
ClassFactory.CLASSES.forEach((annotation, classes) -> {

if (annotation == Component.class) {

// take bean Instantiation , And put in bean In the container 
for (Class<?> aClass : classes) {

Component component = aClass.getAnnotation(Component.class);
String beanName = "".equals(component.name()) ? aClass.getName() : component.name();
Object obj = ReflectionUtil.newInstance(aClass);
BEANS.put(beanName, obj);
}
}
if (annotation == RestController.class) {

for (Class<?> aClass : classes) {

Object obj = ReflectionUtil.newInstance(aClass);
BEANS.put(aClass.getName(), obj);
}
}
});
}
}

3. Once again, go through all the annotations that are specified, such as @Component Marked class , And get all the fields in the class , If the class is @Autowired Note the words marked , On the first 4 Step .

public class DependencyInjection {

public static void dependencyInjection(String packageName) {

Map<String, Object> beans = BeanFactory.BEANS;
if (beans.size() == 0) return;
//3. Once again, go through all the annotations that are specified, such as @Component Marked class , And get all the fields in the class , If the class is `@Autowired` Note the words marked , On the first 4 Step .
// 3.1. Traverse bean All objects in the container 
beans.values().forEach(bean -> {

// 3.2. Get all the fields declared by the class to which the object belongs / attribute 
Field[] beanFields = bean.getClass().getDeclaredFields();
if (beanFields.length == 0) return;
//3.3. Traverses all fields declared by the class to which the object belongs / attribute 
for (Field beanField : beanFields) {

//3.4. Determine whether the field is @Autowired Comment mark 
if (beanField.isAnnotationPresent(Autowired.class)) {

//4. By field name key, from bean Get the corresponding object in the container value.
//4.1. The type of the field 
Class<?> beanFieldClass = beanField.getType();
//4.2. The class name corresponding to the field 
String beanName = beanFieldClass.getName();
if (beanFieldClass.isAnnotationPresent(Component.class)) {

Component component = beanFieldClass.getAnnotation(Component.class);
beanName = "".equals(component.name()) ? beanFieldClass.getName() : component.name();
}
//4.3. from bean Get the corresponding object in the container 
Object beanFieldInstance = beans.get(beanName);
//5. Determine whether the acquired object is an interface . If it's an interface , You need to get the implementation class corresponding to the interface , Then, the instantiated object of the specified implementation class is assigned to the specified object through reflection . If it's not an interface , It directly assigns the acquired object to the specified object through reflection .
if (beanFieldClass.isInterface()) {

// If it's an interface , Get the implementation class corresponding to the interface 
Set<Class<?>> subClasses = getSubClass(packageName, beanFieldClass);
// If there is no implementation class, an exception will be thrown 
if (subClasses.size() == 0) {

throw new InterfaceNotHaveImplementedClassException("interface does not have implemented class exception");
}
// The implementation class has only one word , Direct access to 
if (subClasses.size() == 1) {

Class<?> aClass = subClasses.iterator().next();
beanFieldInstance = ReflectionUtil.newInstance(aClass);
}
// If more than one class is implemented , according to Qualifier Get the value of the annotation 
if (subClasses.size() > 1) {

Class<?> aClass = subClasses.iterator().next();
Qualifier qualifier = beanField.getDeclaredAnnotation(Qualifier.class);
beanName = qualifier == null ? aClass.getName() : qualifier.value();
beanFieldInstance = beans.get(beanName);
}
}
// If the field object finally obtained is null, Throw an exception 
if (beanFieldInstance == null) {

throw new CanNotDetermineTargetBeanException("can not determine target bean");
}
// Set the value of the specified field in the specified object by reflection 
ReflectionUtil.setField(bean, beanField, beanFieldInstance);
}
}
});
}
/**
* Get the implementation class corresponding to the interface
*/
@SuppressWarnings("unchecked")
public static Set<Class<?>> getSubClass(String packageName, Class<?> interfaceClass) {

Reflections reflections = new Reflections(packageName);
return reflections.getSubTypesOf((Class<Object>) interfaceClass);
}
}

I put together a high quality original PDF Free sharing of resources to everyone , Most of the content is my original , A little bit from friends .

image-20201012105608336

Download address :https://cowtransfer.com/s/fbed14f0c22a4d .

版权声明
本文为[JavaGuide]所创,转载请带上原文链接,感谢

  1. [front end -- JavaScript] knowledge point (IV) -- memory leakage in the project (I)
  2. This mechanism in JS
  3. Vue 3.0 source code learning 1 --- rendering process of components
  4. Learning the realization of canvas and simple drawing
  5. gin里获取http请求过来的参数
  6. vue3的新特性
  7. Get the parameters from HTTP request in gin
  8. New features of vue3
  9. vue-cli 引入腾讯地图(最新 api,rocketmq原理面试
  10. Vue 学习笔记(3,免费Java高级工程师学习资源
  11. Vue 学习笔记(2,Java编程视频教程
  12. Vue cli introduces Tencent maps (the latest API, rocketmq)
  13. Vue learning notes (3, free Java senior engineer learning resources)
  14. Vue learning notes (2, Java programming video tutorial)
  15. 【Vue】—props属性
  16. 【Vue】—创建组件
  17. [Vue] - props attribute
  18. [Vue] - create component
  19. 浅谈vue响应式原理及发布订阅模式和观察者模式
  20. On Vue responsive principle, publish subscribe mode and observer mode
  21. 浅谈vue响应式原理及发布订阅模式和观察者模式
  22. On Vue responsive principle, publish subscribe mode and observer mode
  23. Xiaobai can understand it. It only takes 4 steps to solve the problem of Vue keep alive cache component
  24. Publish, subscribe and observer of design patterns
  25. Summary of common content added in ES6 + (II)
  26. No.8 Vue element admin learning (III) vuex learning and login method analysis
  27. Write a mini webpack project construction tool
  28. Shopping cart (front-end static page preparation)
  29. Introduction to the fluent platform
  30. Webpack5 cache
  31. The difference between drop-down box select option and datalist
  32. CSS review (III)
  33. Node.js学习笔记【七】
  34. Node.js learning notes [VII]
  35. Vue Router根据后台数据加载不同的组件(思考-&gt;实现-&gt;不止于实现)
  36. Vue router loads different components according to background data (thinking - & gt; Implementation - & gt; (more than implementation)
  37. 【JQuery框架,Java编程教程视频下载
  38. [jQuery framework, Java programming tutorial video download
  39. Vue Router根据后台数据加载不同的组件(思考-&gt;实现-&gt;不止于实现)
  40. Vue router loads different components according to background data (thinking - & gt; Implementation - & gt; (more than implementation)
  41. 【Vue,阿里P8大佬亲自教你
  42. 【Vue基础知识总结 5,字节跳动算法工程师面试经验
  43. [Vue, Ali P8 teaches you personally
  44. [Vue basic knowledge summary 5. Interview experience of byte beating Algorithm Engineer
  45. 【问题记录】- 谷歌浏览器 Html生成PDF
  46. [problem record] - PDF generated by Google browser HTML
  47. 【问题记录】- 谷歌浏览器 Html生成PDF
  48. [problem record] - PDF generated by Google browser HTML
  49. 【JavaScript】查漏补缺 —数组中reduce()方法
  50. [JavaScript] leak checking and defect filling - reduce() method in array
  51. 【重识 HTML (3),350道Java面试真题分享
  52. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  53. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  54. [re recognize HTML (3) and share 350 real Java interview questions
  55. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  56. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  57. 【重识 HTML ,nginx面试题阿里
  58. 【重识 HTML (4),ELK原来这么简单
  59. [re recognize HTML, nginx interview questions]
  60. [re recognize HTML (4). Elk is so simple