Spring配置类之full和lite模式

本文基于Spring 5.2.7

Spring配置类不仅仅是@Configuration注解修饰的类,只是@Configuration修饰的类更加正式,Spring对此也是进行了区分处理,Spring将配置类划分为了full模式和lite模式。

一、配置类的full和lite模式

lite模式包含:

  1. 没有被@Configuration修饰,被@Component修饰
  2. 没有被@Configuration修饰,被@ComponentScan修饰
  3. 没有被@Configuration修饰,被@Import修饰
  4. 没有被@Configuration修饰,被@ImportResource修饰
  5. 没有任何Spring相关注解,类里面有@Bean修饰的方法
  6. 被@Configuration修饰,但是属性proxyBeanMethods = false

full模式包含:

  1. 被@Configuration修饰,且属性proxyBeanMethods = true(proxyBeanMethods 默认为true)

full模式使用特性:

  1. full模式下的配置类会被CGLIB代理生成代理类取代原始类型(在容器中)
  2. full模式下的@Bean方法不能是private和final
  3. 单例scope下不同@Bean方法可以互相引用,达到单实例的语义

lite模式使用特性:

  1. lite模式下的配置类不生成代理,原始类型进入容器
  2. lite模式下的@Bean方法可以是private和final
  3. 单例scope下不同@Bean方法引用时无法做到单例

lite测试用例

@Component
public class LiteConfig {

    @Bean
    User u1() {
        return new User("小明", 13);
    }

    @Bean
    User u2() {
        User localUser = u1();
        System.out.println(localUser.hashCode());
        User localUser2 = u1();
        System.out.println(localUser2.hashCode());
        return new User("小明2", 14);
    }

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(LiteConfig.class);
        Object liteConfig = ctx.getBean("liteConfig");
        System.out.println(liteConfig);
    }
}
368342628
1192923170
com.peace.test.configuration.test2.LiteConfig@59e2d8e3

Process finished with exit code 0

full测试用例

@Configuration
public class FullConfig {


    @Bean
    User u1() {
        return new User("小明", 13);
    }

    @Bean
    User u2() {
        User localUser = u1();
        System.out.println(System.identityHashCode(localUser));
        User localUser2 = u1();
        System.out.println(System.identityHashCode(localUser2));
        return new User("小明2", 14);
    }

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(com.peace.test.configuration.test2.FullConfig.class);
        Object fullConfig = ctx.getBean("fullConfig");
        System.out.println(fullConfig);
    }

}
1200906406
1200906406
com.peace.test.configuration.test2.FullConfig$$EnhancerBySpringCGLIB$$d004b363@4f0100a7

二、为何要区分full和lite模式

因为被Spring管理的类功能比较多,有一些是业务类,这些类解析起来比较简单和有规律,可归纳为lite模式一类,有一些类需要额外一些逻辑,需要生成CGLIB代理,所以这一类就被划分到full模式一类。

二、@Bean官方文档

@Bean注解始自Spring 3.0

Overview

表明一个方法,这个方法可以生产一个被Spring容器管理的bean。
这个注解的属性值和对应的语义都被特意的贴近Spring XML模式中的<bean>元素。
使用例子:

@Bean
public MyBean myBean() {
    // instantiate and configure MyBean obj
    return obj;
}

Bean Names

当name属性可用时(难道还有不可用的时候?早期不可用,4.3.3才支持),确定bean name的默认策略是使用@Bean修饰的方法名。这是方便有易懂的,但如果需要显示命名,可以使用name(或者其属性别名)属性。同时注意,name属性接受一个Strings数组,允许一个单例bean有多个名字(也即一个主名字加一个或多个别名)。

@Bean({"b1", "b2"}) // bean available as 'b1' and 'b2', but not 'myBean'
public MyBean myBean() {
    // instantiate and configure MyBean obj
    return obj;
}

Profile, Scope, Lazy, DependsOn, Primary, Order

注意,@Bean注解不提供profile,scope, lazy, depends-on和primary这些属性。相反,应该使用@Scope,@Lazy,@DependsOn,@Primary注解来表明这些语义。例如:

@Bean
@Profile("production")
@Scope("prototype")
public MyBean myBean() {
    // instantiate and configure MyBean obj
    return obj;
}

上面提到的注解的语义和在类级别上使用时是一样的。
@Profile允许有环境选择性的注入某个bean
@Scope可以将bean的scope从singleton改到其他指定的scope
@Lazy只在默认的singleton scope下才起到实际效果
@DependsOn,在创建当前bean之前,除了创建当前bean直接引用的bean,@DependsOn会强制在创建当前bean之前创建其指定的bean,这通常在单例启动时有用。
@Primary是一种解决单个bean注入时有多个类型匹配的bean的歧义的机制。
此外,@Bean方法在注入时还可以使用qualifier注解和@Order注解,就像在bean上使用对应的注解一样,但是这种情况可能非常个性化(一个类有多个定义的场景)。Qualifiers在初始类型匹配后缩小了候选beans的范围。order值确定了多个collection注入场景时已解析元素的顺序(有多个目标bean类型匹配限定符也匹配场景)。
注意:@Order值可能影响注入点的优先级,但请知悉,他不会影响单例启动顺序,因为单例启动是顺序由依赖关系和@DependsOn的交叉关系决定的。另外,@Priority不能用在这里因为他不能在方法上声明,@Priority的语义可以通过@Order值结合@Primary实现。

@Bean Methods in @Configuration Classes

通常,@Bean方法被声明在@Configuration类里面。这种情况下,同一个类的里@Bean方法可能直接调用另一个@Bean方法,这确保了beans之间的引用强类型和导向的。这种所谓的inter-bean references是遵守scoping和AOP语义的保证,就像getBean()方法会查找引用一样。这些就是从最初的Spring JavaConfig中已知的语义,他需要在运行时为每个@Configuration类生成CGLIB子类,结果就是,这种模式下,@Configuration类和他的factory methods不能是final和private的,例如:

@Configuration
public class AppConfig {

    @Bean
    public FooService fooService() {
        return new FooService(fooRepository());
    }

    @Bean
    public FooRepository fooRepository() {
        return new JdbcFooRepository(dataSource());
    }

    // ...
}

@Bean Lite  Mode

@Bean方法也可以不在@Configuration类里面使用,例如,@Bean方法可以在@Component类里面甚至一个pojo类里面使用。这种场景下,@Bean方法会得到一个所谓的lite mode的处理。lite mode下的@Bean方法会被容器当作简单factory methods处理(类似于XML中factory-method的声明),并能正确被应用scoping和lifecycle回调。这种情形下,containing class保持不变,没有其他特殊的限制针对containing class和factory methods。
与@Configuration里@Bean方法语义不同,lite mode下inter-bean references不受支持。相反,lite mode下,当一个@Bean方法调用另一个@Bean方法,是标准的Java方法调用,Spring不会通过CGLIB拦截这个调用。这和Spring不拦截代理模式下内部@Transactional方法调用类似——Spring只在ASpectJ模式下拦截。使用例子:

@Component
public class Calculator {
    public int sum(int a, int b) {
        return a+b;
    }

    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}

Bootstrapping

查阅@Configuration文档获取更多使用AnnotationConfigApplicationContext等相关类启动容器的细节。

BeanFactoryPostProcessor returning @Bean methods

必须特别注意返回BeanFactoryPostProcessor(BFPP)类型的@Bean方法。因为BFPP对象必须在容器生命周期的很早阶段被实例化,在@Configuration里面他们能介入处理@Autowired,@Value,@PostConstruct注解,为了避免这种生命周期导致的问题,要将@Bean方法声明为static,使用例子:

@Bean
public static PropertySourcesPlaceholderConfigurer pspc() {
    // instantiate, configure and return pspc ...
}

通过标记这个方法为static,他能在其外部的@Configuration类还没实例化之前被调用,就能避免上面提到的生命周期冲突的问题。但是请注意,static @Bean方法不会得到scoping和AOP语义的增强。这在BFPP情形下是可行的,因为他们通常不被其他@Bean方法调用。作为提醒,任何非static @Bean方法返回BFPP类型时会有一条警告日志给出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值