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开始了格斗。
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:吾乃新日暮里的王,三界哲学的主宰。");
}
}
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.
意料之中,报错了,一切符合预期。