SpringBoot自動裝配原理之Configuration以及@Bean註解的使用

xbhog發表於2021-07-17

Configuration以及Bean註解的使用

該知識點在Spring中應該學過,沒有學過或者遺忘的的朋友需要預習或溫習前置知識點。SpringBoot其實就是Spring的進一步簡化,所以前置知識點還是有必要的學習的,這樣更能明白其底層的原理。

好了,廢話不多說,開始!

結構目錄:

image-20210716230322208

pojo--User:

package com.xbhog.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
    private  String name;
    private int age;
}

config-MyConfig:

package com.xbhog.config;

import com.xbhog.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {
    @Bean
    public User user(){
        return new User("xbhog",18);
    }
}

controller-Mycontroller:

package com.xbhog.controller;

import com.xbhog.config.MyConfig;
import com.xbhog.pojo.User;
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Mycontroller {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User user = context.getBean("user", User.class);
        System.out.println(user.toString());
    }
}

前面這三個檔案埋了一個坑,使用SpringBoot啟動的話是找不到Bean的,因為我們必須把檔案放到與主啟動程式同一目錄下,這樣才能找到,可以這樣:這是由於SpringBootApplication的掃描路徑決定的

image-20210716232848777

image-20210716232942926

但是當我們把Myapp放入主程式資料夾時:發現並沒有找到相應的元件資訊

image-20210716233055827

在不改變原來的程式的情況下,我們可以使用手動掃描的方式,設定自定義掃名路徑:

package com.xbhog.springboot1times;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xbhog")   //設定掃描路徑
public class Myapp {
    public static void main(String[] args) {
        /*1. 返回我們IOC容器*/
        ConfigurableApplicationContext run = SpringApplication.run(Myapp.class, args);
        /*2.檢視容器裡面的元件*/
        String[] names = run.getBeanDefinitionNames();
        for(String name:names){
            System.out.println(name);
        }
    }
}

效果顯示:

image-20210716233317002

單例項問題:

  1. 判斷元件在容器中是否為單例項:
package com.xbhog.springboot1times;

import com.xbhog.pojo.Pet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xbhog")
public class Myapp {
    public static void main(String[] args) {
        /*1. 返回我們IOC容器*/
        ConfigurableApplicationContext run = SpringApplication.run(Myapp.class, args);
        /*2.從容器中獲取元件*/
        Pet tom1 = run.getBean("tom11", Pet.class);
        Pet tom2 = run.getBean("tom11", Pet.class);
        System.out.println("元件是否為單例項:"+(tom1== tom2));
    }
}

元件是否為單例項:true

Myconfig呼叫問題:

因為配置類也屬於元件,如果我們獲取配置類元件後,通過例項化物件在呼叫其中的bean,是呼叫普通方法呢,還是呼叫容器中的相應的元件?

package com.xbhog.springboot1times;

import com.xbhog.config.MyConfig;
import com.xbhog.pojo.Pet;
import com.xbhog.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.xbhog")
public class Myapp {
    public static void main(String[] args) {
        /*1. 返回我們IOC容器*/
        ConfigurableApplicationContext run = SpringApplication.run(Myapp.class, args);
        /*2.檢視容器裡面的元件*/
        String[] names = run.getBeanDefinitionNames();
        for(String name:names){
            System.out.println(name);
        }
        /*3.從容器中獲取元件*/
        Pet tom1 = run.getBean("tom11", Pet.class);
        Pet tom2 = run.getBean("tom11", Pet.class);
        System.out.println("元件是否為單例項:"+(tom1== tom2));

        MyConfig bean = run.getBean(MyConfig.class);
        System.out.println(bean);

        User user1 = bean.user();
        User user2 = bean.user();
        System.out.println("測試通過Myconfig類呼叫的user元件:"+(user1==user2));
    }
}

效果如下:

元件是否為單例項:true
com.xbhog.config.MyConfig EnhancerBySpringCGLIB 9b1ae7c2@6c0905f6
測試通過Myconfig類呼叫的user元件:true

上面的效果也就是Configuration(proxyBeanMethods=true)的作用,保證每個@Bean方法被呼叫多少次返回的元件都是單例項的

image-20210716234805160

實際上就是proxyBeanMethods:代理bean的方法(true),外部無論對配置類中的這個元件註冊方法呼叫多少次獲取的都是之前註冊容器中的單例項物件,也叫:FULL模式.

proxyBeanMethods=true時:

MyConfig返回的Bean本身就是代理物件,CGLIB,並且測試通過Myconfig類呼叫的user元件:true.

com.xbhog.config.MyConfig EnhancerBySpringCGLIB 9b1ae7c2@6c0905f6

MyConfig bean = run.getBean(MyConfig.class);
System.out.println(bean);
//如果@Configuration(proxyBeanMethods = true)代理物件呼叫方法。
// SpringBoot總會檢查這個元件是否在容器中有。
User user1 = bean.user();
User user2 = bean.user();
System.out.println("測試通過Myconfig類呼叫的user元件:"+(user1==user2));

當:proxyBeanMethods=false時(Lite模式-輕量級):

MyCnfig返回的就不是代理模式,測試通過Myconfig類呼叫的user元件:false

總結:

proxyBeanMethods:代理bean的方法 ;

  • Full(proxyBeanMethods = true)、【保證每個@Bean方法被呼叫多少次返回的元件都是單例項的】
  • Lite(proxyBeanMethods = false)【每個@Bean方法被呼叫多少次返回的元件都是新建立的】
  • 元件依賴必須使用Full模式預設。其他預設是否Lite模式

使用場景:

現在向pojo.User中新增Pet物件:

package com.xbhog.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
    private  String name;
    private int age;
    private  Pet pet;
}

proxyBeanMethods=true模式下才是正確的;

package com.xbhog.config;

import com.xbhog.pojo.Pet;
import com.xbhog.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration  //告訴SpringBoot這是一個配置類  ==  application.xml
public class MyConfig {
    @Bean  //給容器新增元件,以方法名作為元件的id,返回型別就是元件型別。返回的值,就是元件在容器中的例項
    public User user(){
        User user = new User("xbhog", 18);
        //user元件依賴與Pet元件,使用者中的寵物與容器中的寵物時一樣的
        user.setPet(tomcat());
        return user;
    }
    @Bean("tom11")  //設定bean別名--》id的名字
    public Pet tomcat(){
        return new Pet("tomcat");
    }
}

User user = run.getBean("user", User.class);
Pet tom3 = run.getBean("tom11", Pet.class);
System.out.println("使用者的寵物"+(user.getPet() == tom3));

使用者的寵物true;

proxyBeanMethods=false模式後,就不會掃描容器,直接建立物件:

元件是否為單例項:true
com.xbhog.config.MyConfig@330c1f61
測試通過Myconfig類呼叫的user元件:false
使用者的寵物false

如果你看到這裡或者正好對你有所幫助,希望能點個關注或者推薦,感謝;
有錯誤的地方,歡迎在評論指出,作者看到會進行修改。

相關文章