一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Spring boot實現(xiàn)一個簡單的ioc(2)

Spring boot實現(xiàn)一個簡單的ioc(2)

2020-09-21 15:34clayanddev Java教程

這篇文章主要為大家詳細(xì)介紹了Spring boot實現(xiàn)一個簡單ioc的第二篇,具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

跳過廢話,直接看正文
仿照spring-boot的項目結(jié)構(gòu)以及部分注解,寫一個簡單的ioc容器。
測試代碼完成后,便正式開始這個ioc容器的開發(fā)工作。

正文

項目結(jié)構(gòu)

Spring boot實現(xiàn)一個簡單的ioc(2)

實際上三四個類完全能搞定這個簡單的ioc容器,但是出于可擴(kuò)展性的考慮,還是寫了不少的類。
因篇幅限制,接下來只將幾個最重要的類的代碼貼出來并加以說明,完整的代碼請直接參考https://github.com/clayandgithub/simple-ioc

SimpleAutowired

代碼

?
1
2
3
4
5
6
7
8
9
10
import java.lang.annotation.*;
 
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SimpleAutowired {
 boolean required() default true;
 
 String value() default ""; // this field is moved from @Qualifier to here for simplicity
}

說明

@SimpleAutowired的作用是用于注解需要自動裝配的字段。
此類和spring的@Autowired的作用類似。但又有以下兩個區(qū)別:
- @SimpleAutowired只能作用于類字段,而不能作用于方法(這樣實現(xiàn)起來相對簡單些,不會用到aop)
- @SimpleAutowired中包括了required(是否一定需要裝配)和value(要裝配的bean的名字)兩個字段,實際上是將spring中的@Autowired以及Qualifier的功能簡單地融合到了一起

SimpleBean

代碼

?
1
2
3
4
5
6
7
8
import java.lang.annotation.*;
 
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SimpleBean {
 String value() default "";
}

說明

@SimpleBean作用于方法,根據(jù)方法返回值來生成一個bean,對應(yīng)spring中的@Bean
用value來設(shè)置要生成的bean的名字

SimpleComponent

代碼

?
1
2
3
4
5
6
7
8
import java.lang.annotation.*;
 
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SimpleBean {
 String value() default "";
}

說明

@SimpleComponent作用于類,ioc容器會為每一個擁有@SimpleComponent的類生成一個bean,對應(yīng)spring中的@Component。特殊說明,為了簡單起見,@SimpleComponent注解的類必須擁有一個無參構(gòu)造函數(shù),否則無法生成該類的實例,這個在之后的SimpleAppliationContext中的processSingleClass方法中會有說明。

SimpleIocBootApplication

代碼

?
1
2
3
4
5
6
7
8
import java.lang.annotation.*;
 
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SimpleIocBootApplication {
 String[] basePackages() default {};
}

說明

@SimpleIocBootApplication作用于應(yīng)用的入口類。
這個啟動模式是照搬了spring-boot的啟動模式,將啟動任務(wù)委托給SimpleIocApplication來完成。ioc容器將根據(jù)注解@SimpleIocBootApplication的相關(guān)配置自動掃描相應(yīng)的package,生成beans并完成自動裝配。(如果沒有配置,默認(rèn)掃描入口類(測試程序中的SampleApplication)所在的package及其子package)

以上就是這個ioc容器所提供的所有注解,接下來講解ioc容器的掃描和裝配過程的實現(xiàn)。

SimpleIocApplication

代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import com.clayoverwind.simpleioc.context.*;
import com.clayoverwind.simpleioc.util.LogUtil;
 
import java.util.Arrays;
import java.util.Map;
import java.util.logging.Logger;
 
 
public class SimpleIocApplication {
 private Class<?> applicationEntryClass;
 
 private ApplicationContext applicationContext;
 
 private final Logger LOGGER = LogUtil.getLogger(this.getClass());
 
 public SimpleIocApplication(Class<?> applicationEntryClass) {
 this.applicationEntryClass = applicationEntryClass;
 }
 
 public static void run(Class<?> applicationEntryClass, String[] args) {
 new SimpleIocApplication(applicationEntryClass).run(args);
 }
 
 public void run(String[] args) {
 LOGGER.info("start running......");
 
 // create application context and application initializer
 applicationContext = createSimpleApplicationContext();
 ApplicationContextInitializer initializer = createSimpleApplicationContextInitializer(applicationEntryClass);
 
 // initialize the application context (this is where we create beans)
 initializer.initialize(applicationContext); // here maybe exist a hidden cast
 
 // process those special beans
 processSpecialBeans(args);
 
 LOGGER.info("over!");
 }
 
 private SimpleApplicationContextInitializer createSimpleApplicationContextInitializer(Class<?> entryClass) {
 // get base packages
 SimpleIocBootApplication annotation = entryClass.getDeclaredAnnotation(SimpleIocBootApplication.class);
 String[] basePackages = annotation.basePackages();
 if (basePackages.length == 0) {
  basePackages = new String[]{entryClass.getPackage().getName()};
 }
 
 // create context initializer with base packages
 return new SimpleApplicationContextInitializer(Arrays.asList(basePackages));
 }
 
 private SimpleApplicationContext createSimpleApplicationContext() {
 return new SimpleApplicationContext();
 }
 
 private void processSpecialBeans(String[] args) {
 callRegisteredRunners(args);
 }
 
 private void callRegisteredRunners(String[] args) {
 Map<String, SimpleIocApplicationRunner> applicationRunners = applicationContext.getBeansOfType(SimpleIocApplicationRunner.class);
 try {
  for (SimpleIocApplicationRunner applicationRunner : applicationRunners.values()) {
  applicationRunner.run(args);
  }
 } catch (Exception e) {
  throw new RuntimeException(e);
 }
 }
}

說明

前面說到應(yīng)用的啟動會委托SimpleIocApplication來完成,通過將應(yīng)用入口類(測試程序中的SampleApplication)傳入SimpleIocApplication的構(gòu)造函數(shù),構(gòu)造出SimpleIocApplication的一個實例并運(yùn)行run方法。在run方法中,會首先生成一個applicationContext,并調(diào)用SimpleApplicationContextInitializer來完成applicationContext的初始化(bean的掃描、裝配)。然后調(diào)用processSpecialBeans來處理一些特殊的bean,如實現(xiàn)了SimpleIocApplicationRunner接口的bean會調(diào)用run方法來完成一些應(yīng)用程序的啟動任務(wù)。
這就是這個ioc容器的整個流程。

SimpleApplicationContextInitializer

代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
 
public class SimpleApplicationContextInitializer implements ApplicationContextInitializer<SimpleApplicationContext> {
 
  private Set<String> basePackages = new LinkedHashSet<>();
 
  public SimpleApplicationContextInitializer(List<String> basePackages) {
    this.basePackages.addAll(basePackages);
  }
 
  @Override
  public void initialize(SimpleApplicationContext applicationContext) {
    try {
      applicationContext.scan(basePackages, true);
    } catch (ClassNotFoundException e) {
      throw new RuntimeException(e);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    applicationContext.setStartupDate(System.currentTimeMillis());
  }
}

說明

在SimpleIocApplication的run中,會根據(jù)basePackages來構(gòu)造一個SimpleApplicationContextInitializer 的實例,進(jìn)而通過這個ApplicationContextInitializer來完成SimpleApplicationContext 的初始化。
在SimpleApplicationContextInitializer中, 簡單地調(diào)用SimpleApplicationContext 中的scan即可完成SimpleApplicationContext的初始化任務(wù)

SimpleApplicationContext

說明:

終于到了最重要的部分了,在SimpleApplicationContext中將真正完成掃描、生成bean以及自動裝配的任務(wù)。這里scan即為SimpleApplicationContext的程序入口,由SimpleApplicationContextInitializer在初始化時調(diào)用。
代碼的調(diào)用邏輯簡單易懂,就不多加說明了。
這里只簡單列一下各個字段的含義以及幾個比較關(guān)鍵的方法的作用。

字段

- startupDate:啟動時間記錄字段
- scannedPackages:已經(jīng)掃描的包的集合,保證不重復(fù)掃描
- registeredBeans:已經(jīng)完全裝配好并注冊好了的bean
- earlyBeans : 只是生成好了,還未裝配完成的bean,用于處理循環(huán)依賴的問題
- totalBeanCount : 所有bean的計數(shù)器,在生成bean的名字時會用到其唯一性

方法

- processEarlyBeans:用于最終裝配earlyBeans 中的bean,若裝配成功,則將bean移至registeredBeans,否則報錯
- scan : 掃描并處理傳入的package集合
- processSingleClass:處理單個類,嘗試生成該類的bean并進(jìn)行裝配(前提是此類有@SimpleComponent注解)
- createBeansByMethodsOfClass : 顧名思義,根據(jù)那些被@Bean注解的方法來生成bean
- autowireFields:嘗試裝配某個bean,lastChance代表是否在裝配失敗是報錯(在第一次裝配時,此值為false,在裝配失敗后會將bean移至earlyBeans,在第二次裝配時,此值為true,實際上就是在裝配earlyBeans中的bean,因此若仍然裝配失敗,就會報錯)。在這個方法中,裝配相應(yīng)的bean時會從registeredBeans以及earlyBeans中去尋找符合條件的bean,只要找到,不管是來自哪里,都算裝配成功。

代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
import com.clayoverwind.simpleioc.context.annotation.SimpleAutowired;
import com.clayoverwind.simpleioc.context.annotation.SimpleBean;
import com.clayoverwind.simpleioc.context.annotation.SimpleComponent;
import com.clayoverwind.simpleioc.context.factory.Bean;
import com.clayoverwind.simpleioc.util.ClassUtil;
import com.clayoverwind.simpleioc.util.ConcurrentHashSet;
import com.clayoverwind.simpleioc.util.LogUtil;
 
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;
 
/**
 * @author clayoverwind
 * @E-mail [email protected]
 * @version 2017/4/5
 */
 
public class SimpleApplicationContext implements ApplicationContext {
 
 private long startupDate;
 
 private Set<String> scannedPackages = new ConcurrentHashSet<>();
 
 private Map<String, Bean> registeredBeans = new ConcurrentHashMap<>();
 
 private Map<String, Bean> earlyBeans = new ConcurrentHashMap<>();
 
 private final Logger LOGGER = LogUtil.getLogger(this.getClass());
 
 AtomicLong totalBeanCount = new AtomicLong(0L);
 
 AtomicLong nameConflictCount = new AtomicLong(0L);
 
 @Override
 public Object getBean(String name) {
 return registeredBeans.get(name);
 }
 
 @Override
 public <T> T getBean(String name, Class<T> type) {
 Bean bean = (Bean)getBean(name);
 return bean == null ? null : (type.isAssignableFrom(bean.getClazz()) ? type.cast(bean.getObject()) : null);
 }
 
 @Override
 public <T> T getBean(Class<T> type) {
 Map<String, T> map = getBeansOfType(type);
 return map.isEmpty() ? null : type.cast(map.values().toArray()[0]);
 }
 
 @Override
 public boolean containsBean(String name) {
 return getBean(name) != null;
 }
 
 @Override
 public <T> Map<String, T> getBeansOfType(Class<T> type) {
 Map<String, T> res = new HashMap<>();
 registeredBeans.entrySet().stream().filter(entry -> type.isAssignableFrom(entry.getValue().getClazz())).forEach(entry -> res.put(entry.getKey(), type.cast(entry.getValue().getObject())));
 return res;
 }
 
 @Override
 public void setStartupDate(long startupDate) {
 this.startupDate = startupDate;
 }
 
 @Override
 public long getStartupDate() {
 return startupDate;
 }
 
 /**
 * try to autowire those beans in earlyBeans
 * if succeed, remove it from earlyBeans and put it into registeredBeans
 * otherwise ,throw a RuntimeException(in autowireFields)
 */
 private synchronized void processEarlyBeans() {
 for (Map.Entry<String, Bean> entry : earlyBeans.entrySet()) {
  Bean myBean = entry.getValue();
  try {
  if (autowireFields(myBean.getObject(), myBean.getClazz(), true)) {
   registeredBeans.put(entry.getKey(), myBean);
   earlyBeans.remove(entry.getKey());
  }
  } catch (IllegalAccessException e) {
  throw new RuntimeException(e);
  }
 }
 }
 
 /**
 * scan base packages and create beans
 * @param basePackages
 * @param recursively
 * @throws ClassNotFoundException
 */
 public void scan(Set<String> basePackages, boolean recursively) throws ClassNotFoundException, IOException {
 LOGGER.info("start scanning......");
 
 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
 
 // get all classes who haven't been registered
 Set<Class<?>> classes = new LinkedHashSet<>();
 for (String packageName : basePackages) {
  if (scannedPackages.add(packageName)) {
  classes.addAll(ClassUtil.getClassesByPackageName(classLoader, packageName, recursively));
  }
 }
 
 // autowire or create bean for each class
 classes.forEach(this::processSingleClass);
 
 processEarlyBeans();
 
 LOGGER.info("scan over!");
 }
 
 /**
 * try to create a bean for certain class, put it into registeredBeans if success, otherwise put it into earlyBeans
 * @param clazz
 */
 private void processSingleClass(Class<?> clazz) {
 LOGGER.info(String.format("processSingleClass [%s] ...", clazz.getName()));
 
 Annotation[] annotations = clazz.getDeclaredAnnotations();
 for (Annotation annotation : annotations) {
  if (annotation instanceof SimpleComponent) {
  Object instance;
  try {
   instance = clazz.newInstance();
  } catch (InstantiationException e) {
   throw new RuntimeException(e);
  } catch (IllegalAccessException e) {
   throw new RuntimeException(e);
  }
 
  long beanId = totalBeanCount.getAndIncrement();
  SimpleComponent component = (SimpleComponent) annotation;
  String beanName = component.value();
  if (beanName.isEmpty()) {
   beanName = getUniqueBeanNameByClassAndBeanId(clazz, beanId);
  }
 
  try {
   if (autowireFields(instance, clazz, false)) {
   registeredBeans.put(beanName, new Bean(instance, clazz));
   } else {
   earlyBeans.put(beanName, new Bean(instance, clazz));
   }
  } catch (IllegalAccessException e) {
   throw new RuntimeException(e);
  }
 
  try {
   createBeansByMethodsOfClass(instance, clazz);
  } catch (InvocationTargetException e) {
   throw new RuntimeException(e);
  } catch (IllegalAccessException e) {
   throw new RuntimeException(e);
  }
  }
 }
 }
 
 private void createBeansByMethodsOfClass(Object instance, Class<?> clazz) throws InvocationTargetException, IllegalAccessException {
 List<Method> methods = getMethodsWithAnnotation(clazz, SimpleBean.class);
 for (Method method : methods) {
  method.setAccessible(true);
  Object methodBean = method.invoke(instance);
  long beanId = totalBeanCount.getAndIncrement();
  Class<?> methodBeanClass = methodBean.getClass();
 
  //bean name
  SimpleBean simpleBean = method.getAnnotation(SimpleBean.class);
  String beanName = simpleBean.value();
  if (beanName.isEmpty()) {
  beanName = getUniqueBeanNameByClassAndBeanId(clazz, beanId);
  }
 
  // register bean
  registeredBeans.put(beanName, new Bean(methodBean, methodBeanClass));
 }
 }
 
 private List<Method> getMethodsWithAnnotation(Class<?> clazz, Class<?> annotationClass) {
 List<Method> res = new LinkedList<>();
 Method[] methods = clazz.getDeclaredMethods();
 for (Method method : methods) {
  Annotation[] annotations = method.getAnnotations();
  for (Annotation annotation : annotations) {
  if (annotation.annotationType() == annotationClass) {
   res.add(method);
   break;
  }
  }
 }
 return res;
 }
 
 
 /**
 * try autowire all fields of a certain instance
 * @param instance
 * @param clazz
 * @param lastChance
 * @return true if success, otherwise return false or throw a exception if this is the lastChance
 * @throws IllegalAccessException
 */
 private boolean autowireFields(Object instance, Class<?> clazz, boolean lastChance) throws IllegalAccessException {
 Field[] fields = clazz.getDeclaredFields();
 for (Field field : fields) {
  Annotation[] annotations = field.getAnnotations();
  for (Annotation annotation : annotations) {
  if (annotation instanceof SimpleAutowired) {
   SimpleAutowired autowired = (SimpleAutowired) annotation;
   String beanName = autowired.value();
   Bean bean = getSimpleBeanByNameOrType(beanName, field.getType(), true);
   if (bean == null) {
   if (lastChance) {
    if (!autowired.required()) {
    break;
    }
    throw new RuntimeException(String.format("Failed in autowireFields : [%s].[%s]", clazz.getName(), field.getName()));
   } else {
    return false;
   }
   }
   field.setAccessible(true);
   field.set(instance, bean.getObject());
  }
  }
 }
 return true;
 }
 
 /**
 * only used in autowireFields
 * @param beanName
 * @param type
 * @param allowEarlyBean
 * @return
 */
 private Bean getSimpleBeanByNameOrType(String beanName, Class<?> type, boolean allowEarlyBean) {
 // 1. by name
 Bean res = registeredBeans.get(beanName);
 if (res == null && allowEarlyBean) {
  res = earlyBeans.get(beanName);
 }
 
 // 2. by type
 if (type != null) {
  if (res == null) {
  res = getSimpleBeanByType(type, registeredBeans);
  }
  if (res == null && allowEarlyBean) {
  res = getSimpleBeanByType(type, earlyBeans);
  }
 }
 
 return res;
 }
 
 /**
 * search bean by type in certain beans map
 * @param type
 * @param beansMap
 * @return
 */
 private Bean getSimpleBeanByType(Class<?> type, Map<String, Bean> beansMap) {
 List<Bean> beans = new LinkedList<>();
 beansMap.entrySet().stream().filter(entry -> type.isAssignableFrom(entry.getValue().getClazz())).forEach(entry -> beans.add(entry.getValue()));
 if (beans.size() > 1) {
  throw new RuntimeException(String.format("Autowire by type, but more than one instance of type [%s] is founded!", beans.get(0).getClazz().getName()));
 }
 return beans.isEmpty() ? null : beans.get(0);
 }
 
 private String getUniqueBeanNameByClassAndBeanId(Class<?> clazz, long beanId) {
 String beanName = clazz.getName() + "_" + beanId;
 while (registeredBeans.containsKey(beanName) || earlyBeans.containsKey(beanName)) {
  beanName = clazz.getName() + "_" + beanId + "_" + nameConflictCount.getAndIncrement();
 }
 return beanName;
 }
}

后記

至此,一個簡單的ioc容器就完成了,總結(jié)一下優(yōu)缺點。

優(yōu)點:

小而簡單。
可以使用@SimpleBean、@SimpleComponent以及@SimpleAutowired 來完成一些簡單但常用的依賴注入任務(wù).

缺點:

很明顯,實現(xiàn)過于簡單,提供的功能太少。
如果你想了解ioc的實現(xiàn)原理,或者你想要開發(fā)一個小型個人項目但又嫌spring過于龐大,這個簡單的ioc容器或許可以幫到你。

如果你想做的不僅如此,那么你應(yīng)該將目光轉(zhuǎn)向spring-boot

完整代碼參考:https://github.com/clayandgithub/simple-ioc

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美午夜寂寞影院安卓列表 | 亚洲国产高清视频 | 天堂在线中文字幕 | sao虎影院桃红视频在线观看 | 22222色男人的天堂 | 波多野结衣一区 | 亚洲人成影院午夜网站 | 欧洲男同直粗无套播放视频 | 我的年轻漂亮继坶三级 | 男女做污事 | 国产精品福利久久2020 | 91国内在线国内在线播放 | 成在线人免费视频一区二区三区 | 日本漫画大全之工之口 | 欧美日韩精品免费一区二区三区 | 欧洲网色偷偷亚洲男人的天堂 | 丝瓜视频在线观看污 | 日韩成人在线网站 | 欧美黑人性猛交╳xx╳动态图 | 丝袜足控免费网站xx动漫漫画 | 日韩亚洲人成网站在线播放 | chinesegay黑袜玩奴 | 国产精品福利在线观看免费不卡 | 无码AV精品一区二区三区 | 国产一二在线观看视频网站 | 国产福利片在线 易阳 | 欧美图片另类小说综合 | 男人狂擦女人的下面视频 | 人配人种视频xxxx | xxoo做爰猛烈动态 | 免费一级欧美片在线观看 | 精品综合久久久久久88小说 | 四虎影视地址 | 91肥熟国产老肥熟在线 | 色帝国亚洲欧美在线蜜汁tv | 北条麻妃黑人正在播放 | 国产麻豆在线观看网站 | 北条麻妃黑人正在播放 | 免费高清资源黄网站在线观看 | 91美女在线观看 | 视频在线免费看 |