手寫web服務(wù)器:定義Configuration和Bean注解,實(shí)現(xiàn)更靈活的類注入

前言
前幾天,我們實(shí)現(xiàn)了Service注解,解決了類注入的問題(component注解后面實(shí)現(xiàn),這個(gè)就很簡單了),但是這種方式不夠靈活,比如我們要實(shí)現(xiàn)某些屬性的賦值,或者其他特殊的構(gòu)建方法,這些注解就不夠靈活了,為了解決這個(gè)問題,spring提供了Configuration注解和Bean注解,今天我們就參照這兩個(gè)注解的功用,用我自己的方式來實(shí)現(xiàn)這兩個(gè)注解,讓我們的類注入更靈活。
話不多說,直接開始。
實(shí)現(xiàn)過程
定義注解
configuration注解,我們從一開始就在寫注解,所以到現(xiàn)在都是閉著眼睛寫的,so easy!
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {
}
Bean注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
核心實(shí)現(xiàn)
我們說的核心實(shí)現(xiàn),主要是指這兩個(gè)注解的使用,因?yàn)?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Bean注解的作用是創(chuàng)建對象,所以必須在服務(wù)器啟動前完成創(chuàng)建,但是不能早于包掃描前,我們要在包掃描完成后進(jìn)行:
// 掃描包
componentScanInit(aClass);
// 初始化配置類
initConfiguration();
我們看下配置類初始化里面是如何實(shí)現(xiàn)的:
/**
* 初始化配置類
*/
private static void initConfiguration() {
classSet.forEach(c -> {
try {
if (hasAnnotation(c, Configuration.class)) {
Method[] methods = c.getMethods();
Object o = c.newInstance();
for (Method method : methods) {
Bean annotation = method.getAnnotation(Bean.class);
if (Objects.nonNull(annotation)) {
Object invoke = method.invoke(o);
contentMap.put(method.getReturnType().getName(), invoke);
}
}
}
} catch (Exception e) {
logger.error("掃描配置類錯誤", e);
}
});
}
這里也很簡單,就是循環(huán)遍歷我們的包掃描結(jié)果,拿出有Configuration注解的類,然后找到有Bean注解的方法,運(yùn)行方法,把方法運(yùn)行結(jié)果放進(jìn)我們的Ioc容器即可。
簡單應(yīng)用
我們有這樣一個(gè)類,我們需要通過Bean和Configuration注解來實(shí)現(xiàn)更靈活的類注入
public class TestBean {
private String name;
private int age;
public TestBean() {
}
public TestBean(String name) {
System.out.println("create bean, " + name);
}
public void testBean() {
System.out.println(this);
System.out.println("hello bean");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "TestBean{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
我們的配置類:
@Configuration
public class TestConfig {
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setAge(18);
testBean.setName("云中志");
return testBean;
}
}
測試
我們在Controller中注入testBean:
@Controller
public class Test2Controller {
@Autowired
private TestService service;
@Autowired
private TestBean testBean;
@RequestMapping("/testAutowire")
public String testAutowire(@RequestParameter("name") String name){
testBean.testBean();
return service.helloIoc(name);
}
}
然后,我們前端訪問下看下效果:

配置類中的屬性也已經(jīng)被注入進(jìn)來了,這種方式的好處就是比較靈活,你可以根據(jù)自己的需要調(diào)用對應(yīng)的方法,實(shí)現(xiàn)符合你需求的構(gòu)建方式,是不是很簡單呀!
總結(jié)
今天的內(nèi)容總體來說還是比較簡單的,核心的點(diǎn)就是注解的解析和方法的反射調(diào)用,當(dāng)然難點(diǎn)也是有的,你要在實(shí)現(xiàn)之前考慮好思路,因?yàn)榇a本身只是思路的表達(dá),所以在我的認(rèn)知理解中,我覺得合格的軟件工程師,首先得是個(gè)合格的架構(gòu)師和設(shè)計(jì)師,否則你真的是能當(dāng)個(gè)小碼農(nóng)了,遇到需求的時(shí)候,先想想如果讓你來做,你會如何實(shí)現(xiàn),而不是考慮該不該你來做,每一次你排斥的事情,其實(shí)對你而言,都是一次機(jī)會,重要的是你如何看待它。
好了,今天的內(nèi)容就到這里吧。
下面是項(xiàng)目的開源倉庫,有興趣的小伙伴可以去看看,如果有想法的小伙伴,我真心推薦你自己動個(gè)手,自己寫一下,真的感覺不錯:
https://github.com/Syske/syske-boot

