序
这两天再看 公司 之前写的组件的代码,不看不知道,一看吓一跳。。。。这里就说其中一个
不知道你在写组件中的 @Bean 加载的时候 怎么写?
方法一
直接META-INF/spring.factories 写
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xxx.ForeignProxyServiceImpl
复制代码
方法二
定义一个 config类,然后
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xxx.ForeignSdkValidateConfig
复制代码
然后 config中写
@Bean
@ConditionalOnMissingBean
public ForeignCallbackImpl foreignCallbackImpl() {
return new ForeignCallbackImpl();
}
复制代码
小总结
上面2个 哪个更好,肯定是方法二
方法一 对之后的扩展不友好,因为 可能之后有需求,根据type 或者 enable 来决定开启哪几个类,这个时候就会发现 结构不清晰,不好拆分
方法二 可以 对config类 进行控制,结构上也更加清晰
到这 你看看上面的写法还有问题么
没错 就上面这短短几行代码还有问题
问题出在
返回值应该是 接口 ,不能是 实现类
@Bean
@ConditionalOnMissingBean
//返回值应该是 接口 ,不能是 实现类
public ForeignCallbackImpl foreignCallbackImpl() {
return new ForeignCallbackImpl();
}
复制代码
问题
@ConditionalOnMissingBean 和 @ConditionalOnMissingBean(xxx.class) 有区别么?
这就需要知道 @ConditionalOnMissingBean 如果不填的时候 默认值是怎么取的
其中最后的代码在
private void addDeducedBeanTypeForBeanMethod(ConditionContext context, MethodMetadata metadata, final List<String> beanTypes) {
try {
Class<?> returnType = this.getReturnType(context, metadata);
//returnType 获取的是 方法的返回值的类型
beanTypes.add(returnType.getName());
} catch (Throwable var5) {
throw new BeanTypeDeductionException(metadata.getDeclaringClassName(), metadata.getMethodName(), var5);
}
}
复制代码
例子
@Component
@Slf4j
public class ForeignSdkValidateConfig {
@Bean
@ConditionalOnMissingBean
public AImpl foreignCallbackController() {
return new AImpl();
}
}
复制代码
public interface AIn {
}
复制代码
public class AImpl implements AIn {
}
复制代码
这就相当于@ConditionalOnMissingBean(AImpl.class)
可是我们要的是 @ConditionalOnMissingBean(AIn.class)
这样才能让 AIn 只有一个实现的bean
一句话 可以使用 @ConditionalOnMissingBean 无参数 ,但是一定要返回 接口,不能是 实现类
其实这个 一般不会出错,和技术无关, 就是细节上的事
问一个额外的问题
@ConditionalOnMissingBean(AIn.class)
AIn.class 赋值在哪个属性上了
答案是 会赋值在 value 属性上
不赋值的时候 会自动获取 返回值的class 放到value中
具体资料 可以看 docs.oracle.com/javase/tuto…
If there is just one element named `value`, then the name can be omitted, as in:
复制代码
大概意思是 会赋值在 value上,那是不是可以理解为 只要@interface 注解中有value ,默认就可以不指定 value元素了
例子
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestBean {
}
复制代码
你以为到这 就结束了?
到这 会想到 不设置属性会去找 value 是怎么写的,我们可以自定义么?
首先我们要先确定 属于编译期间做的事 还是 解析的时候 做的事
这就涉及 java的编译
大概流程如下
简单的办法就是看class 文件,下面是 class文件的内容,发现还是没有 value=
可以确定是 java 代码执行的时候 带的处理
其实这部分的解析 就是spring 对注解的解析
我们可以先看我们平时怎么获取 class中的注解的
实际代码
Field[] fields = new ForeignSdkValidateConfig().getClass().getDeclaredFields();
Annotation annotation = AnnotationUtils.getAnnotation(fields[0], TestBean.class);
复制代码
真正的解析代码
declAnnos = AnnotationParser.parseAnnotations(
annotations,
sun.misc.SharedSecrets.getJavaLangAccess()
.getConstantPool(getDeclaringClass()),
getDeclaringClass());
复制代码
public static Object parseMemberValue(Class<?> memberType,
ByteBuffer buf,
ConstantPool constPool,
Class<?> container) {
Object result = null;
int tag = buf.get();
switch(tag) {
case 'e':
return parseEnumValue((Class<? extends Enum<?>>)memberType, buf, constPool, container);
case 'c':
result = parseClassValue(buf, constPool, container);
break;
case '@':
result = parseAnnotation(buf, constPool, container, true);
break;
case '[':
return parseArray(memberType, buf, constPool, container);
default:
result = parseConst(tag, buf, constPool);
}
if (!(result instanceof ExceptionProxy) &&
!memberType.isInstance(result))
result = new AnnotationTypeMismatchExceptionProxy(
result.getClass() + "[" + result + "]");
return result;
}
复制代码
默认是value是下面这块,底层调用的native 方法
//和 16个1 进行& 操作,取前16位
int memberNameIndex = buf.getShort() & 0xFFFF;
String memberName = constPool.getUTF8At(memberNameIndex);
public String getUTF8At (int index) { return getUTF8At0 (constantPoolOop, index); }
private native String getUTF8At0 (Object constantPoolOop, int index);
复制代码
private static Object parseClassValue(ByteBuffer buf,
ConstantPool constPool,
Class<?> container) {
int classIndex = buf.getShort() & 0xFFFF;
try {
try {
String sig = constPool.getUTF8At(classIndex);
return parseSig(sig, container);
} catch (IllegalArgumentException ex) {
// support obsolete early jsr175 format class files
return constPool.getClassAt(classIndex);
}
} catch (NoClassDefFoundError e) {
return new TypeNotPresentExceptionProxy("[unknown]", e);
}
catch (TypeNotPresentException e) {
return new TypeNotPresentExceptionProxy(e.typeName(), e.getCause());
}
}
复制代码
value 这个属性的默认值 是写在class 类中的
什么? 你问 @Bean 还有什么常用注解?
@Conditional 满足指定的条件,则进行组件注入,如果不满足,则不注入。
@ConditionalOnBean:表示当容器中存在某个组件才进行组件注入
@ConditionalOnMissingBean:表示当容器中没有某个组件才进行组件注入
思考
Java 的 JIT 编译器和 JavaScript 的 V8 编译器,它们都不约而同地采用了“Sea of Nodes”的 IR 来做优化,这是为什么呢?这种 IR 有什么优势呢?