0

I'm making a library to be used with Spring Boot. This lib define some annotations that can be used in methods.

How can I find (at runtime) the package of the application where the library is being used?
I need this in order to scan for the annotated methods.

Lucas Noetzold
  • 1,670
  • 1
  • 13
  • 29
  • Do you want to create spring boot starters? – codeMan Nov 18 '19 at 18:30
  • 1
    You can scan all the classes under @ComponentScan(basePackages = "package.name") like Spring does. Or provide another custom annotation to be added on the main class where client application using the library can use your custom annotation to declare which package to scan. – Rajesh Dwivedi Nov 18 '19 at 18:34
  • @codeMan only if necessary for this to work – Lucas Noetzold Nov 18 '19 at 18:40
  • @RajeshDwivedi how to do the first? – Lucas Noetzold Nov 18 '19 at 18:42
  • 1
    Spring has this code already. Please go through org.springframework.boot.autoconfigure.domain.EntityScanPackages class. It can provide much better insight how spring is handling it. You can align your implementation on same line . https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java#L146 – Rajesh Dwivedi Nov 18 '19 at 18:53
  • Do you want to find packages at the initialization stage or after app launch? – tsarenkotxt Nov 18 '19 at 21:37
  • @tsarenkotxt at the initialization – Lucas Noetzold Nov 18 '19 at 21:44

1 Answers1

1

You can implement BeanFactoryPostProcessor:
1. Using ConfigurableListableBeanFactory you can iterate over BeanDefinition
2. Determine if bean's class has your annotation
3. Get the package from the bean's class name

Example:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Set<String> packages = findAnnotationUsagePackages(MyAnnotation.class, beanFactory);
        ...
    }

    private Set<String> findAnnotationUsagePackages(Class<? extends Annotation> annotationClass,
                                                    ConfigurableListableBeanFactory beanFactory) {
        Set<String> annotationUsagePackages = new HashSet<>();

        for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);

            if (beanDefinition instanceof ScannedGenericBeanDefinition) {
                ScannedGenericBeanDefinition genericBeanDefinition = (ScannedGenericBeanDefinition) beanDefinition;

                if (AnnotationUtils.isCandidateClass(genericBeanDefinition.getBeanClass(), annotationClass)) {
                    String beanClassName = genericBeanDefinition.getBeanClassName();

                    if (beanClassName != null) {
                        annotationUsagePackages.add(ClassUtils.getPackageName(beanClassName));

                    }
                }
            }
        }
        return annotationUsagePackages;
    }

}

About the AnnotationUtils.isCandidateClass():

Determine whether the given class is a candidate for carrying the specified annotation (at type, method or field level)

Also pay attention to the AbstractBeanDefinition.getBeanClass():

Throws: IllegalStateException - if the bean definition does not define a bean class, or a specified bean class name has not been resolved into an actual Class yet

P.S. also you can collect classes or meta-information inside AnnotationUtils.isCandidateClass condition block

tsarenkotxt
  • 3,231
  • 4
  • 22
  • 38
  • my annotation applies only to methods, would it still work? – Lucas Noetzold Nov 19 '19 at 00:54
  • yes `AnnotationUtils.isCandidateClass(beanClass, myAnnotationClass)` will find your annotations that were specified on the methods (as stated in the documentation and I also checked it works) – tsarenkotxt Nov 19 '19 at 01:05