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

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

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