springboot学习笔记

本文深入探讨SpringBoot中@Bean、@Component、@Configuration等注解的使用与区别,包括它们如何影响Bean的创建、管理和生命周期,以及如何利用@Autowired进行依赖注入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


老版的基本xml配置的spring简直就是一场配置灾难.
按官方的说法, 使用xml的好处:
修改配置之后, 不需要重新编译代码. 这样可以方面调整某些参数. (但是如果需要重启服务, 这好像也没有多大的意义, 我通常需要的是在线调整参数).
缺点:
配置太臃肿了, 编辑起来累人.

因此, 个人推荐直接使用注解.
官方文档:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#spring-core

@Bean

作用在方法之上, 定义一个bean对象, 这个对象是单例模式的. @Bean所在的class通常需要使用@Configuratoin, @Service, @Component中的一种来注解

@Configuration
public class MyConfig {
    @Bean
    String hello(){return "hello world";}
}

上例定义了一个name为"hello", 值为"hello world"的String对象.

@Component

当开启了EnableAutoConfiguration, springboot会自动搜索被@Component注解的class, 并将其注册为Bean对象
@Component中@bean注解的方法也会生成bean对象

@Configuration

它相当于加强版的@Component

@Configuration与@Component差别

处理嵌套的bean的方式不同

@Configuration与@Component的区别, 在于处理嵌套的bean时, 方式不一样.

  1. @Configuration处理嵌套的bean.
    下面的例子中, 只会创建一个MyParam对象, "config1"和"config2"都关联的是同一个对象.
@Configuration
public class MyConfig {
    private final static Logger log = LogManager.getLogger(MyConfig.class);
    @Bean
    public MyParam config1(){
        return config2();
    }
    @Bean
    public MyParam config2(){
        return new MyParam(1);
    }
}
  1. @Component处理嵌套的bean.
    同样的例子, Component会创建两个MyParam, "config1"和"config2"关联的不是同一个对象
@Component
public class MyConfig {
    private final static Logger log = LogManager.getLogger(MyConfig.class);
    @Bean
    public MyParam config1(){
        return config2();
    }
    @Bean
    public MyParam config2(){
        return new MyParam(1);
    }
}

CGLIB代理差异

@Configuration注解的class会被CGLIB代理, 生成的Bean对象其实是由代理之后的class生成的, 像这样

MyConfig$$EnhancerBySpringCGLIB$$e6716a4e

@Component不会被CGLib代理, 生成对bean就是原始的class构造的

@Service

等同于@Component, 没有任何差别, 可以相互替代.
通常用于注解业务层的class, 这样代码逻辑更清晰一些.

@Repository

基本等同于@Component, 但是它对DAO的异常做了特殊处理.
通常, 操作数据库时, 可能会抛出Hibernate的HibernateException, 或者是JPA的PersistenceException,
@Repository会将这些异常转换成SpringBoot的异常.

主要是用于区分不同的功能.
@Service通常用于业务层
@Repository通常用于DAO.
都可以使用@Component代替.

@Autowired

自动装配, 可以用于自动构造参数/成员对象.
本例中的@Configuration的作用, 后面再讲.

基本用法

作用于构造方法

@Configuration
public class MyParam {
    private final static Logger log = LogManager.getLogger(MyParam.class);
    
    public MyParam(){
        log.info("MyParam()");
    }

    public MyParam(int x){
        log.info("MyParam x = {}", x);
    }

	// 作用于构造方法,  自动装配时, 使用此构造方法 
    @Autowired
    public MyParam(int x, int y){
        /** (x, y的值是未知的, 不清楚spring是如何生成x, y的) */
        log.info("MyParam x = {}, y = {}", x, y);
    }
}

@Configuration
public class MyConfig {
    private final static Logger log = LogManager.getLogger(MyConfig.class);
    
    // 此处省略了 @Autowired, 因为此Class只有一个构造方法.  
    // springboot会查找名为"myParam"的bean, 如果没有, 则会查找类型MyParam 
    // 的bean. 
    //  MyParam 参数将使用上面的public MyParam(int x, int y)方法来构造
    public MyConfig(MyParam myParam){
        log.info("construct MyConfig");
    }
}

作用于普通方法

@Configuration
public class MyConfig {
    private final static Logger log = LogManager.getLogger(MyConfig.class);

    public static class MyParam{
        int x;
        public MyParam(int x){
            this.x = x;
        }
    }

    @Bean
    MyParam arg1(){
        log.info("arg1()");
        return new MyParam(1);
    }

    @Bean
    MyParam arg2(){
        log.info("arg2()");
        return new MyParam(2);
    }

	//springboot将搜索名为"arg1"的bean.
    @Autowired
    public void config(MyParam arg1) {
        log.info("config");
    }
}

作用于成员变量

@Configuration
public class MyConfig {
    private final static Logger log = LogManager.getLogger(MyConfig.class);
    
    // springboot将搜索名为"myParam"的对象, 自动赋值.
    @Autowired
    private MyParam myParam;
}

AutoWired搜索Bean的过程

先根据参数的类型(byType)查找bean. 如果没有找到, 则根据name(byName)来查找
如果定义这两种方式都定义了, 则默认情况下, 都使用byType查找到的bean, 例如

//此处相当于创建了一个type为"MyParam"的bean 
@Configuration
public class MyParam {
    private final static Logger log = LogManager.getLogger(MyParam.class);
    public int x;
    public MyParam(int x){
        log.info("MyParam x = {}", x);
        this.x = x;
    }
}
@Configuration
public class MyConfig {
    private final static Logger log = LogManager.getLogger(MyConfig.class);

	//定义了name为"arg1"的bean
    @Bean
    MyParam arg1(){
        log.info("arg1()");
        return new MyParam(1);
    }
    
    //定义了name为"arg2"的bean
    @Bean
    MyParam arg2(){
        log.info("arg2()");
        return new MyParam(2);
    }

	//由于springboot context中已经有了类型"MyParam"的bean, 
	// 所以此处不会使用arg1()创建的bean.
    @Autowired
    public void config(MyParam arg1) {
        log.info("config: {}", arg1.x);
    }
}

上例中, springboot context中存在三个类型为MyParam的bean, 按byName搜索成功之后, 不会再往下搜索了.
如果public void config(MyParam arg1)一定要使用arg1()创建的bean, 可以显式的指定name, 如下

    @Autowired
    @Qualifier("arg1")
    public void config(MyParam arg1) {
        log.info("config: {}", arg1.x);
    }

使用@Qualifier注解强制要求使用某个bean.
或者使用

@Configuration
public class MyConfig {
    private final static Logger log = LogManager.getLogger(MyConfig.class);

	//定义了name为"arg1"的bean
    @Bean
    @Primary
    MyParam arg1(){
        log.info("arg1()");
        return new MyParam(1);
    }
    
    //定义了name为"arg2"的bean
    @Bean
    MyParam arg2(){
        log.info("arg2()");
        return new MyParam(2);
    }

	//由于springboot context中已经有了类型"MyParam"的bean, 
	// 所以此处不会使用arg1()创建的bean.
    @Autowired
    public void config(MyParam arg1) {
        log.info("config: {}", arg1.x);
    }
}

@Primary

上例中, @Configuration注解会byType创建了一个bean, 这个bean具有第一优先级. 使用@Primary可以实现相同的效果(注意: @Primary不会覆盖@Configuration创建的bean)

@Configuration
public class MyConfig {
    private final static Logger log = LogManager.getLogger(MyConfig.class);
    public static class MyParam {
        public int x;
        public MyParam(int x){
            log.info("MyParam x = {}", x);
            this.x = x;
        }
    }

    @Bean
    MyParam arg1(){
        log.info("arg1()");
        return new MyParam(1);
    }
    @Bean
    @Primary
    MyParam arg2(){
        log.info("arg2()");
        return new MyParam(2);
    }

	/** 装配的是arg2()创建的对象 */
    @Autowired
    public void config(MyParam arg) {
        log.info("config: {}", arg.x);
    }

	/** 装配的也是arg2()创建的对象 */
    @Autowired
    public void config2(MyParam arg1) {
        log.info("config2: {}", arg1.x);
    }
}

Bean的定制化

官方文档
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-nature

监听bean的创建和销毁事件

@Configuration
public class MyConfig implements InitializingBean, DisposableBean {
    private final static Logger log = LogManager.getLogger(MyConfig.class);

    @Override
    public void afterPropertiesSet() throws Exception {
        //MyConfig创建之后调用
        log.info("[MyConfig] afterPropertiesSet");
    }

    @Override
    public void destroy() throws Exception {
        //MyConfig销毁之前调用
        log.info("[MyConfig] destroy()");
    }
}

或者使用注解的方式

@Configuration
public class MyConfig{
    private final static Logger log = LogManager.getLogger(MyConfig.class);

    @PostConstruct
    public void afterPropertiesSet() throws Exception {
        //MyConfig创建之后调用    
        log.info("[MyConfig] afterPropertiesSet");
    }

    @PreDestroy
    public void destroy() throws Exception {
        //MyConfig销毁之前调用    
        log.info("[MyConfig] destroy()");
    }
}

装配ApplicationContext

bean可能需要操作ApplicationContext, 需要装配ApplicationContext对象. 也有两种方式.

@Configuration
public class MyConfig implements ApplicationContextAware { //方式1, 实现ApplicationContextAware 
    private final static Logger log = LogManager.getLogger(MyConfig.class);

	//方式2, 自动装配
    @Autowired
    ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("[MyConfig] setApplicationContext");
    }
}

装配Bean Name

springboot会为每个bean对象, 创建一个名称, bean可以通过以下方式获取到springboot为其创建的名称

@Configuration
public class MyConfig implements BeanNameAware {
    private final static Logger log = LogManager.getLogger(MyConfig.class);

    @Override
    public void setBeanName(String name) {
        log.info("set bean name: {}", name);
    }
}

使用BeanPostProcessor批量定制

@Configuration
public class MyBeanPostProcessor implements BeanPostProcessor {
    public static final Logger log = LogManager.getLogger(MyBeanPostProcessor.class);
    private ConfigurableListableBeanFactory configurableBeanFactory;

    @Autowired
    public MyBeanPostProcessor(ConfigurableListableBeanFactory beanFactory) {
        this.configurableBeanFactory = beanFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        /** 开始定制 bean */
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        /** 开始定制 bean */
        return bean;
    }
}    

加速扫描过程

默认情况下, springboot会扫描所有依赖的jar包, 会影响启动速度.
spring提供了一个工具, 为所有的Component创建索引

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.1.2.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

生成的jar/war中的META-INF下会出现一个索引文件spring.components

#
com.my.MyConfig2=org.springframework.stereotype.Component
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值