Issue
I have a spring boot application that I wish to script with groovy.
I wish to autowire spring boot services to the script but when I compile and run the script autowired accountService turns out to be null.
I see "setApplicationContext called with org.springframework.context.annotation.AnnotationConfigApplicationContext@34b56772" in the log so setApplicationContext does get called.
What am I missing. Thanks.
PS. I cleaned the code a little to better illustrate the problem.
More #1:
Purpose of the application is to extend some base functionality that is written in java , using groovy scripts.
hello.groovy is loaded from resources folder and extends CustomScript which has more functionality that script will override in production.
CustomScript is now ApplicationContextAware but autowired AccountService in groovy script is still null.
I need to make Spring know about the groovy script that extended CustomScript that I instantiated using "newInstance".
Making CustomScript ApplicationContextAware by implementing ApplicationContextAware and/or making groovy script itself ApplicationContextAware by implementing ApplicationContextAware didn't help so far.
The accountService in "@Autowired AccountService accountService" line in the groovy script always returns null.
AccountService.java
@Service
public class AccountService
{
public void hello()
{
System.out.println( "hello from account" );
}
}
CustomScriptInterface.java
public interface CustomScriptInterface
{
public void main();
}
CustomScript.java
@Component
public class CustomScript implements CustomScriptInterface, Serializable, ApplicationContextAware, BeanNameAware
{
private ApplicationContext applicationContext;
private String name;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
System.out.println( "*** setApplicationContext called with " + applicationContext );
this.applicationContext = applicationContext;
}
public ApplicationContext getApplicationContext()
{
return applicationContext;
}
@Override
public void setBeanName(String name)
{
this.name = name;
}
public void main()
{
}
}
CustomScriptImportCustomizer.java
public class CustomScriptImportCustomizer extends ImportCustomizer
{
public CustomScriptImportCustomizer()
{
addStarImports("gro2vy");
addStarImports("org.springframework.beans.factory.annotation");
addImports("org.springframework.stereotype.Component");
}
}
Gro2vyApplication.java
@SpringBootApplication
public class Gro2vyApplication
{
@Autowired private ApplicationContext ctx;
public CustomScript compile(String groovy) throws Throwable
{
CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
CustomScriptImportCustomizer importCustomizer = new CustomScriptImportCustomizer();
compilerConfiguration.addCompilationCustomizers( importCustomizer );
ClassLoader classLoader = this.getClass().getClassLoader();
GroovyClassLoader groovyClassLoader = new GroovyClassLoader( classLoader, compilerConfiguration );
Class<CustomScriptInterface> clazz = groovyClassLoader.parseClass( groovy );
CustomScript script = (CustomScript) clazz.newInstance();
return script;
}
public void init() throws Throwable
{
File resource = new ClassPathResource( "hello.groovy" ).getFile();
String groovy = new String( Files.readAllBytes(resource.toPath() ) );
CustomScript script = compile( groovy );
postProcess( script );
script.main();
}
public void postProcess(Object object) throws BeansException, IllegalArgumentException, IllegalAccessException
{
Field[] fields = object.getClass().getDeclaredFields();
for(int i = 0; i < fields.length; i++)
{
Annotation[] annotations = fields[i].getDeclaredAnnotations();
if ( annotations.length > 0 )
{
for(Annotation annotation : annotations)
{
if ( annotation instanceof Autowired )
{
Autowired a = (Autowired) annotation;
Class claz = fields[i].getType();
fields[i].set( object, applicationContext.getBean( claz ) );
}
}
}
}
}
public static void main(String[] args) throws Throwable
{
SpringApplication.run(Gro2vyApplication.class, args);
new Gro2vyApplication().init();
}
}
hello.groovy
class Runner extends CustomScript
{
// has to be defined public
@Autowired public AccountService accountService
@Override
public void main() throws Throwable
{
accountService.hello()
}
}
Solution
You are creating a new instance for Runner class using reflection and not leveraging Spring's Dependency Injection. Either you need to instantiate AccountService as well while you instantiate Runner, because that way you are not using Spring container capabilities. Or, you need to use ApplicationContextAware on your Runner class.
Answered By - KnockingHeads
Answer Checked By - Gilberto Lyons (JavaFixing Admin)