Spring的新引入的注解

 @ConditionalOnClass

是Springboot实现自动配置的重要支撑之一。其用途是判断当前classpath下是否存在指定类,若是则将当前的配置装载入spring容器。举例来说,如果在maven中引入了velocity,那么视图就使用velocity,若引入的是freemarker,则使用freemarker.

但是眼见为虚,手敲为实,所以自己决定来验证下其使用。

场景设在新日暮里,主角是van,是一个喜爱摔跤的格斗爱好者,他将向新日暮里的其他选手发起友谊比赛。这里,有两位新日暮里的强者,一位是新日暮里王,billy;另一位来自自由的大美利坚,香蕉君banana。van急于找人摔跤,因此只要有人在,他就发起挑战。


用intellj新建个springboot工程,目录如下:




入口类:
package com.ff.fun;
import com.ff.fun.player.Van;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SbfunApplication implements CommandLineRunner{

	@Autowired
	private Van van;

	public static void main(String[] args) {
		SpringApplication.run(SbfunApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		//do something
		van.fight();
	}
}

没什么好多说的,我们的主角Van开始了格斗。

主角Van:
package com.ff.fun.player;

import com.ff.fun.fighter.Fighter;

public class Van {

    private Fighter fighter;

    public Van(Fighter fighter) {
        this.fighter = fighter;
    }

    public void fight(){
        System.out.println("van:boy next door,do you like 玩游戏");
        fighter.fight();
    }
}
新日暮里格斗家们:
package com.ff.fun.fighter;

public interface Fighter {

    void fight();
}
package com.ff.fun.fighter;

public class Banana implements Fighter {
    @Override
    public void fight() {
        System.out.println("banana:自由的气息,蕉迟但到");
    }
}
package com.ff.fun.fighter;

public class Billy implements Fighter{

    @Override
    public void fight(){
        System.out.println("billy:吾乃新日暮里的王,三界哲学的主宰。");
    }
}

Config:
package com.ff.fun.config;

import com.ff.fun.player.Van;
import com.ff.fun.fighter.Billy;
import com.ff.fun.fighter.Fighter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass({Billy.class})
public class VanConfig {

    @Bean
    public Fighter billy(){
        return new Billy();
    }

    @Bean
    public Van van(){
        return new Van(billy());
    }
}
package com.ff.fun.config;

import com.ff.fun.player.Van;
import com.ff.fun.fighter.Banana;
import com.ff.fun.fighter.Fighter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass({Banana.class})
public class Van2Config {
    @Bean
    public Fighter banana(){
        return new Banana();
    }

    @Bean
    public Van van(){
        return new Van(banana());
    }
}
这就是重点了,这两个带条件的配置类,一个是当Billy在的时候启用,一个是在当Banana在的时候启用

试验环节:

编译后,在target中,可见


这两个类,这时候如果直接执行程序,会输出:

van:boy next door,do you like 玩游戏
billy:吾乃新日暮里的王,三界哲学的主宰。

说明,当候选类都在的情况下,spring会挑其中之一(至于如何选择的得另行研究);


这时候,删掉Billy.class,让吾王下线,新日暮里就只剩下Banana一个哲学战士了,此时,再次运行,输出为:

van:boy next door,do you like 玩游戏
banana:自由的气息,蕉迟但到

可以看到,billy的配置没有加载,van的对手是banana,@ConditionalOnClass这个注解起到了选择Config的作用。


此时,如果将香蕉君也删掉,新日暮里空无一人(⊙︿⊙),此时运行结果为:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field van in com.ff.fun.SbfunApplication required a bean of type 'com.ff.fun.player.Van' that could not be found.
	- Bean method 'van' not loaded because @ConditionalOnClass did not find required class 'com.ff.fun.fighter.Banana'
	- Bean method 'van' not loaded because @ConditionalOnClass did not find required class 'com.ff.fun.fighter.Billy'


Action:

Consider revisiting the conditions above or defining a bean of type 'com.ff.fun.player.Van' in your configuration.

意料之中,报错了,一切符合预期。



Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐