BVAL-170 perf boost for CDI integrations startup
diff --git a/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java b/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java
index c564823..6b15b2b 100644
--- a/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java
+++ b/bval-jsr/src/main/java/org/apache/bval/cdi/BValExtension.java
@@ -18,18 +18,22 @@
  */
 package org.apache.bval.cdi;
 
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.util.stream.Stream;
 
 import javax.enterprise.context.spi.CreationalContext;
 import javax.enterprise.event.Observes;
 import javax.enterprise.inject.spi.AfterBeanDiscovery;
-import javax.enterprise.inject.spi.AnnotatedCallable;
+import javax.enterprise.inject.spi.AfterDeploymentValidation;
+import javax.enterprise.inject.spi.Annotated;
 import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.Bean;
 import javax.enterprise.inject.spi.BeanManager;
@@ -41,14 +45,14 @@
 import javax.enterprise.inject.spi.ProcessBean;
 import javax.validation.BootstrapConfiguration;
 import javax.validation.Configuration;
+import javax.validation.Constraint;
+import javax.validation.Valid;
 import javax.validation.Validation;
 import javax.validation.ValidationException;
 import javax.validation.Validator;
 import javax.validation.ValidatorFactory;
 import javax.validation.executable.ExecutableType;
 import javax.validation.executable.ValidateOnExecution;
-import javax.validation.metadata.BeanDescriptor;
-import javax.validation.metadata.MethodType;
 
 import org.apache.bval.jsr.ConfigurationImpl;
 import org.apache.bval.jsr.util.ExecutableTypes;
@@ -75,11 +79,13 @@
 
     private final Configuration<?> config;
     private Lazy<ValidatorFactory> factory;
-    private Lazy<Validator> validator;
 
     private Set<ExecutableType> globalExecutableTypes;
     private boolean isExecutableValidationEnabled;
 
+    private final Collection<Class<?>> potentiallyBValAnnotation = new HashSet<>();
+    private final Collection<Class<?>> notBValAnnotation = new HashSet<>();
+
     public BValExtension() { // read the config, could be done in a quicker way but this let us get defaults without duplicating code
         config = Validation.byDefaultProvider().configure();
         try {
@@ -97,21 +103,6 @@
         }
     }
 
-    // lazily to get a small luck to have CDI in place
-    private void ensureFactoryValidator() {
-        if (validator != null) {
-            return;
-        }
-        if (config instanceof ConfigurationImpl) {
-            // ignore parts of the config relying on CDI since we didn't start yet
-            ((ConfigurationImpl) config).deferBootstrapOverrides();
-        }
-        if (factory == null) {
-            factory = new Lazy<>(config::buildValidatorFactory);
-        }
-        validator = new Lazy<>(() -> factory.get().getValidator());
-    }
-
     public Set<ExecutableType> getGlobalExecutableTypes() {
         return globalExecutableTypes;
     }
@@ -133,43 +124,27 @@
         }
         final Class<A> javaClass = annotatedType.getJavaClass();
         final int modifiers = javaClass.getModifiers();
-        if (!javaClass.isInterface() && !Modifier.isFinal(modifiers) && !Modifier.isAbstract(modifiers)) {
+        if (!javaClass.isInterface() && !javaClass.isAnonymousClass() && !Modifier.isFinal(modifiers) && !Modifier.isAbstract(modifiers)) {
             try {
-                ensureFactoryValidator();
-                try {
-                    final BeanDescriptor classConstraints = validator.get().getConstraintsForClass(javaClass);
-
-                    final boolean validConstructors = globalExecutableTypes.contains(ExecutableType.CONSTRUCTORS)
-                        && !classConstraints.getConstrainedConstructors().isEmpty();
-                    final boolean validBusinessMethods =
-                        globalExecutableTypes.contains(ExecutableType.NON_GETTER_METHODS)
-                            && !classConstraints.getConstrainedMethods(MethodType.NON_GETTER).isEmpty();
-                    final boolean validGetterMethods = globalExecutableTypes.contains(ExecutableType.GETTER_METHODS)
-                        && !classConstraints.getConstrainedMethods(MethodType.GETTER).isEmpty();
-
-                    if (annotatedType.isAnnotationPresent(ValidateOnExecution.class)
-                        || hasValidationAnnotation(annotatedType.getMethods())
-                        || hasValidationAnnotation(annotatedType.getConstructors()) || validConstructors
-                        || validBusinessMethods || validGetterMethods) {
-                        pat.setAnnotatedType(new BValAnnotatedType<>(annotatedType));
-                    }
-                } catch (final NoClassDefFoundError ncdfe) {
-                    // skip
+                if (hasValidation(annotatedType)
+                    || hasValidationAnnotation(annotatedType.getMethods())
+                    || hasValidationAnnotation(annotatedType.getConstructors())
+                    || Stream.concat(annotatedType.getMethods().stream(), annotatedType.getConstructors().stream())
+                        .flatMap(it -> it.getParameters().stream())
+                        .anyMatch(this::hasValidation)) {
+                    pat.setAnnotatedType(new BValAnnotatedType<>(annotatedType));
                 }
             } catch (final Exception e) {
                 if (e instanceof ValidationException) {
                     throw e;
                 }
                 LOGGER.log(Level.INFO, e.getMessage());
+            } catch (final NoClassDefFoundError ncdfe) {
+                // skip
             }
         }
     }
 
-    private static <A> boolean hasValidationAnnotation(
-        final Collection<? extends AnnotatedCallable<? super A>> methods) {
-        return methods.stream().anyMatch(m -> m.isAnnotationPresent(ValidateOnExecution.class));
-    }
-
     public <A> void processBean(final @Observes ProcessBean<A> processBeanEvent) {
         if (validatorFound && validatorFactoryFound) {
             return;
@@ -219,6 +194,73 @@
         }
     }
 
+    public void afterStart(@Observes final AfterDeploymentValidation clearEvent) {
+        potentiallyBValAnnotation.clear();
+        notBValAnnotation.clear();
+    }
+
+    private boolean hasValidationAnnotation(final Collection<? extends Annotated> annotateds) {
+        return annotateds.stream().anyMatch(this::hasValidation);
+    }
+
+    private boolean hasValidation(final Annotated m) {
+        return m.getAnnotations().stream()
+                .anyMatch(it -> {
+                    final Class<? extends Annotation> type = it.annotationType();
+                    if (type == ValidateOnExecution.class || type == Valid.class) {
+                        return true;
+                    }
+                    if (isSkippedAnnotation(type)) {
+                        return false;
+                    }
+                    if (type.getName().startsWith("javax.validation.constraints")) {
+                        return true;
+                    }
+                    if (notBValAnnotation.contains(type)) { // more likely so faster first
+                        return false;
+                    }
+                    if (potentiallyBValAnnotation.contains(type)) {
+                        return true;
+                    }
+                    cacheIsBvalAnnotation(type);
+                    return potentiallyBValAnnotation.contains(type);
+                });
+    }
+
+    private boolean isSkippedAnnotation(final Class<? extends Annotation> type) {
+        if (type.getName().startsWith("java.")) {
+            return true;
+        }
+        if (type.getName().startsWith("javax.enterprise.")) {
+            return true;
+        }
+        if (type.getName().startsWith("javax.inject.")) {
+            return true;
+        }
+        return false;
+    }
+
+    private void cacheIsBvalAnnotation(final Class<? extends Annotation> type) {
+        if (flattenAnnotations(type, new HashSet<>()).anyMatch(it -> it == Constraint.class)) {
+            potentiallyBValAnnotation.add(type);
+        } else {
+            notBValAnnotation.add(type);
+        }
+    }
+
+    private Stream<Class<?>> flattenAnnotations(final Class<? extends Annotation> type, final Set<Class<?>> seen) {
+        seen.add(type);
+        return Stream.of(type)
+                     .flatMap(it -> Stream.concat(
+                             Stream.of(it),
+                             Stream.of(it.getAnnotations())
+                                   .map(Annotation::annotationType)
+                                   .distinct()
+                                   .filter(a -> !isSkippedAnnotation(a))
+                                   .filter(seen::add)
+                                   .flatMap(a -> flattenAnnotations(a, seen))));
+    }
+
     /**
      * Request that an instance of the specified type be provided by the container.
      * @param clazz