SpringBoot自动装配原理之Configuration以及@Bean注解的使用
Configuration以及Bean注解的使用
该知识点在Spring中应该学过,没有学过或者遗忘的的朋友需要预习或温习前置知识点。SpringBoot其实就是Spring的进一步简化,所以前置知识点还是有必要的学习的,这样更能明白其底层的原理。
好了,废话不多说,开始!
结构目录:

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的扫描路径决定的


但是当我们把Myapp放入主程序文件夹时:发现并没有找到相应的组件信息

在不改变原来的程序的情况下,我们可以使用手动扫描的方式,设置自定义扫名路径:
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);
}
}
}
效果显示:

单实例问题:
- 判断组件在容器中是否为单实例:
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方法被调用多少次返回的组件都是单实例的

实际上就是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
如果你看到这里或者正好对你有所帮助,希望能点个关注或者推荐,感谢;
有错误的地方,欢迎在评论指出,作者看到会进行修改。
SpringBoot自动装配原理之Configuration以及@Bean注解的使用的更多相关文章
- SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...
- springboot自动装配原理,写一个自己的start
springboot自动装配原理 第一次使用springboot的时候,都感觉很神奇.只要加入一个maven的依赖,写几行配置,就能注入redisTemple,rabbitmqTemple等对象. 这 ...
- SpringBoot自动装配原理解析
本文包含:SpringBoot的自动配置原理及如何自定义SpringBootStar等 我们知道,在使用SpringBoot的时候,我们只需要如下方式即可直接启动一个Web程序: @SpringBoo ...
- SpringBoot | 2.1 SpringBoot自动装配原理
@ 目录 前言 1. 引入配置文件与配置绑定 @ImportResource @ConfigurationProperties 1.1 @ConfigurationProperties + @Enab ...
- springboot自动装配原理回顾、配置文件分析
配置文件 spring boot官方文档 官方外部配置文件说明参考文档 自动配置原理分析 1. SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfigurat ...
- springboot自动装配原理
最近开始学习spring源码,看各种文章的时候看到了springboot自动装配实现原理.用自己的话简单概括下. 首先打开一个基本的springboot项目,点进去@SpringBootApplica ...
- 【Springboot】Springboot自动装配原理
1.核心注解就是 EnableAutoConfiguration 该注解会激活SpringBoot的自动装配功能: 代码如下: @Target(ElementType.TYPE) @Retentio ...
- SpringBoot 自动装配原理
早期的Spring项目需要添加需要配置繁琐的xml,比如MVC.事务.数据库连接等繁琐的配置.Spring Boot的出现就无需这些繁琐的配置,因为Spring Boot基于约定大于配置的理念,在项目 ...
- SpringBoot:带你认认真真梳理一遍自动装配原理
前言 Spring翻译为中文是“春天”,的确,在某段时间内,它给Java开发人员带来过春天,但是随着我们项目规模的扩大,Spring需要配置的地方就越来越多,夸张点说,“配置两小时,Coding五分钟 ...
随机推荐
- 启动dubbo消费端过程提示No provider available for the service的问题定位与解决
文/朱季谦 某次在启动dubbo消费端时,发现无法从zookeeper注册中心获取到所依赖的消费者API,启动日志一直出现这样的异常提示 Failed to check the status of t ...
- 出现 关于UTF-8 序列的字节 2 无效的异常
学习mybatis中碰到了 Caused by: org.apache.ibatis.builder.BuilderException: Error creating document instanc ...
- web容器获取SSL指纹实现和ByPass
@font-face { font-family: octicons-link; src: url("data:font/woff;charset=utf-8;base64,d09GRgAB ...
- GO语言基础---值传递与引用传递
package main import ( "fmt" ) /* 值传递 函数的[形式参数]是对[实际参数]的值拷贝 所有对地址中内容的修改都与外界的实际参数无关 所有基本数据类型 ...
- GO文件读写03---使用缓冲读写实现视频文件的拷贝
package main import ( "bufio" "fmt" "io" "os" ) /* ·使用缓冲读写实现 ...
- CVPR2018论文看点:基于度量学习分类与少镜头目标检测
CVPR2018论文看点:基于度量学习分类与少镜头目标检测 简介 本文链接地址:https://arxiv.org/pdf/1806.04728.pdf 距离度量学习(DML)已成功地应用于目标分类, ...
- CVPR 2020目标跟踪多篇开源论文(上)
CVPR 2020目标跟踪多篇开源论文(上) 1. SiamBAN:面向目标跟踪的Siamese Box自适应网络 作者团队:华侨大学&中科院&哈工大&鹏城实验室&厦门 ...
- CUDA C++编程接口:编译
CUDA C++编程接口:编译 一.概述 CUDA C++为熟悉C++编程语言的用户提供了一个简单的路径,以方便地编写程序以执行该设备. 它由一组最小的扩展到C++语言和运行库. 在编程模型中引入了核 ...
- 保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事
保护嵌入式802.11 Wi-Fi设备时需要考虑的10件事 10 things to consider when securing an embedded 802.11 Wi-Fi device 随着 ...
- Hash源码注释解析
部分代码注释解析: 1 import java.io.IOException; 2 import java.io.InvalidObjectException; 3 import java.io.Se ...