All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micronaut.annotation.processing.BeanDefinitionInjectProcessor Maven / Gradle / Ivy

/*
 * Copyright 2017-2021 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.micronaut.annotation.processing;

import io.micronaut.annotation.processing.visitor.JavaClassElement;
import io.micronaut.annotation.processing.visitor.JavaNativeElement;
import io.micronaut.context.annotation.ConfigurationReader;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationUtil;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.Vetoed;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.inject.ast.annotation.ElementAnnotationMetadataFactory;
import io.micronaut.inject.processing.BeanDefinitionCreator;
import io.micronaut.inject.processing.BeanDefinitionCreatorFactory;
import io.micronaut.inject.processing.JavaModelUtils;
import io.micronaut.inject.processing.ProcessingException;
import io.micronaut.inject.visitor.BeanElementVisitor;
import io.micronaut.inject.writer.AbstractBeanDefinitionBuilder;
import io.micronaut.inject.writer.BeanDefinitionVisitor;
import io.micronaut.inject.writer.BeanDefinitionWriter;

import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static javax.lang.model.element.ElementKind.ENUM;

/**
 * 

The core annotation processor used to generate bean definitions and power AOP for Micronaut.

* *

Each dependency injection candidate is visited and {@link BeanDefinitionWriter} is used to produce byte code via ASM. * Each bean results in a instanceof {@link io.micronaut.inject.BeanDefinition}

* * @author Graeme Rocher * @author Dean Wette * @since 1.0 */ @Internal @SupportedOptions({AbstractInjectAnnotationProcessor.MICRONAUT_PROCESSING_INCREMENTAL, AbstractInjectAnnotationProcessor.MICRONAUT_PROCESSING_ANNOTATIONS, BeanDefinitionWriter.OMIT_CONFPROP_INJECTION_POINTS}) public class BeanDefinitionInjectProcessor extends AbstractInjectAnnotationProcessor { private static final String[] ANNOTATION_STEREOTYPES = new String[]{ AnnotationUtil.POST_CONSTRUCT, AnnotationUtil.PRE_DESTROY, "jakarta.annotation.PreDestroy", "jakarta.annotation.PostConstruct", "jakarta.inject.Inject", "jakarta.inject.Qualifier", "jakarta.inject.Singleton", "jakarta.inject.Inject", "jakarta.inject.Qualifier", "jakarta.inject.Singleton", "io.micronaut.context.annotation.Bean", "io.micronaut.context.annotation.Replaces", "io.micronaut.context.annotation.Value", "io.micronaut.context.annotation.Property", "io.micronaut.context.annotation.Executable", AnnotationUtil.ANN_AROUND, AnnotationUtil.ANN_INTERCEPTOR_BINDINGS, AnnotationUtil.ANN_INTERCEPTOR_BINDING, AnnotationUtil.ANN_INTRODUCTION }; private Set beanDefinitions; private final Set processed = new HashSet<>(); private final Map postponed = new HashMap<>(); @Override public final synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.beanDefinitions = new LinkedHashSet<>(); for (BeanElementVisitor visitor : BeanElementVisitor.VISITORS) { if (visitor.isEnabled()) { try { visitor.start(javaVisitorContext); } catch (Exception e) { javaVisitorContext.fail("Error initializing bean element visitor [" + visitor.getClass().getName() + "]: " + e.getMessage(), null); } } } } @Override public final boolean process(Set annotations, RoundEnvironment roundEnv) { boolean processingOver = roundEnv.processingOver(); if (!processingOver) { JavaAnnotationMetadataBuilder annotationMetadataBuilder = javaVisitorContext.getAnnotationMetadataBuilder(); annotations = annotations .stream() .filter(ann -> { final String name = ann.getQualifiedName().toString(); String packageName = NameUtils.getPackageName(name); return !name.equals(AnnotationUtil.KOTLIN_METADATA) && !AnnotationUtil.STEREOTYPE_EXCLUDES.contains(packageName); }) .filter(ann -> annotationMetadataBuilder.lookupOrBuildForType(ann).hasStereotype(ANNOTATION_STEREOTYPES) || isProcessedAnnotation(ann.getQualifiedName().toString())) .collect(Collectors.toSet()); if (!annotations.isEmpty()) { TypeElement groovyObjectTypeElement = elementUtils.getTypeElement("groovy.lang.GroovyObject"); TypeMirror groovyObjectType = groovyObjectTypeElement != null ? groovyObjectTypeElement.asType() : null; // accumulate all the class elements for all annotated elements annotations.forEach(annotation -> modelUtils.resolveTypeElements( roundEnv.getElementsAnnotatedWith(annotation) ) .forEach(typeElement -> { if (typeElement.getKind() == ENUM) { final AnnotationMetadata am = annotationMetadataBuilder.lookupOrBuildForType(typeElement); if (BeanDefinitionCreatorFactory.isDeclaredBeanInMetadata(am)) { error(typeElement, "Enum types cannot be defined as beans"); } return; } // skip Groovy code, handled by InjectTransform. Required for GroovyEclipse compiler if ((groovyObjectType != null && typeUtils.isAssignable(typeElement.asType(), groovyObjectType))) { return; } String name = typeElement.getQualifiedName().toString(); if (beanDefinitions.contains(name) || processed.contains(name) || name.endsWith(BeanDefinitionVisitor.PROXY_SUFFIX)) { return; } boolean isInterface = JavaModelUtils.resolveKind(typeElement, ElementKind.INTERFACE).isPresent(); if (!isInterface) { beanDefinitions.add(name); } else { AnnotationMetadata annotationMetadata = annotationMetadataBuilder.lookupOrBuildForType(typeElement); if (BeanDefinitionCreatorFactory.isIntroduction(annotationMetadata) || annotationMetadata.hasStereotype(ConfigurationReader.class)) { beanDefinitions.add(name); } } })); } // remove already processed in previous round for (String name : processed) { beanDefinitions.remove(name); postponed.remove(name); } // process remaining int count = beanDefinitions.size(); if (count > 0) { note("Creating bean classes for %s type elements", count); ElementAnnotationMetadataFactory annotationMetadataFactory = javaVisitorContext.getElementAnnotationMetadataFactory().readOnly(); for (String className : beanDefinitions) { if (processed.add(className)) { final TypeElement typeElement = elementUtils.getTypeElement(className); try { Name classElementQualifiedName = typeElement.getQualifiedName(); if ("java.lang.Record".equals(classElementQualifiedName.toString())) { continue; } JavaClassElement classElement = javaVisitorContext.getElementFactory() .newClassElement(typeElement, annotationMetadataFactory); if (classElement.hasAnnotation(Vetoed.class) || classElement.getPackage().hasAnnotation(Vetoed.class)) { continue; } BeanDefinitionCreator beanDefinitionCreator = BeanDefinitionCreatorFactory.produce(classElement, javaVisitorContext); for (BeanDefinitionVisitor writer : beanDefinitionCreator.build()) { if (processed.contains(writer.getBeanDefinitionName())) { throw new IllegalStateException("Already processed: " + writer.getBeanDefinitionName()); } else { processBeanDefinitions(writer); processed.add(writer.getBeanDefinitionName()); } } } catch (ProcessingException ex) { error(((JavaNativeElement) ex.getOriginatingElement()).element(), ex.getMessage()); } catch (PostponeToNextRoundException e) { processed.remove(className); postponed.put(className, e); } } } } } /* Since the underlying Filer expects us to write only once into a file we need to make sure it happens in the last processing round. */ if (processingOver) { for (Map.Entry e : postponed.entrySet()) { javaVisitorContext.warn("Bean definition generation [" + e.getKey() + "] skipped from processing because of prior error: [" + e.getValue().getPath() + "]." + " This error is normally due to missing classes on the classpath. Verify the compilation classpath is correct to resolve the problem.", (Element) e.getValue().getErrorElement()); } try { writeBeanDefinitionsToMetaInf(); for (BeanElementVisitor visitor : BeanElementVisitor.VISITORS) { if (visitor.isEnabled()) { try { visitor.finish(javaVisitorContext); } catch (Exception e) { javaVisitorContext.fail("Error finalizing bean element visitor [" + visitor.getClass().getName() + "]: " + e.getMessage(), null); } } } final List beanElementBuilders = javaVisitorContext.getBeanElementBuilders(); if (CollectionUtils.isNotEmpty(beanElementBuilders)) { try { AbstractBeanDefinitionBuilder.writeBeanDefinitionBuilders(classWriterOutputVisitor, beanElementBuilders); } catch (IOException e) { // raise a compile error String message = e.getMessage(); error("Unexpected error: %s", message != null ? message : e.getClass().getSimpleName()); } } } finally { BeanDefinitionWriter.finish(); } } return false; } /** * Writes {@link io.micronaut.inject.BeanDefinitionReference} into /META-INF/services/io * .micronaut.inject.BeanDefinitionReference. */ private void writeBeanDefinitionsToMetaInf() { try { classWriterOutputVisitor.finish(); } catch (Exception e) { String message = e.getMessage(); error("Error occurred writing META-INF files: %s", message != null ? message : e); } } private void processBeanDefinitions(BeanDefinitionVisitor beanDefinitionWriter) { try { beanDefinitionWriter.visitBeanDefinitionEnd(); if (beanDefinitionWriter.isEnabled()) { beanDefinitionWriter.accept(classWriterOutputVisitor); } } catch (IOException e) { // raise a compile error String message = e.getMessage(); error("Unexpected error: %s", message != null ? message : e.getClass().getSimpleName()); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy