上一章讲到了SpringApplication的bindToSpringApplication的Binder.get(environment)方法, 这章接着讲Binder的bind方法。 bindToSpringApplication方法通过Binder.get(environment)方法得到了Binder实例,再调用bind(String name, Bindable target)方法使用此 binder的property sources绑定指定的目标Bindable。
public <T> BindResult<T> bind(String name, Bindable<T> target) {
return bind(ConfigurationPropertyName.of(name), target, null);
}
public <T> BindResult<T> bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler) {
T bound = bind(name, target, handler, false);
return BindResult.of(bound);
}
private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, boolean create) {
Assert.notNull(name, "Name must not be null");
Assert.notNull(target, "Target must not be null");
handler = (handler != null) ? handler : this.defaultBindHandler;
Context context = new Context();
return bind(name, target, handler, context, false, create);
}
private <T> T bind(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler, Context context,
boolean allowRecursiveBinding, boolean create) {
try {
Bindable<T> replacementTarget = handler.onStart(name, target, context);
if (replacementTarget == null) {
return handleBindResult(name, target, handler, context, null, create);
}
target = replacementTarget;
Object bound = bindObject(name, target, handler, context, allowRecursiveBinding);
return handleBindResult(name, target, handler, context, bound, create);
}
catch (Exception ex) {
return handleBindError(name, target, handler, context, ex);
}
}
Bindable是可以由Binder绑定的源,其ofInstance方法使用与该实例相等的现有值创建指定实例类型的新Bindable。
public static <T> Bindable<T> ofInstance(T instance) {
Assert.notNull(instance, "Instance must not be null");
Class<T> type = (Class<T>) instance.getClass();
return of(type).withExistingValue(instance);
}
public static <T> Bindable<T> of(Class<T> type) {
Assert.notNull(type, "Type must not be null");
return of(ResolvableType.forClass(type));
}
public static <T> Bindable<T> of(ResolvableType type) {
Assert.notNull(type, "Type must not be null");
ResolvableType boxedType = box(type);
return new Bindable<>(type, boxedType, null, NO_ANNOTATIONS, NO_BIND_RESTRICTIONS);
}
private static ResolvableType box(ResolvableType type) {
Class<?> resolved = type.resolve();
if (resolved != null && resolved.isPrimitive()) {
Object array = Array.newInstance(resolved, 1);
Class<?> wrapperType = Array.get(array, 0).getClass();
return ResolvableType.forClass(wrapperType);
}
if (resolved != null && resolved.isArray()) {
return ResolvableType.forArrayComponent(box(type.getComponentType()));
}
return type;
}
调用Bindable类的构造方法时,传入的参数如下: 在调用bind(ConfigurationPropertyName name, Bindable target, BindHandler handler, Context context, boolean allowRecursiveBinding, boolean create)方法时,传入的参数如下: handleBindResult方法的源码:
private <T> T handleBindResult(ConfigurationPropertyName name, Bindable<T> target, BindHandler handler,
Context context, Object result, boolean create) throws Exception {
if (result != null) {
result = handler.onSuccess(name, target, context, result);
result = context.getConverter().convert(result, target);
}
if (result == null && create) {
result = create(target, context);
result = handler.onCreate(name, target, context, result);
result = context.getConverter().convert(result, target);
Assert.state(result != null, () -> "Unable to create instance for " + target.getType());
}
handler.onFinish(name, target, context, result);
return context.getConverter().convert(result, target);
}
获取到的bound为null: BindResult用于返回Binder绑定操作结果的容器对象,可能包含成功绑定的对象或空结果。BindResult的of方法:
static <T> BindResult<T> of(T value) {
if (value == null) {
return (BindResult<T>) UNBOUND;
}
return new BindResult<>(value);
}
private static final BindResult<?> UNBOUND = new BindResult<>(null);
private BindResult(T value) {
this.value = value;
}
在执行完bindToSpringApplication方法后,会判断当前环境是否是自定义环境,如果是,则会调用convertEnvironment方法:
@Deprecated
public StandardEnvironment convertEnvironment(ConfigurableEnvironment environment) {
return new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
由于绑定后的环境可能会被修改,所以需要再次执行ConfigurationPropertySources.attach方法 SpringApplication的configureIgnoreBeanInfo方法:
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
}
}
CachedIntrospectionResults是为Java 类缓存JavaBeans PropertyDescriptor信息的类,该类不适合应用程序代码直接使用。Spring自己在应用程序ClassLoader中缓存 bean 描述符所必需的,而不是依赖于 JDK 的系统范围的BeanInfo缓存(以避免在共享 JVM 中个别应用程序关闭时泄漏)。信息是静态缓存的,因此我们不需要为我们操作的每个 JavaBean 创建此类的新对象。因此,该类实现了工厂设计模式,使用私有构造函数和静态forClass(Class)工厂方法来获取实例。
public static final String IGNORE_BEANINFO_PROPERTY_NAME = "spring.beaninfo.ignore";
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
printBanner方法用于打印Spring应用程序横幅 这里获取到banner是SpringBootBanner
class SpringBootBanner implements Banner {
private static final String[] BANNER = { "", " . ____ _ __ _ _",
" /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
" \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", " ' |____| .__|_| |_|_| |_\\__, | / / / /",
" =========|_|==============|___/=/_/_/_/" };
private static final String SPRING_BOOT = " :: Spring Boot :: ";
private static final int STRAP_LINE_SIZE = 42;
@Override
public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
for (String line : BANNER) {
printStream.println(line);
}
String version = SpringBootVersion.getVersion();
version = (version != null) ? " (v" + version + ")" : "";
StringBuilder padding = new StringBuilder();
while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
padding.append(" ");
}
printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
AnsiStyle.FAINT, version));
printStream.println();
}
}
printBanner运行后的结果如下:
|