In org.springframework.beans.propertyeditors.ClassEditor, the Class is obtained by ClassUtils.resolveClassName as a string, but when the Class is dynamically generated by javax.tools.JavaCompiler, the Class cannot be obtained by ClassUtils.resolveClassName . Expect to be passed from a primitive type that can be used directly instead of a string conversion, thanks.

Comment From: snicoll

Rather than asking for a code change, can you please first share the context? What are you trying to do?

Comment From: Air-Cooled

Create a SpringBoot project and add the following additional dependencies and startup classes to test; overwrite org.springframework.beans.propertyeditors.ClassEditor when errors are reported, as shown at the end.

<dependency>
    <groupId>com.itranswarp</groupId>
    <artifactId>compiler</artifactId>
    <version>1.0</version>
</dependency>
import com.itranswarp.compiler.JavaStringCompiler;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import java.util.Map;

@SpringBootApplication
public class DynamicCompilationApplication implements CommandLineRunner {


    public static void main(String[] args) throws Exception {
        // Initializes the dynamic build class example
        buildClassesDynamically();

        SpringApplication.run(DynamicCompilationApplication.class, args);
    }

    /**
     * {@link  org.springframework.beans.propertyeditors.ClassEditor#setAsText} gets the value here to avoid error
     */
    public static Class<?> appClass;
    public static Object app;

    private static void buildClassesDynamically() throws Exception {
        String javaName = "DynamicApplication";
        String javaPath = DynamicCompilationApplication.class.getPackage().getName();

        JavaStringCompiler javaStringCompiler = new JavaStringCompiler();
        Map<String, byte[]> classBytes = javaStringCompiler.compile(javaName + ".java",
                "package " + javaPath + ";\n" +
                        "import org.springframework.boot.CommandLineRunner;" +
                        "public class " + javaName + " implements CommandLineRunner {" +
                        "public void run(String... args) { System.err.println(\"Holle Word!\"); }" +
                        "}"
        );
        appClass = javaStringCompiler.loadClass(javaPath + "." + javaName, classBytes);
        app = appClass.getDeclaredConstructor().newInstance();
    }

    @Autowired
    public ConfigurableApplicationContext context;

    @Override
    public void run(String... args) throws Exception {

        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) context.getBeanFactory();

        // case 1: Direct register
        String beanName = "dynamic";
        // Dynamic type register
        beanFactory.registerBeanDefinition(beanName, BeanDefinitionBuilder.genericBeanDefinition(appClass).getRawBeanDefinition());
        // Console output:Holle Word!
        context.getBean(beanName, CommandLineRunner.class).run(args);

        // case 2: Use FactoryBean injection, this will cause an exception
        String beanName2 = "factoryDynamic";
        BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(CustomFactoryBean.class).getRawBeanDefinition();
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(appClass.getName());
        beanDefinition.getPropertyValues().add("type", appClass);
        beanFactory.registerBeanDefinition(beanName2, beanDefinition);
        // TODO : An error will be reported here, and you need to modify the org.springframework.beans.propertyeditors.ClassEditor, as shown below
        context.getBean(beanName2, CommandLineRunner.class).run(args);
    }

    // Like MapperFactoryBean
    public static class CustomFactoryBean<T> implements FactoryBean<T> {

        private Class<T> type;

        public CustomFactoryBean(Class<T> type) {
            // TODO :There is also a question, since the parameter construct is called, why is the Setter method called separately again?
            this.type = type;
        }

        public void setType(Class<T> type) {
            this.type = type;
        }

        @Override
        public T getObject() throws Exception {
            return (T) app;
        }

        @Override
        public Class<?> getObjectType() {
            return type;
        }
    }

}

/*
 * Copyright 2002-2017 the original author or 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 org.springframework.beans.propertyeditors;

import java.beans.PropertyEditorSupport;

import com.test.dynamic.DynamicCompilationApplication;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * Property editor for {@link Class java.lang.Class}, to enable the direct
 * population of a {@code Class} property without recourse to having to use a
 * String class name property as bridge.
 *
 * <p>Also supports "java.lang.String[]"-style array class names, in contrast to the
 * standard {@link Class#forName(String)} method.
 *
 * @author Juergen Hoeller
 * @author Rick Evans
 * @see Class#forName
 * @see ClassUtils#forName(String, ClassLoader)
 * @since 13.05.2003
 */
public class ClassEditor extends PropertyEditorSupport {

    @Nullable
    private final ClassLoader classLoader;


    /**
     * Create a default ClassEditor, using the thread context ClassLoader.
     */
    public ClassEditor() {
        this(null);
    }

    /**
     * Create a default ClassEditor, using the given ClassLoader.
     *
     * @param classLoader the ClassLoader to use
     *                    (or {@code null} for the thread context ClassLoader)
     */
    public ClassEditor(@Nullable ClassLoader classLoader) {
        this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
    }


    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        if (StringUtils.hasText(text)) {
            // TODO Changes
            try {
                setValue(ClassUtils.resolveClassName(text.trim(), this.classLoader));
            } catch (Exception e) {
                if (text.equals(DynamicCompilationApplication.appClass.getName())){
                    setValue(DynamicCompilationApplication.appClass);
                } else throw e;
            }
        } else {
            setValue(null);
        }
    }

    @Override
    public String getAsText() {
        Class<?> clazz = (Class<?>) getValue();
        if (clazz != null) {
            return ClassUtils.getQualifiedName(clazz);
        } else {
            return "";
        }
    }

}

Comment From: snicoll

I don't really understand what you're trying to do. In the second example, you're passing the name of the class but that class doesn't exist. You are the one forcing the editor to kick in an translate the String into a Class.

If you're looking for support on using Spring, please ask your question on StackOverflow.

Comment From: Air-Cooled

What's wrong with dynamically compiling a Class? The point is why would FactoryBean inputs be converted to String and then back again, which, performance aside, doesn't seem elegant. Perhaps to be compatible with xml injection Baen, what can be done to bypass this cumbersome transformation?

What I need is dynamically compiled classes that can also be registered through FactoryBean, the purpose of which is to simplify repetitive code in a project without breaking its structure (MVC). Cost reduction and efficiency, dynamic and flexible, the future can be expected!

Comment From: snicoll

What's wrong with dynamically compiling a Class?

Nothing, I guess?

The point is why would FactoryBean inputs be converted to String and then back again

You are doing that, not us.

beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(appClass.getName());

If you have more questions, as I've asked already, please follow up on StackOverflow.

Comment From: Air-Cooled

😳I'm sorry, is my use of error; Thank you for your patient guidance.