阅读 338

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

如果你看到这里或者正好对你有所帮助,希望能点个关注或者推荐,感谢; 有错误的地方,欢迎在评论指出,作者看到会进行修改。

文章分类
后端
文章标签