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.method; 018 019import java.lang.annotation.Annotation; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Collections; 023import java.util.LinkedHashSet; 024import java.util.List; 025import java.util.Set; 026import java.util.function.Predicate; 027 028import org.springframework.core.annotation.AnnotationUtils; 029import org.springframework.util.ClassUtils; 030import org.springframework.util.StringUtils; 031 032/** 033 * A {@code Predicate} to match request handling component types if 034 * <strong>any</strong> of the following selectors match: 035 * <ul> 036 * <li>Base packages -- for selecting handlers by their package. 037 * <li>Assignable types -- for selecting handlers by super type. 038 * <li>Annotations -- for selecting handlers annotated in a specific way. 039 * </ul> 040 * <p>Composability methods on {@link Predicate} can be used : 041 * <pre class="code"> 042 * Predicate<Class<?>> predicate = 043 * HandlerTypePredicate.forAnnotation(RestController.class) 044 * .and(HandlerTypePredicate.forBasePackage("org.example")); 045 * </pre> 046 * 047 * @author Rossen Stoyanchev 048 * @since 5.1 049 */ 050public final class HandlerTypePredicate implements Predicate<Class<?>> { 051 052 private final Set<String> basePackages; 053 054 private final List<Class<?>> assignableTypes; 055 056 private final List<Class<? extends Annotation>> annotations; 057 058 059 /** 060 * Private constructor. See static factory methods. 061 */ 062 private HandlerTypePredicate(Set<String> basePackages, List<Class<?>> assignableTypes, 063 List<Class<? extends Annotation>> annotations) { 064 065 this.basePackages = Collections.unmodifiableSet(basePackages); 066 this.assignableTypes = Collections.unmodifiableList(assignableTypes); 067 this.annotations = Collections.unmodifiableList(annotations); 068 } 069 070 071 @Override 072 public boolean test(Class<?> controllerType) { 073 if (!hasSelectors()) { 074 return true; 075 } 076 else if (controllerType != null) { 077 for (String basePackage : this.basePackages) { 078 if (controllerType.getName().startsWith(basePackage)) { 079 return true; 080 } 081 } 082 for (Class<?> clazz : this.assignableTypes) { 083 if (ClassUtils.isAssignable(clazz, controllerType)) { 084 return true; 085 } 086 } 087 for (Class<? extends Annotation> annotationClass : this.annotations) { 088 if (AnnotationUtils.findAnnotation(controllerType, annotationClass) != null) { 089 return true; 090 } 091 } 092 } 093 return false; 094 } 095 096 private boolean hasSelectors() { 097 return (!this.basePackages.isEmpty() || !this.assignableTypes.isEmpty() || !this.annotations.isEmpty()); 098 } 099 100 101 // Static factory methods 102 103 /** 104 * {@code Predicate} that applies to any handlers. 105 */ 106 public static HandlerTypePredicate forAnyHandlerType() { 107 return new HandlerTypePredicate( 108 Collections.emptySet(), Collections.emptyList(), Collections.emptyList()); 109 } 110 111 /** 112 * Match handlers declared under a base package, e.g. "org.example". 113 * @param packages one or more base package names 114 */ 115 public static HandlerTypePredicate forBasePackage(String... packages) { 116 return new Builder().basePackage(packages).build(); 117 } 118 119 /** 120 * Type-safe alternative to {@link #forBasePackage(String...)} to specify a 121 * base package through a class. 122 * @param packageClasses one or more base package classes 123 */ 124 public static HandlerTypePredicate forBasePackageClass(Class<?>... packageClasses) { 125 return new Builder().basePackageClass(packageClasses).build(); 126 } 127 128 /** 129 * Match handlers that are assignable to a given type. 130 * @param types one or more handler super types 131 */ 132 public static HandlerTypePredicate forAssignableType(Class<?>... types) { 133 return new Builder().assignableType(types).build(); 134 } 135 136 /** 137 * Match handlers annotated with a specific annotation. 138 * @param annotations one or more annotations to check for 139 */ 140 @SafeVarargs 141 public static HandlerTypePredicate forAnnotation(Class<? extends Annotation>... annotations) { 142 return new Builder().annotation(annotations).build(); 143 } 144 145 /** 146 * Return a builder for a {@code HandlerTypePredicate}. 147 */ 148 public static Builder builder() { 149 return new Builder(); 150 } 151 152 153 /** 154 * A {@link HandlerTypePredicate} builder. 155 */ 156 public static class Builder { 157 158 private final Set<String> basePackages = new LinkedHashSet<>(); 159 160 private final List<Class<?>> assignableTypes = new ArrayList<>(); 161 162 private final List<Class<? extends Annotation>> annotations = new ArrayList<>(); 163 164 /** 165 * Match handlers declared under a base package, e.g. "org.example". 166 * @param packages one or more base package classes 167 */ 168 public Builder basePackage(String... packages) { 169 Arrays.stream(packages).filter(StringUtils::hasText).forEach(this::addBasePackage); 170 return this; 171 } 172 173 /** 174 * Type-safe alternative to {@link #forBasePackage(String...)} to specify a 175 * base package through a class. 176 * @param packageClasses one or more base package names 177 */ 178 public Builder basePackageClass(Class<?>... packageClasses) { 179 Arrays.stream(packageClasses).forEach(clazz -> addBasePackage(ClassUtils.getPackageName(clazz))); 180 return this; 181 } 182 183 private void addBasePackage(String basePackage) { 184 this.basePackages.add(basePackage.endsWith(".") ? basePackage : basePackage + "."); 185 } 186 187 /** 188 * Match handlers that are assignable to a given type. 189 * @param types one or more handler super types 190 */ 191 public Builder assignableType(Class<?>... types) { 192 this.assignableTypes.addAll(Arrays.asList(types)); 193 return this; 194 } 195 196 /** 197 * Match types that are annotated with one of the given annotations. 198 * @param annotations one or more annotations to check for 199 */ 200 @SuppressWarnings("unchecked") 201 public final Builder annotation(Class<? extends Annotation>... annotations) { 202 this.annotations.addAll(Arrays.asList(annotations)); 203 return this; 204 } 205 206 public HandlerTypePredicate build() { 207 return new HandlerTypePredicate(this.basePackages, this.assignableTypes, this.annotations); 208 } 209 } 210 211}