package com.jspxcms.common.web; import java.beans.PropertyEditorSupport; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.MethodParameter; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.web.PageableDefaults; import org.springframework.validation.DataBinder; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.util.WebUtils; /** * 分页参数获取类 * * @author liufang * */ public class PageableArgumentResolver implements HandlerMethodArgumentResolver { private static final Pageable DEFAULT_PAGE_REQUEST = new PageRequest(0, 15); private static final String DEFAULT_PREFIX = "page"; private static final String DEFAULT_SEPARATOR = "_"; private static final String DEFAULT_COOKIE_NAME = "page_size"; private Pageable fallbackPagable = DEFAULT_PAGE_REQUEST; private String prefix = DEFAULT_PREFIX; private String separator = DEFAULT_SEPARATOR; private String cookieName = DEFAULT_COOKIE_NAME; /** * Setter to configure a fallback instance of {@link Pageable} that is being * used to back missing parameters. Defaults to * {@value #DEFAULT_PAGE_REQUEST}. * * @param fallbackPagable * the fallbackPagable to set */ public void setFallbackPagable(Pageable fallbackPagable) { this.fallbackPagable = null == fallbackPagable ? DEFAULT_PAGE_REQUEST : fallbackPagable; } /** * Setter to configure the prefix of request parameters to be used to * retrieve paging information. Defaults to {@link #DEFAULT_PREFIX}. * * @param prefix * the prefix to set */ public void setPrefix(String prefix) { this.prefix = null == prefix ? DEFAULT_PREFIX : prefix; } /** * Setter to configure the separator between prefix and actual property * value. Defaults to {@link #DEFAULT_SEPARATOR}. * * @param separator * the separator to set */ public void setSeparator(String separator) { this.separator = null == separator ? DEFAULT_SEPARATOR : separator; } public void setCookieName(String cookieName) { this.cookieName = cookieName; } public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().equals(Pageable.class); } public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { assertPageableUniqueness(parameter); HttpServletRequest servletRequest = (HttpServletRequest) webRequest .getNativeRequest(); Pageable request = getDefaultFromCookieOrAnnotationOrFallback( parameter, servletRequest); String prefix = getPrefix(parameter); Map<String, Object> map = WebUtils.getParametersStartingWith( servletRequest, prefix + separator); String pageStr = servletRequest.getParameter(prefix); if (StringUtils.isNotBlank(pageStr)) { int page = NumberUtils.toInt(pageStr, 0); if (page < 0) { page = 0; } map.put("page", page); } PropertyValues propertyValues = new MutablePropertyValues(map); // PropertyValues propertyValues = new // ServletRequestParameterPropertyValues( // servletRequest, prefix, separator); DataBinder binder = new ServletRequestDataBinder(request); binder.initDirectFieldAccess(); binder.registerCustomEditor(Sort.class, new SortPropertyEditor( "sort_dir", propertyValues)); binder.bind(propertyValues); if (request.getPageNumber() > 0) { request = new PageRequest(request.getPageNumber() - 1, request.getPageSize(), request.getSort()); } return request; } private Pageable getDefaultFromCookieOrAnnotationOrFallback( MethodParameter methodParameter, HttpServletRequest servletRequest) { Cookie cookie = WebUtils.getCookie(servletRequest, cookieName); Integer pageSize = null; if (cookie != null) { String value = cookie.getValue(); int ps = NumberUtils.toInt(value, 0); if (ps > 0) { pageSize = ps; } } // search for PageableDefaults annotation for (Annotation annotation : methodParameter.getParameterAnnotations()) { if (annotation instanceof PageableDefaults) { return getDefaultPageRequestFrom((PageableDefaults) annotation, pageSize); } } // Construct request with fallback request to ensure sensible // default values. Create fresh copy as Spring will manipulate the // instance under the covers if (pageSize == null) { pageSize = fallbackPagable.getPageSize(); } return new PageRequest(fallbackPagable.getPageNumber(), pageSize, fallbackPagable.getSort()); } private static Pageable getDefaultPageRequestFrom( PageableDefaults defaults, Integer pageSize) { // +1 is because we substract 1 later int defaultPageNumber = defaults.pageNumber() + 1; if (pageSize == null) { pageSize = defaults.value(); } if (defaults.sort().length == 0) { return new PageRequest(defaultPageNumber, pageSize); } return new PageRequest(defaultPageNumber, pageSize, defaults.sortDir(), defaults.sort()); } /** * Resolves the prefix to use to bind properties from. Will prepend a * possible {@link Qualifier} if available or return the configured prefix * otherwise. * * @param parameter * @return */ private String getPrefix(MethodParameter parameter) { for (Annotation annotation : parameter.getParameterAnnotations()) { if (annotation instanceof Qualifier) { return new StringBuilder(((Qualifier) annotation).value()) .append("_").append(prefix).toString(); } } return prefix; } /** * Asserts uniqueness of all {@link Pageable} parameters of the method of * the given {@link MethodParameter}. * * @param parameter */ private void assertPageableUniqueness(MethodParameter parameter) { Method method = parameter.getMethod(); if (containsMoreThanOnePageableParameter(method)) { Annotation[][] annotations = method.getParameterAnnotations(); assertQualifiersFor(method.getParameterTypes(), annotations); } } /** * Returns whether the given {@link Method} has more than one * {@link Pageable} parameter. * * @param method * @return */ private boolean containsMoreThanOnePageableParameter(Method method) { boolean pageableFound = false; for (Class<?> type : method.getParameterTypes()) { if (pageableFound && type.equals(Pageable.class)) { return true; } if (type.equals(Pageable.class)) { pageableFound = true; } } return false; } /** * Asserts that every {@link Pageable} parameter of the given parameters * carries an {@link Qualifier} annotation to distinguish them from each * other. * * @param parameterTypes * @param annotations */ private void assertQualifiersFor(Class<?>[] parameterTypes, Annotation[][] annotations) { Set<String> values = new HashSet<String>(); for (int i = 0; i < annotations.length; i++) { if (Pageable.class.equals(parameterTypes[i])) { Qualifier qualifier = findAnnotation(annotations[i]); if (null == qualifier) { throw new IllegalStateException( "Ambiguous Pageable arguments in handler method. If you use multiple parameters of type Pageable you need to qualify them with @Qualifier"); } if (values.contains(qualifier.value())) { throw new IllegalStateException( "Values of the user Qualifiers must be unique!"); } values.add(qualifier.value()); } } } /** * Returns a {@link Qualifier} annotation from the given array of * {@link Annotation}s. Returns {@literal null} if the array does not * contain a {@link Qualifier} annotation. * * @param annotations * @return */ private Qualifier findAnnotation(Annotation[] annotations) { for (Annotation annotation : annotations) { if (annotation instanceof Qualifier) { return (Qualifier) annotation; } } return null; } /** * {@link java.beans.PropertyEditor} to create {@link Sort} instances from * textual representations. The implementation interprets the string as a * comma separated list where the first entry is the sort direction ( * {@code asc}, {@code desc}) followed by the properties to sort by. * * @author Oliver Gierke */ private static class SortPropertyEditor extends PropertyEditorSupport { private final String orderProperty; private final PropertyValues values; /** * Creates a new {@link SortPropertyEditor}. * * @param orderProperty * @param values */ public SortPropertyEditor(String orderProperty, PropertyValues values) { this.orderProperty = orderProperty; this.values = values; } /* * (non-Javadoc) * * @see java.beans.PropertyEditorSupport#setAsText(java.lang.String) */ @Override public void setAsText(String text) { PropertyValue rawOrder = values.getPropertyValue(orderProperty); Direction order = null == rawOrder ? Direction.ASC : Direction .fromString(rawOrder.getValue().toString()); setValue(new Sort(order, text)); } } }