背景
本次因为业务要求需要集成外部jar,但是该jdk版本是1.8,我们的服务是当前使用区域支持1.8但是其他其余部分不支持,但是也不需要该服务。因此需要项目差异化实例化需要调用的服务
实现方式
- 引入第三方jar,并且设置访问provided
scope的其他参数如下:
compile
默认的scope,表示 dependency 都可以在生命周期中使用。而且,这些dependencies 会传递到依赖的项目中。适用于所有阶段,会随着项目一起发布
provided
跟compile相似,但是表明了dependency 由JDK或者容器提供,例如Servlet AP和一些Java EE APIs。这个scope 只能作用在编译和测试时,同时没有传递性。
采用provided的好处就是保证了该jar方法可以正常使用以及编译,但是不会发布; - 在区域差异化打包中重新引入该jar的maven信息
- 最后控制引用该方法的实例方法是否被初始化
Profile
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
String[] value();
}
@Component
@Profile("dev")
public class DevDatasourceConfig
上面的DevDatasourceConfig被定义为 profile=dev,于是该Bean只会在dev(开发环境)模式下被启用。
如果需要定义为非dev环境,可以使用这样的形式:
@Component
@Profile("!dev")
public class DevDatasourceConfig
设置Profile的方式
- WebApplicationInitializer接口
@Configuration
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("spring.profiles.active", "dev");
}
}
- JVM启动参数
-Dspring.profiles.active=dev - 通过 web.xml定义
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
- application.properties
可以在application.properties配置文件中指定spring.profiles.active属性:
spring.profiles.active=dev
- Maven Profile
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<spring.profiles.active>dev</spring.profiles.active>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<spring.profiles.active>prod</spring.profiles.active>
</properties>
</profile>
</profiles>
源码分析
@Profile实际上是一个@Conditional注解。并且明确使用了ProfileCondition
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
}
mathces()方法的返回值是一个布尔值,返回true时,spring就会创建这个bean,返回false时就不会创建。
@Condition解析
@ConditionalOnBean 当容器有指定Bean的条件下
@ConditionalOnClass 当容器有指定类的条件下
@ConditionalOnExpression 基于SpEL表达式作为判断条件
@ConditionalOnJava 基于JVM版本作为判断条件
@ConditionalOnJndi 在JDNI存在的条件下查找指定位置
@ConditionalOnMissingBean 当容器没有指定Bean的情况下
@ConditionalOnMissingClass 当容器没有指定类的情况下
@ConditionalOnNotWebApplication 当前项目不是Web项目的条件下
@ConditionalOnProperty 指定的属性是否有指定的值
@ConditionalOnResource 类路径是否有指定的值
@ConditionalOnSingleCandidate 当前指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean
@ConditionalOnWebApplication 当前项目是Web项目的情况下
@FunctionalInterface
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked
* @return {@code true} if the condition matches and the component can be registered,
* or {@code false} to veto the annotated component's registration
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
//获取ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//获取类加载器
ClassLoader classLoader = conditionContext.getClassLoader();
//获取当前环境信息
Environment environment = conditionContext.getEnvironment();
//获取bean定义的注册类
BeanDefinitionRegistry registry = conditionContext.getRegistry();
实际项目中可以自定义注解并且继承Condition,用于认为判断是否需要实例化该bean