配置类不加@Configuration竟然也可以注册bean

        @Configuration这个注解我想大家都不陌生吧,在大家看来加了@Configuration注解的类只不过是一个配置类,用来代替xml配置文件的一个类,在这个配置类里面可以和@Bean注解一起使用,通过@Bean注解往spring容器注入bean,简化了我们的开发。但是@Configurantion的作用真的是这样子的吗?话不多说就让我们一起来揭开@Configuration的真实身份。

public Class UserService{

    public UserService(){
        System.out.println("UserService init");
    }

}

public Class OrderSerivce{

    public OrderSerivce(){
        System.out.println("OrderSerivce init");
    }

}


@Configuration
public Class AppConfig{

    @Bean
    public UserService userService(){
        return new UserService();
    }

    @Bean
    public OrderSerivce orderSerivce(){
        return new OrderSerivce();
    }
}


public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new     
               AnnotationConfigApplicationContext(AppConfig.class);
        
    }
}

        我想大家对上面这些代码都很熟悉吧,和大家想的一样,这些代码是可以正常运行的没有问题。运行结果如下:

UserService init
OrderService init

Process finished with exit code 0

        我们可以看到,UserService和OrderSerivce都成功的进行了初始化,被放到spring容器里面了,而起到这种作用的原因是因为我们在类上面加上了@Configuration注解,所以能扫描到加了@Bean的方法把对象注入到容器中。真的是这样吗?接下来我们把@Configuration注解重配置类上去掉

public Class AppConfig{

    @Bean
    public UserService userService(){
        return new UserService();
    }

    @Bean
    public OrderSerivce orderSerivce(){
        return new OrderSerivce();
    }
}



public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new             
             AnnotationConfigApplicationContext(AppConfig.class);
        System.out.println(ac.getBean(UserService.class));
}

        讲道理,当我们去掉了@Configuration注解,配置类就失效了,里面的UserService和OrderService就无法被注册到spring容器当中了。这是当我们通过getBean获取UserSerivce的时候就会出现异常,没有找到UserService这个bean。但是结果真的是这样吗?运行结果如下:

UserService init
OrderService init
com.zhijia.service.UserService@2833cc44

Process finished with exit code 0

        运行结果很明显,代码是可以正常运行的。UserService和OrderSerivce一样可以实例化到spring容器当中,通过getBean()也可以得到UserService打印到控制台。小伙伴们是不是不敢相信。你们可以直接手动去测试一下,代码是不会骗人的。

        这个时候我们就会有个疑问,那我们还要@Configuration有何用?不要心急,我们学编程的时候要沉下心来。接下来我们就来揭开@Configuration的真实作用,让我们举个实例来分析。

public class AppConfig {

    @Bean
    public UserService userService(){
        return new UserService();
    }

    @Bean
    public OrderService orderService(){
        userService();
        return new OrderService();
    }

}



public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new             
             AnnotationConfigApplicationContext(AppConfig.class);
        
}

        上面我们在UserService里面调用OrderService这个时候UserSerivce会被调用两次,运行结果如下:

UserService init
UserService init
OrderService init

Process finished with exit code 0

        那当我们在配置类上加上@Configuration的时候,那又是个什么样的结果呢?UserService会被实例化几次呢?

@Configuration
public class AppConfig {

    @Bean
    public UserService userService(){
        return new UserService();
    }

    @Bean
    public OrderService orderService(){
        userService();
        return new OrderService();
    }

}


public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new             
             AnnotationConfigApplicationContext(AppConfig.class);
        
}

运行结果如下:

UserService init
OrderService init

Process finished with exit code 0

        我们可以看到,UserService只被实例化了一次。按照我们java的语法来讲,我们这里调用了userService这个方法,调用这个方法它一定会实例化一次。但是但我们加上@Configuration注解的之后,你会发觉它只实例化了一次。

        现在我们搞清楚了@Configuration主要是用来做什么的了吧,它就是为了能够让我们spring,比如所我们的springboot或者spring在写上这种@Bean注解的时候(利用Java Config注解来完成对spring环境开发的时候),保证bean的一个作用域,保证它(bean)的生命周期跟它的作用域,包括Scope,Scope就是我们讲的单例(singletion)和原型(prototype)。

        那他是如何保证的呢,到这里我们来分析一个问题:

        按照我们对java的理解,我们spring内部它要去实现这个UserService,它首先会调用一遍
下边这个userService()然后得到UserService。然后再去实例化我们OrderService的时候,
在OrderService里面调用又调用userService(),由于我们的代码中已经写死了,一定会去
new UserSerivce() 那么肯定会执行两遍构造方法。

@Configuration
public class AppConfig {

    /**
     *  按照我们对java的理解,我们spring内部它要去实现这个UserService,它首先会调用一遍
     *  下边这个userService()然后得到UserService。然后再去实例化我们OrderService的时候,
     *  在OrderService里面调用又调用userService(),由于我们的代码中已经写死了,一定会去
     *  new UserSerivce() 那么肯定会执行两遍。
     *  
     */
    @Bean
    public UserService userService(){
        return new UserService();
    }

    @Bean
    public OrderService orderService(){
        userService();
        return new OrderService();
    }

}

        那么它没有执行两遍只有一个原因,你们可以思考一下,当我们一个方法调用的时候它的预期结果跟你想的不一样,那么你觉得是什么导致的。说白了,就是这个方法被改变了,这个方法不再是原来的这个方法,。因为我们这个方法,它的结果是必然的,它必然会产生一个类,但是我们调用两次它只产生一个类。那就违背了它的必然结果,那就说明了这个代码已经被别人改了。那么被随改了,肯定是被spring改了。那么按照我们现在所学的知识当中,我们要去改变一个方法的行为,没有改源码,可以用什么实现?

        没错就是动态代理,在这里用的是cglib动态代理。同学们不相信是吧?那我就是通过代码跟大家证明一下。

        现在我们配置类上是没有加@Configuration注解的,这时从容器中获取这个AppConfig这个配置类,你会发现这个类没有改变,他还是叫AppConfig。


public class AppConfig {

    @Bean
    public UserService userService(){
        return new UserService();
    }

    @Bean
    public OrderService orderService(){
        userService();
        return new OrderService();
    }

}


public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new             
             AnnotationConfigApplicationContext(AppConfig.class);
        AppConfig appConfig = ac.getBean(AppConfig.class);
        System.out.println(appConfig);
    }
}

        运行结果如下:com.zhijia.config.AppConfig@2833cc44

UserService init
UserService init
OrderService init

com.zhijia.config.AppConfig@2833cc44

Process finished with exit code 0

        当我们加上@Configuration注解的时候,这时从容器中获取这个AppConfig这个配置类是一个cglib动态代理产生的类。

@Configuration
public class AppConfig {

    @Bean
    public UserService userService(){
        return new UserService();
    }

    @Bean
    public OrderService orderService(){
        userService();
        return new OrderService();
    }

}


public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new             
             AnnotationConfigApplicationContext(AppConfig.class);
        AppConfig appConfig = ac.getBean(AppConfig.class);
        System.out.println(appConfig);
    }
}

           运行结果如下:很明显的五个大字母

                com.zhijia.config.AppConfig$$EnhancerBySpringCGLIB$$e52e37ff@588df31b

UserService init
OrderService init

com.zhijia.config.AppConfig$$EnhancerBySpringCGLIB$$e52e37ff@588df31b

Process finished with exit code 0

        这下子就清楚底层是用什么实现了吧,大家可以手动自己去证实一下。由于底层代码写起来比较复杂,这里就简单跟大家提一下主要是用什么实现的,就不深入分析了,如果感兴趣的伙伴可以自己去研究一下源码。

        之所以发布这篇文章就是想解决大家对@Configuration的一些误解。 如果本文对你有帮助的话,记得点个赞!谢谢大家!

       HZJ,一个在学习路上匍匐前行的小菜鸟!

  • 12
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值