国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring手寫模擬源碼篇(你值得擁有)

這篇具有很好參考價(jià)值的文章主要介紹了Spring手寫模擬源碼篇(你值得擁有)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

概念篇

下面是本文章關(guān)于Spring底層原理的章節(jié)

Spring手寫模擬源碼篇(你值得擁有)

Bean的創(chuàng)建的生命周期

類-》推斷構(gòu)造方法-》根據(jù)構(gòu)造方法創(chuàng)建普通對(duì)象-》依賴注入(@Autowired等進(jìn)行屬性注入)-》初始化前(@PostConstruct)->初始化(InitializingBean)-》初始化后(AOP)-》代理對(duì)象(沒(méi)有開啟AOP就會(huì)把普通對(duì)象放入單例池)-》放入單例池-》Bean對(duì)象

依賴注入

在Spring容器中創(chuàng)建了一個(gè)普通對(duì)象后,如果這個(gè)對(duì)象有類似于@Autowired注解的屬性,如何給這個(gè)屬性賦值呢?這里利用的是反射的機(jī)制,在創(chuàng)建完一個(gè)普通對(duì)象后,利用反射機(jī)制看有沒(méi)有@Autowird注解的屬性,如果依賴注入的bean為單例,首先從單例池中尋找,找到就賦值注入,找不到就創(chuàng)建然后注入屬性,如果這個(gè)bean為多例,就會(huì)直接new 一個(gè)對(duì)象出來(lái)然后賦值。這個(gè)具體可以看下面的模擬代碼進(jìn)行深入理解。

推斷構(gòu)造方法

在Spring容器中使用構(gòu)造方法創(chuàng)建對(duì)象的時(shí)候默認(rèn)采用無(wú)參構(gòu)造方法。在Spring容器中創(chuàng)建對(duì)象是通過(guò)反射根據(jù)構(gòu)造方法進(jìn)行創(chuàng)建的,至于具體根據(jù)哪個(gè)構(gòu)造方法進(jìn)行創(chuàng)建對(duì)象,內(nèi)容如下:

1.只有一個(gè)構(gòu)造方法,那么實(shí)例化就只能使用這個(gè)構(gòu)造方法了。有參的話(前提是根據(jù)參數(shù)類型或者名字可以找到唯一的bean。
2.有多個(gè)構(gòu)造方法,不管構(gòu)造方法參數(shù)是一個(gè)還是多個(gè),那么Spring會(huì)去找默認(rèn)的無(wú)參的構(gòu)造方法,找不到則報(bào)錯(cuò)。
3.多個(gè)構(gòu)造方法,并且開發(fā)者指定了想使用的構(gòu)造方法,那么就用這個(gè)構(gòu)造方法
通過(guò)@Autowired注解,@Autowired注解可以寫在構(gòu)造方法上,所以哪個(gè)構(gòu)造方法上寫了@Autowired注解,表示開發(fā)者想使用哪個(gè)構(gòu)造方法。通過(guò)@Autowired注解的方式,需要Spring通過(guò)byType+byName的方式去找到符合條件的bean作為構(gòu)造方法的參數(shù)值,當(dāng)然找不到是要報(bào)錯(cuò)的。通過(guò)byType找如果只有一個(gè)就使用該Bean對(duì)象,如果有多個(gè)再根據(jù)名字去找,Spring容器在尋找過(guò)程中是根據(jù)參數(shù)名作為名字去尋找的,找不到則報(bào)錯(cuò)。這個(gè)類似于@Autowired注解,一開始根據(jù)類型去尋找,如果有多個(gè),再根據(jù)屬性名去找對(duì)應(yīng)的是該名字的Bean對(duì)象。

@PostConstruct

如果想要在對(duì)象初始化之前執(zhí)行該對(duì)象中的一些方法,可以在該對(duì)象方法上加上@PostConstruct注解。在Spring容器中初始化之前執(zhí)行有該注解的方法。

初始化

Spring容器中對(duì)于對(duì)象的初始化可以通過(guò)繼承 InitializingBean 接口重寫?afterPropertiesSet() 方法,在此方法里面執(zhí)行自己的初始化的業(yè)務(wù)邏輯。有關(guān)代碼如下:

@Component("test")
public class Test implements InitializingBean {
    public  void hello(){
        System.out.println("執(zhí)行方法");
    }

    @PostConstruct
    public void go(){
        System.out.println("初始化之前");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化");
    }
}

執(zhí)行結(jié)果如下圖:

Spring手寫模擬源碼篇(你值得擁有)

AOP

AOP簡(jiǎn)介

這里先對(duì)AOP進(jìn)行簡(jiǎn)單的介紹

AOP (Aspect?Orient Programming),直譯過(guò)來(lái)就是 面向切面編程,AOP 是一種編程思想,是面向?qū)ο缶幊蹋∣OP)的一種補(bǔ)充。面向切面編程,實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加額外功能的一種技術(shù)。AOP可以攔截指定的方法并且對(duì)方法增強(qiáng),而且無(wú)需侵入到業(yè)務(wù)代碼中,使業(yè)務(wù)與非業(yè)務(wù)處理邏輯分離,比如Spring的事務(wù),通過(guò)事務(wù)的注解配置,Spring會(huì)自動(dòng)在業(yè)務(wù)方法中開啟、提交業(yè)務(wù),并且在業(yè)務(wù)處理失敗時(shí),執(zhí)行相應(yīng)的回滾策略。AOP可以攔截指定的方法并且對(duì)方法增強(qiáng),而且無(wú)需侵入到業(yè)務(wù)代碼中,使業(yè)務(wù)與非業(yè)務(wù)處理邏輯分離,比如Spring的事務(wù),通過(guò)事務(wù)的注解配置,Spring會(huì)自動(dòng)在業(yè)務(wù)方法中開啟、提交業(yè)務(wù),并且在業(yè)務(wù)處理失敗時(shí),執(zhí)行相應(yīng)的回滾策略。

這里采用SpringBoot整合AOP實(shí)例代碼如下:

導(dǎo)入依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

導(dǎo)入配置類

@Configuration
@ComponentScan("com.example.demo.test")
@EnableAspectJAutoProxy
public class AspectConfiguration {

}

導(dǎo)入切面類

@Aspect
@Component
public class MyAspect {

    @Pointcut("execution(* com.example.demo.test.*.*(..))")
    public void myPointCut(){

    }
    //前置通知
    @Before("myPointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("前置通知");
        System.out.println("目標(biāo)類對(duì)象"+joinPoint.getTarget()+"被增強(qiáng)的方法"+joinPoint.getSignature().getName());
    }
    //后置返回通知
    @AfterReturning("myPointCut()")
    public void afterreturn(JoinPoint joinPoint){
        System.out.println("后置返回通知");
        System.out.println("目標(biāo)類對(duì)象"+joinPoint.getTarget()+"被增強(qiáng)的方法"+joinPoint.getSignature().getName());
    }

      //環(huán)繞通知,返回值類型為Object,必須有一參數(shù)是ProceedingJoinPoint
    @Around("myPointCut()")
    public Object aroud(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("環(huán)繞開始,模擬開啟事務(wù)");
       //執(zhí)行當(dāng)前方法
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("環(huán)繞結(jié)束,模擬關(guān)閉事務(wù)");
        return proceed;
    }

    //異常通知
    @AfterThrowing(value = "myPointCut()",throwing = "e")
    public void except(Throwable e){
        System.out.println("異常通知"+e.getMessage());
    }

    //后置最終通知
    @After("myPointCut()")
    public void after(JoinPoint joinPoint){
        System.out.println("最終通知");
    }



}

執(zhí)行代碼

public class hello {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext=new AnnotationConfigApplicationContext(AspectConfiguration.class);
        Test test = (Test) annotationConfigApplicationContext.getBean("test");
        test.hello();
    }
}

結(jié)果如下圖所示

Spring手寫模擬源碼篇(你值得擁有)

?這里對(duì)于Spring5通知的執(zhí)行順序進(jìn)行簡(jiǎn)單的總結(jié),注意Spring4通知的執(zhí)行順序和Spring5不一樣。

程序執(zhí)行正常:

1、環(huán)繞通知前
2、@Before通知
3、程序邏輯
4、@AfterReturning通知
5、@After通知
6、環(huán)繞通知后

程序執(zhí)行異常:

1、環(huán)繞通知前
2、@Before通知
3、@AfterThrowing異常通知
4、@After通知
異常日志

AOP原理

對(duì)于上面的代碼我們?cè)趫?zhí)行test.hello的時(shí)候我們的對(duì)象是Test對(duì)象嗎?還是代理對(duì)象?經(jīng)過(guò)debug我們來(lái)看一下。如下圖證實(shí)我們拿到的對(duì)象是代理對(duì)象。

Spring手寫模擬源碼篇(你值得擁有)

?注意在Spring容器中假如真實(shí)對(duì)象中有類似@Autowired注解進(jìn)行依賴注入的時(shí)候,我們?cè)谶@里debug拿到的代理對(duì)象關(guān)于這樣的屬性實(shí)際上是空的,但是直接運(yùn)行的時(shí)候?qū)嶋H上又會(huì)獲得依賴注入對(duì)象,這是什么原因呢?

在Spring容器中代理類其實(shí)是真實(shí)類的子類,通過(guò)extends繼承,既然代理類是真實(shí)類的子對(duì)象,那么他們之間是怎么實(shí)現(xiàn)的呢?實(shí)現(xiàn)方法之一如下:

public class My extends Test {
    @Override
    public void hello() {
        //執(zhí)行切面邏輯
        super.hello();
    }
}

這樣可以正確的實(shí)現(xiàn)嗎?其實(shí)是不行,假入Test類中有依賴注入的屬性,然后My代理類執(zhí)行父類的時(shí)候,在執(zhí)行方法中的有依賴注入的屬性其實(shí)是空的,因?yàn)楦割悇?chuàng)建了一個(gè)對(duì)象并為這個(gè)屬性賦值,它的子類并不會(huì)獲得該屬性的值的。那解決辦法呢?那就是在My類中創(chuàng)建一個(gè)Test類對(duì)象的屬性Target,并把真實(shí)類賦值給Target屬性,然后在執(zhí)行方法中執(zhí)行Target.hello方法就可以了。

Spring的事務(wù)

Spring聲明式事務(wù)管理是通過(guò)AOP技術(shù)實(shí)現(xiàn)的事務(wù)管理,其本質(zhì)是對(duì)方法前后進(jìn)行攔截,在目標(biāo)方法開始之前創(chuàng)建一個(gè)事務(wù),在目標(biāo)方法執(zhí)行之后,根據(jù)執(zhí)行情況提交或回滾事務(wù)。

事務(wù)在邏輯上是一組操作,要么執(zhí)行,要不都不執(zhí)行。主要是針對(duì)數(shù)據(jù)庫(kù)而言的,為了保證事務(wù)是正確可靠的,在數(shù)據(jù)庫(kù)進(jìn)行寫入或者更新操作時(shí),就必須得表現(xiàn)出 ACID 的 4 個(gè)重要特性:
原子性(Atomicity):一個(gè)事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過(guò)程中發(fā)生錯(cuò)誤,會(huì)被回滾(Rollback)到事務(wù)開始前的狀態(tài),就像這個(gè)事務(wù)從來(lái)沒(méi)有執(zhí)行過(guò)一樣。
一致性(Consistency):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫(kù)的完整性沒(méi)有被破壞。
事務(wù)隔離(Isolation):數(shù)據(jù)庫(kù)允許多個(gè)并發(fā)事務(wù)同時(shí)對(duì)其數(shù)據(jù)進(jìn)行讀寫和修改,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。
持久性(Durability):事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失。

Spring事務(wù)其實(shí)就是在上一個(gè)AOP內(nèi)容中切面邏輯中實(shí)現(xiàn)的,在開啟事務(wù)后,Spring容器會(huì)用事務(wù)管理器新建一個(gè)數(shù)據(jù)庫(kù)連接,并且一開始設(shè)置autocommint=false,然后執(zhí)行普通對(duì)象中的相關(guān)方法如果沒(méi)有拋異常就會(huì)提交,否則就會(huì)回滾。然后下面我們來(lái)分析個(gè)案例代碼如下

@Component()
public class Test{


    @Transactional
    public  void hello(){
        System.out.println("good");
        a();
    }

    @Transactional(propagation = Propagation.NEVER)
    public void a(){
        System.out.println("a");
    }
}

@SpringBootApplication
@MapperScan("com.example.demo.mybatisplus")
@EnableTransactionManagement
public class DemoApplication {


    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args);
        Test bean = run.getBean(Test.class);
        bean.hello();
    }

}

PROPAGATION_NEVER該傳播機(jī)制不支持外層事務(wù),即如果外層有事務(wù)就拋出異常,當(dāng)我們獲取Test Bean對(duì)象的時(shí)候,然后執(zhí)行hello方法的時(shí)候他會(huì)拋出異常嗎?實(shí)際上是不會(huì)的,那到底是什么原因呢?我們通過(guò)上面的內(nèi)容我們知道了Spring事務(wù)是在代理類中的相關(guān)方法的代理邏輯中執(zhí)行的,在執(zhí)行方法切面邏輯的時(shí)候是由我們的代理對(duì)象執(zhí)行事務(wù)管理器相關(guān)操作的,而a方法實(shí)際的執(zhí)行的是普通對(duì)象并不是代理對(duì)象,普通對(duì)象在執(zhí)行a方法的時(shí)候是不會(huì)有事務(wù)的操作的,注解僅僅就成為了一個(gè)擺設(shè)。如果有興趣的小伙伴可以嘗試一下。那么怎么解決問(wèn)題呢?當(dāng)有@Transactional的類就會(huì)在Spring容器中生成代理對(duì)象放到單例池當(dāng)中,那么可以在這個(gè)對(duì)象中使用@Autowired注解依賴注入代理對(duì)象,把a(bǔ)方法放在這個(gè)代理對(duì)象對(duì)應(yīng)的普通類中,然后通過(guò)這個(gè)依賴注入的代理對(duì)象調(diào)用a方法,就可以正常的解決。還有一個(gè)辦法就是如下面代碼也可以正常的解決。

@Transactional
public  void hello(){
    System.out.println("good");
    Test o = (Test) AopContext.currentProxy();
    o.a();
}

循環(huán)依賴

這里引用

被 Spring 管理的對(duì)象叫做 Bean 。Bean的生成步驟如下:

Spring 掃描 class 得到 BeanDefinition;
根據(jù)得到的 BeanDefinition 去生成 bean;
首先根據(jù) class 推斷構(gòu)造方法;
根據(jù)推斷出來(lái)的構(gòu)造方法,反射,得到一個(gè)對(duì)象(暫時(shí)叫做原始對(duì)象);
填充原始對(duì)象中的屬性(依賴注入);
如果原始對(duì)象中的某個(gè)方法被 AOP 了,那么則需要根據(jù)原始對(duì)象生成一個(gè)代理對(duì)象;
把最終生成的代理對(duì)象放入單例池(源碼中叫做 singletonObjects)中,下次 getBean 時(shí)就直接從單例池拿即可;
對(duì)于 Spring 中的 Bean 的生成過(guò)程,步驟還是很多的,并且不僅僅只有上面的7步,還有很多很多,這里不詳細(xì)說(shuō)了。

我們可以發(fā)現(xiàn),在得到一個(gè)原始對(duì)象后,Spring 需要給對(duì)象中的屬性進(jìn)行依賴注入,那么這個(gè)注入過(guò)程是怎樣的?

比如上文說(shuō)的 A 類,A 類中存在一個(gè) B 類的 b 屬性,所以,當(dāng) A 類生成了一個(gè)原始對(duì)象之后,就會(huì)去給 b 屬性去賦值,此時(shí)就會(huì)根據(jù) b 屬性的類型和屬性名去 BeanFactory 中去獲取 B 類所對(duì)應(yīng)的單例bean。

1. 如果此時(shí) BeanFactory 中存在 B 對(duì)應(yīng)的 Bean,那么直接拿來(lái)賦值給 b 屬性;
2. 如果此時(shí) BeanFactory 中不存在 B 對(duì)應(yīng)的 Bean,則需要生成一個(gè) B 對(duì)應(yīng)的 Bean,然后賦值給 b屬性。

問(wèn)題就出現(xiàn)在「第二種」情況,如果此時(shí) B 類在 BeanFactory 中還沒(méi)有生成對(duì)應(yīng)的 Bean,那么就需要去生成,就會(huì)經(jīng)過(guò) B 的 Bean 的生命周期。

那么在創(chuàng)建 B 類的 Bean 的過(guò)程中,如果 B 類中存在一個(gè) A 類的 a 屬性,那么在創(chuàng)建 B 的 Bean 的過(guò)程中就需要 A 類對(duì)應(yīng)的 Bean,但是,觸發(fā) B 類 Bean 的創(chuàng)建的條件是 A 類 Bean 在創(chuàng)建過(guò)程中的依賴注入,所以這里就出現(xiàn)了循環(huán)依賴:

A Bean創(chuàng)建–>依賴了 B 屬性–>觸發(fā) B Bean創(chuàng)建—>B 依賴了 A 屬性—>需要 A Bean(但A Bean還在創(chuàng)建過(guò)程中)

從而導(dǎo)致 A Bean 創(chuàng)建不出來(lái),B Bean 也創(chuàng)建不出來(lái)。

這里就用到三級(jí)緩存了,這里設(shè)置兩個(gè)類Aservice,Bservice。Aservice中有Bservice屬性的注入,Bservice中有Aservice屬性的注入。那么三級(jí)緩存是如何解決問(wèn)題的呢?這里先對(duì)三級(jí)緩存進(jìn)行簡(jiǎn)單的描述。

「singletonObjects」:緩存某個(gè) beanName 對(duì)應(yīng)的經(jīng)過(guò)了完整生命周期的bean也就是我們的單例池;
「earlySingletonObjects」:緩存提前拿原始對(duì)象進(jìn)行了 AOP 之后得到的代理對(duì)象,原始對(duì)象還沒(méi)有進(jìn)行屬性注入和后續(xù)的 BeanPostProcesso r等生命周期;
「singletonFactories」:緩存的是一個(gè) ObjectFactory ,主要用來(lái)去生成原始對(duì)象進(jìn)行了 AOP之后得到的「代理對(duì)象」,在每個(gè) Bean 的生成過(guò)程中,都會(huì)提前暴露一個(gè)工廠,這個(gè)工廠可能用到,也可能用不到,如果沒(méi)有出現(xiàn)循環(huán)依賴依賴本 bean,那么這個(gè)工廠無(wú)用,本 bean 按照自己的生命周期執(zhí)行,執(zhí)行完后直接把本 bean 放入 singletonObjects 中即可,如果出現(xiàn)了循環(huán)依賴依賴了本 bean,則另外那個(gè) bean 執(zhí)行 ObjectFactory 提交得到一個(gè) AOP 之后的代理對(duì)象(如果有 AOP 的話,如果無(wú)需 AOP ,則直接得到一個(gè)原始對(duì)象)。

那么如何打破循環(huán)依賴呢?

摘用網(wǎng)上的圖片

Spring手寫模擬源碼篇(你值得擁有)

Aservice ?在Spring容器創(chuàng)建過(guò)程中,在實(shí)例化后把Aservice普通對(duì)象放在緩存中,然后進(jìn)行Bservice屬性的依賴注入,首先從單例池中尋找Bservice,如果找到就會(huì)賦值,找不到就會(huì)創(chuàng)建Bservice,在進(jìn)行Aservice注入的時(shí)候從單例池尋找,找不到然后從緩存中尋找進(jìn)行屬性的注入。此時(shí)循環(huán)依賴問(wèn)題得以解決。因?yàn)樵谡麄€(gè)過(guò)程中AService都是單例的 , 所以即使從緩存中拿到的AService的原始對(duì)象也沒(méi)有關(guān)系 , 因?yàn)樵诤罄m(xù)的Bean生命周期中 ,AService在堆內(nèi)存中沒(méi)有發(fā)生變化。這種情況當(dāng)Aservice對(duì)象沒(méi)有AOP的時(shí)候這種情況是沒(méi)有問(wèn)題的,如果Aservice類有AOP,從上文可知那么單例池中的該對(duì)象是代理對(duì)象,而我們?cè)贐service中依賴注入的Aservice是普通對(duì)象,這顯而易見就有問(wèn)題了。

所以就需要二級(jí)緩存了,在Bservice進(jìn)行Aservice屬性注入的時(shí)候,要進(jìn)行提前AOP,而上面的緩存就相當(dāng)于三級(jí)緩存存儲(chǔ)原始對(duì)象,出現(xiàn)循環(huán)依賴后從二級(jí)緩存earlySingletonObjects中獲取如果獲取不到對(duì)應(yīng)的對(duì)象,然后就會(huì)從三級(jí)緩存中獲取原始對(duì)象,如果是AOP就生成代理對(duì)象,不是就是普通對(duì)象然后放在二級(jí)緩存中,此時(shí)這個(gè)對(duì)象還不能放入單例池中,為什么呢?假如這里是個(gè)代理對(duì)象,代理對(duì)象的Target原始對(duì)象還沒(méi)有完成生命周期屬性還沒(méi)有完全注入完成,如果在這里放入單例池,在多線程環(huán)境下在這時(shí)從單例池中獲取這個(gè)bean對(duì)象就會(huì)發(fā)生不可預(yù)期的錯(cuò)誤。當(dāng)Bservice Bean對(duì)象創(chuàng)建完成后然后在Aservice中填充完所有屬性后,就可以從二級(jí)緩存中獲取該對(duì)象然后放到單例池中了。

手寫源碼篇

通過(guò)手寫模擬,了解Spring的底層源碼啟動(dòng)過(guò)程

通過(guò)手寫模擬,了解掃描邏輯和依賴注入等底層源碼工作流程

通過(guò)手寫模擬,了解初始化機(jī)制工作流程

通過(guò)手寫模擬,了解BeanDefinition、BeanPostProcessor的概念

通過(guò)手寫模擬,了解Spring AOP的底層源碼工作流程

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

Spring手寫模擬源碼篇(你值得擁有)

啟動(dòng)代碼

public class test {
    public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        ApplicationContext applicationContext = new ApplicationContext(AppConfig.class);
        UserInterface userservice = (UserInterface) applicationContext.getBean("userservice");
        userservice.test();

    }
}

運(yùn)行代碼如下圖

Spring手寫模擬源碼篇(你值得擁有)

Component注解

這里其余的注解代碼省略

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value() default "";
}

ApplicationContext類

這個(gè)類主要是掃描@ComponentScan指定的包路徑生成其中類含有@Componet注解的Bean對(duì)象。

public class ApplicationContext {


    private Class configClass;
    private ConcurrentHashMap<String,Object> singletonObjects=new ConcurrentHashMap<>();//單列池
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionConcurrentHashMap=new ConcurrentHashMap<>();
    private List<BeanPostProcessor> beanPostProcessorList=new ArrayList<>();

    public ApplicationContext(Class configClass) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        this.configClass = configClass;
        scan(configClass);
        for (Map.Entry<String, BeanDefinition> stringBeanDefinitionEntry : beanDefinitionConcurrentHashMap.entrySet()) {
            String beanName = stringBeanDefinitionEntry.getKey();
            BeanDefinition beanDefinition = stringBeanDefinitionEntry.getValue();
            if(beanDefinition.getScope().equals("singleton")){
                Object bean = createBean(beanDefinition);
                singletonObjects.put(beanName,bean);
            }
        }
    }

    //創(chuàng)造Bean對(duì)象
    public Object createBean(BeanDefinition beanDefinition){
        Class clazz = beanDefinition.getClazz();
        Object o = null;
        try {
            o = clazz.getDeclaredConstructor().newInstance();
            for (Field declaredField : clazz.getDeclaredFields()) {
                if(declaredField.isAnnotationPresent(Autowired.class)){
                    Object bean = getBean(declaredField.getName());
                    declaredField.setAccessible(true);
                    declaredField.set(o,bean);//此處有bug,當(dāng)singleton時(shí)注入失敗為null,當(dāng)注入的屬性對(duì)象bean在單例池排序靠前可以成功。
                }
            }//getFields為獲得所有public的屬性,這里為所有
            //后置處理器,這里沒(méi)有對(duì)beanName進(jìn)行設(shè)計(jì)
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessBeforeInitialization(o,null);
            }

            //下面為初始化機(jī)制
            if (o instanceof InitializingBean){
                try {
                    ((InitializingBean) o).afterPropertiesSet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
               o = beanPostProcessor.postProcessAfterInitialization(o, null);
            }

            return o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

   //掃描包路徑
    public void scan(Class configClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String value = componentScan.value();
//        System.out.println(value);
        String replace = value.replace('.', '/');
        ClassLoader classLoader = ApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(replace);//根據(jù)相對(duì)路徑 com.example.service
//        System.out.println(resource);
        File file=new File(resource.getFile());
        if(file.isDirectory()){
            File[] files=file.listFiles();
            for (File file1 : files) {
                String absolutePath = file1.getAbsolutePath();
                if (absolutePath.endsWith(".class")) {
//                System.out.println(absolutePath);
                    String com = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                    String replace1 = com.replace("\\", ".");//注意\是轉(zhuǎn)義字符,而/不是,com.example.service.xx
//                System.out.println(replace1);
                    Class<?> aClass = null;
                    try {
                        aClass = classLoader.loadClass(replace1);
                        if (aClass.isAnnotationPresent(Component.class)) {
                            if(BeanPostProcessor.class.isAssignableFrom(aClass)){//該class對(duì)象是否實(shí)現(xiàn)了BeanPostProcessor接口,不能用instanceof
                               BeanPostProcessor beanPostProcessor = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance();
                                beanPostProcessorList.add(beanPostProcessor);
                            }


                            Component declaredAnnotation = aClass.getDeclaredAnnotation(Component.class);
                            String beanName = declaredAnnotation.value();
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(aClass);
                            if (aClass.isAnnotationPresent(Scope.class)) {
                                Scope scope = aClass.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scope.value());
                            } else {
                                beanDefinition.setScope("singleton");
                            }
                            beanDefinitionConcurrentHashMap.put(beanName,beanDefinition);

                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }


                }
            }
        }
    }


   //從Spring容器中獲取Bean對(duì)象
    public Object getBean(String beanName){
      if(beanDefinitionConcurrentHashMap.containsKey(beanName)){
          BeanDefinition beanDefinition = beanDefinitionConcurrentHashMap.get(beanName);
          if(beanDefinition.getScope().equals("singleton")){
              Object o = singletonObjects.get(beanName);
              return o;
          }
          else {
              Object bean = createBean(beanDefinition);
              return bean;
          }
      }else {
        throw new NullPointerException();
      }
    }
}

BeanDefinition類

這個(gè)類主要包含Bean對(duì)象的class類型和scope。

public class BeanDefinition {
    private Class clazz;
    private String scope;

    public BeanDefinition(Class clazz, String scope) {
        this.clazz = clazz;
        this.scope = scope;
    }

    public BeanDefinition() {
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }
}

BeanPostProcessor接口實(shí)現(xiàn)

繼承BeanPostProcessor接口主要是實(shí)現(xiàn)初始化前和初始化后的操作。

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if(bean instanceof UserInterface)
        System.out.println("初始化前");
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        //AOP模擬
        if(bean instanceof UserInterface){
            Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("代理前");
                    Object invoke = method.invoke(bean, args);
                    System.out.println("代理后");
                    System.out.println("初始化后");
                    return invoke;
                }
            });
            return proxyInstance;
        }

        return bean;
    }
}

相關(guān)代碼可以在我的文件資源下載https://download.csdn.net/download/qq_43649937/87506483?spm=1001.2014.3001.5503文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-427410.html

到了這里,關(guān)于Spring手寫模擬源碼篇(你值得擁有)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【2】Spring手寫模擬-依賴注入、初始化、AOP

    【2】Spring手寫模擬-依賴注入、初始化、AOP

    首先回顧一下我們之前的流程 ApplicationContext 構(gòu)造方法 獲取Spring配置類 @ComponetScan 注解,掃描包,獲取其路徑及其對(duì)應(yīng)的字節(jié)碼文件 逐個(gè)掃描字節(jié)碼文件 利用字節(jié)碼文件獲取類,查看是否包含 @Componet 注解,并獲取或者生成 BeanName 獲取 BeanDefinition ,包含其類和類型(單例,多

    2024年02月16日
    瀏覽(56)
  • 【手撕Spring源碼】一些值得注意的Spring底層細(xì)節(jié)

    【手撕Spring源碼】一些值得注意的Spring底層細(xì)節(jié)

    FactoryBean是一個(gè)Factory對(duì)象,用于生成其他bean示例。當(dāng)一個(gè)bean實(shí)現(xiàn)FactoryBean接口后,Spring容器調(diào)用其getObject方法返回該工廠所生成的bean,而不是該FactoryBean本身。但是工廠生成的產(chǎn)品對(duì)象只能說(shuō)是一部分受到Spring容器的管理,我們來(lái)看看下面的例子: 我們把一個(gè)Bean1通過(guò)工廠Bean的

    2024年02月09日
    瀏覽(25)
  • 完整攻防知識(shí)體系-你值得擁有

    完整攻防知識(shí)體系-你值得擁有

    根據(jù)中國(guó)互聯(lián)網(wǎng)絡(luò)信息中心(CNNIC)發(fā)布的第51次《中國(guó)互聯(lián)網(wǎng)絡(luò)發(fā)展?fàn)顩r統(tǒng)計(jì)報(bào)告》,截至2022年12月,我國(guó)網(wǎng)民規(guī)模為10.67億,互聯(lián)網(wǎng)普及率達(dá)75.6%。 我國(guó)有潛力建設(shè)全球規(guī)模最大、應(yīng)用滲透最強(qiáng)的數(shù)字社會(huì)。在此背景下,網(wǎng)絡(luò)安全事關(guān)國(guó)家安全和經(jīng)濟(jì)社會(huì)穩(wěn)定,事關(guān)廣大人

    2024年02月06日
    瀏覽(21)
  • Spring源碼系列:初探底層,手寫Spring

    Spring源碼系列:初探底層,手寫Spring

    在學(xué)習(xí)Spring框架源碼時(shí),記住一句話:源碼并不難,只需要給你各種業(yè)務(wù)場(chǎng)景或者項(xiàng)目經(jīng)理,你也能實(shí)現(xiàn)自己的Spring。雖然你的實(shí)現(xiàn)可能無(wú)法與開源團(tuán)隊(duì)相媲美,但是你肯定可以實(shí)現(xiàn)一個(gè)0.0.1版本。因此,初次閱讀源碼時(shí),不要陷入太深的細(xì)節(jié)中。先了解大體邏輯,再仔細(xì)研

    2023年04月12日
    瀏覽(20)
  • SpringMvc攔截器和手寫模擬SpringMvc工作流程源碼詳解

    SpringMvc攔截器和手寫模擬SpringMvc工作流程源碼詳解

    目錄 1. SpringMvc簡(jiǎn)介 1.1 什么是MVC 1.2 什么是SpringMvc 1.3 SpringMvc 能干什么 1.4 SpringMvc 工作流程 2. SpringMvc攔截器和過(guò)濾器 2.1 攔截器 2.1.1 攔截器作用 2.1.2 攔截器和過(guò)濾器的區(qū)別 2.1.3 攔截器方法說(shuō)明 2.1.4 多個(gè)攔截器執(zhí)行順序 2.1.5 自定義攔截器 2.2?過(guò)濾器(附加) 3. 手寫模擬Spri

    2024年02月09日
    瀏覽(25)
  • 200+的AI繪畫工具你值得擁有

    200+的AI繪畫工具你值得擁有 工具 簡(jiǎn)介 鏈接 分類 描述1 描述2 是否免費(fèi) Midjourney 一個(gè)獨(dú)立的研究實(shí)驗(yàn)室,探索新的思想媒介,拓展人類的想象力。 https://www.midjourney.com/ 圖片 Ai繪畫 藝術(shù) Draft ai繪畫 https://draft.art/1891704 圖片 Ai繪畫 藝術(shù) 意間AI繪畫 意間ai繪畫,微信小程序。 h

    2024年02月11日
    瀏覽(25)
  • 200+的AI寫作工具你值得擁有

    200+的AI寫作工具你值得擁有 工具 簡(jiǎn)介 鏈接 分類 描述1 描述2 是否免費(fèi) Better Synonyms BetterSynonyms是一個(gè)方便的工具,可以幫助你在特定語(yǔ)境中找到更好的同義詞。它可以幫助你搜索更自然地放入句子中的同義詞,從而更容易傳達(dá)出你想要表達(dá)的意思。 http://www.bettersynonyms.com 寫

    2024年02月05日
    瀏覽(21)
  • 軟件工程師,TypeScript值得你擁有

    軟件工程師,TypeScript值得你擁有

    背景 ?????????□ TypeScript起源于使用JavaScript開發(fā)的大型項(xiàng)目。由于JavaScript語(yǔ)言本身的局限性,難以勝任和維護(hù)大型項(xiàng)目開發(fā),因此微軟開發(fā)了TypeScript,使得其能夠勝任開發(fā)大型項(xiàng)目。 ????????□ TypeScript是微軟開發(fā)的一個(gè)開源的編程語(yǔ)言,通過(guò)在JavaScript的基礎(chǔ)上添

    2024年02月16日
    瀏覽(21)
  • 盤點(diǎn)搜索引擎一些高級(jí)技巧,你值得擁有!

    盤點(diǎn)搜索引擎一些高級(jí)技巧,你值得擁有!

    搜索引擎是我們?nèi)粘I钪胁豢苫蛉钡墓ぞ咧?,通過(guò)搜索引擎,我們可以在互聯(lián)網(wǎng)上找到任何我們需要的信息。 平時(shí)我們使用搜索引擎除來(lái)直接輸入外,它們還包含了一些高級(jí)技巧?,接下來(lái)我們以 Google 搜索引擎為例進(jìn)行演示。 1、雙引號(hào) \\\"\\\" 雙引號(hào)?\\\"\\\"?可以讓搜索

    2024年01月16日
    瀏覽(27)
  • 5款辦公必備的好軟件,你值得擁有

    5款辦公必備的好軟件,你值得擁有

    隨著網(wǎng)絡(luò)信息技術(shù)的發(fā)展,越來(lái)越多的人在辦公時(shí)需要用到電腦了。如果你想提高辦公效率,那么就少不了工具的幫忙,今天給大家分享5款辦公必備的好軟件。 TagSpaces 是一款開源的文件管理工具,它可以通過(guò)標(biāo)簽來(lái)組織文件,讓你以更符合人類思維方式的方式管理文件。它界

    2024年02月03日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包