上一章讲到了SpringApplication的bindToSpringApplication的Binder.get(environment)方法, 这章接着讲Binder的bind方法。
bindToSpringApplication方法通过Binder.get(environment)方法得到了Binder实例,再调用bind(String name, Bindable target)方法使用此 binder的property sources绑定指定的目标Bindable。
publicBindResult bind(String name, Bindable target) { return bind(ConfigurationPropertyName.of(name), target, null); } public BindResult bind(ConfigurationPropertyName name, Bindable target, BindHandler handler) { T bound = bind(name, target, handler, false); return BindResult.of(bound); } private T bind(ConfigurationPropertyName name, Bindable 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 bind(ConfigurationPropertyName name, Bindable target, BindHandler handler, Context context, boolean allowRecursiveBinding, boolean create) { try { Bindable 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 staticBindable ofInstance(T instance) { Assert.notNull(instance, "Instance must not be null"); Class type = (Class ) instance.getClass(); return of(type).withExistingValue(instance); } public static Bindable of(Class type) { Assert.notNull(type, "Type must not be null"); return of(ResolvableType.forClass(type)); } public static Bindable 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方法的源码:
privateT handleBindResult(ConfigurationPropertyName name, Bindable 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方法:
staticBindResult of(T value) { if (value == null) { return (BindResult ) 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运行后的结果如下:



