001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.bind.support; 018 019import org.springframework.beans.PropertyEditorRegistrar; 020import org.springframework.core.convert.ConversionService; 021import org.springframework.lang.Nullable; 022import org.springframework.validation.BindingErrorProcessor; 023import org.springframework.validation.MessageCodesResolver; 024import org.springframework.validation.Validator; 025import org.springframework.web.bind.WebDataBinder; 026 027/** 028 * Convenient {@link WebBindingInitializer} for declarative configuration 029 * in a Spring application context. Allows for reusing pre-configured 030 * initializers with multiple controller/handlers. 031 * 032 * @author Juergen Hoeller 033 * @since 2.5 034 * @see #setDirectFieldAccess 035 * @see #setMessageCodesResolver 036 * @see #setBindingErrorProcessor 037 * @see #setValidator(Validator) 038 * @see #setConversionService(ConversionService) 039 * @see #setPropertyEditorRegistrar 040 */ 041public class ConfigurableWebBindingInitializer implements WebBindingInitializer { 042 043 private boolean autoGrowNestedPaths = true; 044 045 private boolean directFieldAccess = false; 046 047 @Nullable 048 private MessageCodesResolver messageCodesResolver; 049 050 @Nullable 051 private BindingErrorProcessor bindingErrorProcessor; 052 053 @Nullable 054 private Validator validator; 055 056 @Nullable 057 private ConversionService conversionService; 058 059 @Nullable 060 private PropertyEditorRegistrar[] propertyEditorRegistrars; 061 062 063 /** 064 * Set whether a binder should attempt to "auto-grow" a nested path that contains a null value. 065 * <p>If "true", a null path location will be populated with a default object value and traversed 066 * instead of resulting in an exception. This flag also enables auto-growth of collection elements 067 * when accessing an out-of-bounds index. 068 * <p>Default is "true" on a standard DataBinder. Note that this feature is only supported 069 * for bean property access (DataBinder's default mode), not for field access. 070 * @see org.springframework.validation.DataBinder#initBeanPropertyAccess() 071 * @see org.springframework.validation.DataBinder#setAutoGrowNestedPaths 072 */ 073 public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) { 074 this.autoGrowNestedPaths = autoGrowNestedPaths; 075 } 076 077 /** 078 * Return whether a binder should attempt to "auto-grow" a nested path that contains a null value. 079 */ 080 public boolean isAutoGrowNestedPaths() { 081 return this.autoGrowNestedPaths; 082 } 083 084 /** 085 * Set whether to use direct field access instead of bean property access. 086 * <p>Default is {@code false}, using bean property access. 087 * Switch this to {@code true} in order to enforce direct field access. 088 * @see org.springframework.validation.DataBinder#initDirectFieldAccess() 089 * @see org.springframework.validation.DataBinder#initBeanPropertyAccess() 090 */ 091 public final void setDirectFieldAccess(boolean directFieldAccess) { 092 this.directFieldAccess = directFieldAccess; 093 } 094 095 /** 096 * Return whether to use direct field access instead of bean property access. 097 */ 098 public boolean isDirectFieldAccess() { 099 return this.directFieldAccess; 100 } 101 102 /** 103 * Set the strategy to use for resolving errors into message codes. 104 * Applies the given strategy to all data binders used by this controller. 105 * <p>Default is {@code null}, i.e. using the default strategy of 106 * the data binder. 107 * @see org.springframework.validation.DataBinder#setMessageCodesResolver 108 */ 109 public final void setMessageCodesResolver(@Nullable MessageCodesResolver messageCodesResolver) { 110 this.messageCodesResolver = messageCodesResolver; 111 } 112 113 /** 114 * Return the strategy to use for resolving errors into message codes. 115 */ 116 @Nullable 117 public final MessageCodesResolver getMessageCodesResolver() { 118 return this.messageCodesResolver; 119 } 120 121 /** 122 * Set the strategy to use for processing binding errors, that is, 123 * required field errors and {@code PropertyAccessException}s. 124 * <p>Default is {@code null}, that is, using the default strategy 125 * of the data binder. 126 * @see org.springframework.validation.DataBinder#setBindingErrorProcessor 127 */ 128 public final void setBindingErrorProcessor(@Nullable BindingErrorProcessor bindingErrorProcessor) { 129 this.bindingErrorProcessor = bindingErrorProcessor; 130 } 131 132 /** 133 * Return the strategy to use for processing binding errors. 134 */ 135 @Nullable 136 public final BindingErrorProcessor getBindingErrorProcessor() { 137 return this.bindingErrorProcessor; 138 } 139 140 /** 141 * Set the Validator to apply after each binding step. 142 */ 143 public final void setValidator(@Nullable Validator validator) { 144 this.validator = validator; 145 } 146 147 /** 148 * Return the Validator to apply after each binding step, if any. 149 */ 150 @Nullable 151 public final Validator getValidator() { 152 return this.validator; 153 } 154 155 /** 156 * Specify a ConversionService which will apply to every DataBinder. 157 * @since 3.0 158 */ 159 public final void setConversionService(@Nullable ConversionService conversionService) { 160 this.conversionService = conversionService; 161 } 162 163 /** 164 * Return the ConversionService which will apply to every DataBinder. 165 */ 166 @Nullable 167 public final ConversionService getConversionService() { 168 return this.conversionService; 169 } 170 171 /** 172 * Specify a single PropertyEditorRegistrar to be applied to every DataBinder. 173 */ 174 public final void setPropertyEditorRegistrar(PropertyEditorRegistrar propertyEditorRegistrar) { 175 this.propertyEditorRegistrars = new PropertyEditorRegistrar[] {propertyEditorRegistrar}; 176 } 177 178 /** 179 * Specify multiple PropertyEditorRegistrars to be applied to every DataBinder. 180 */ 181 public final void setPropertyEditorRegistrars(@Nullable PropertyEditorRegistrar[] propertyEditorRegistrars) { 182 this.propertyEditorRegistrars = propertyEditorRegistrars; 183 } 184 185 /** 186 * Return the PropertyEditorRegistrars to be applied to every DataBinder. 187 */ 188 @Nullable 189 public final PropertyEditorRegistrar[] getPropertyEditorRegistrars() { 190 return this.propertyEditorRegistrars; 191 } 192 193 194 @Override 195 public void initBinder(WebDataBinder binder) { 196 binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths); 197 if (this.directFieldAccess) { 198 binder.initDirectFieldAccess(); 199 } 200 if (this.messageCodesResolver != null) { 201 binder.setMessageCodesResolver(this.messageCodesResolver); 202 } 203 if (this.bindingErrorProcessor != null) { 204 binder.setBindingErrorProcessor(this.bindingErrorProcessor); 205 } 206 if (this.validator != null && binder.getTarget() != null && 207 this.validator.supports(binder.getTarget().getClass())) { 208 binder.setValidator(this.validator); 209 } 210 if (this.conversionService != null) { 211 binder.setConversionService(this.conversionService); 212 } 213 if (this.propertyEditorRegistrars != null) { 214 for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) { 215 propertyEditorRegistrar.registerCustomEditors(binder); 216 } 217 } 218 } 219 220}