I am trying to enable AspectJ load-time weaving (not Spring AOP) in a Spring Boot application. My goal is to weave advice into annotated fields and java.lang.reflect.Field.set(Object, Object) at load-time.
Per the Spring docs, I tried:
#Configuration
#EnableLoadTimeWeaving
public class Config {}
Running the Spring Boot application with this configuration resulted in the application context failing to load with this message:
Caused by: java.lang.IllegalStateException:
ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader]
does NOT provide an 'addTransformer(ClassFileTransformer)' method.
Specify a custom LoadTimeWeaver or start your Java virtual machine
with Spring's agent: -javaagent:spring-instrument-{version}.jar
The latter suggestion in that message is not a good option as I am trying to avoid necessitating launch script modifications. The aspect I need to weave actually resides in a library, so all implementing Spring Boot projects will have to make whatever changes required to get LTW to work.
I also tried this configuration:
#Configuration
#EnableLoadTimeWeaving
public class Config implements LoadTimeWeavingConfigurer {
#Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
Running the Spring Boot application with this configuration resulted in the application context failing to load with this message:
Caused by: java.lang.IllegalStateException:
ClassLoader [jdk.internal.loader.ClassLoaders$AppClassLoader]
does NOT provide an 'addTransformer(ClassFileTransformer)' method.
It seems I need to make the JVM use a class loader that has an addTransformer(ClassFileTransformer) method. I don't know how to do that, particularly for this situation. Any suggestions?
I am not an active Spring user, but I know that Spring supports annotation- or XML-configured agent hot-attachment and has some container-specific classes for that according to its documentation. It does not seem to work reliably in all situations, though, especially when running a Spring Boot application from an IDE or so.
Anyway, the AspectJ weaver 1.8.7 and more recent can be hot-attached. I explained how to do that in a Spring setup here. If you want a simpler solution with less boilerplate but one more dependency to a tiny helper library called byte-buddy-agent, you can use this solution as a shortcut. I have not tried it, but I know the helper library and am using it myself in other contexts when hot-attaching bytecode instrumentation agents, avoiding the fuss to cater to different JVM versions and configuration situations. But in order for that to work on JVM 9+, you might need to manually activate auto-attachment for the JVM, which would be another modification for your start-up script, and you would be back to square 1.
Related
I'm making some Integration Tests for my app and I'm encountering this problem I can't see how to solve.
I'm using Spring Boot 2.4.13 + Spring Data Neo4J 6.1.9
FYI, I deleted the Application default test that comes bundled when you create a project through Spring Initializr, and under /src/test/resources I have a .yml file named application.yml
My IT class looks like this:
#SpringBootTest
public class ClientIT {
#Autowired
private ClientServiceImpl service;
#Autowired
private ClientRepository repository;
#Test
void someTest() {
//Given
//When
//Then
}
}
But when I run this test I get the following Exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
And this is the cause:
Caused by: java.lang.IllegalStateException: The provided database selection provider differs from the ReactiveNeo4jClient's one.
The thing is I don't use SDN's Reactive features at all in my project. I don't even understand why Spring tries to load it. I've created an Issue under the Spring Data Neo4j GitHub repository (https://github.com/spring-projects/spring-data-neo4j/issues/2488) but they could only tell me that ReactiveNeo4jDataAutoConfiguration gets automatically included if there's a Driver or Flux class in the classpath which I don't have.
I've been debugging the Spring internals while booting up the Application after JUnit Jupiter methods to no success.
What I could see is that at some point after JUnit Jupiter tests preparation/initialization, "reactiveNeo4jTemplate" gets injected into DefaultListableBeanFactory's beanDefinitionNames variable.
I've tried many combinations of different annotations intended to be used when making Integration Tests but the one time it worked was after I explicitly excluded ReactiveNeo4jDataAutoConfiguration class through
#EnableAutoConfiguration(exclude=ReactiveNeo4jDataAutoConfiguration.class)
What I've always seen in some blogposts is that by using #SpringBootTest I shouldn't worry about this kind of problem but it looks like I need to add that annotation every time I want to make a new IT test.
My Integration Tests basically consist of bootstrapping the application + web server (tomcat) along with an embedded Neo4J instance and after that, making requests to check everything works as it should. Do I really need to worry about all of this just to make these simple tests?
Thank you
References:
How do I set up a Spring Data Neo4j integration test with JUnit 5 (in Kotlin)?
SprintBootTest - create only necessary beans
Answering my own question after finding what is causing this error:
In the linked Github Issue, one of the developers says having Flux.class in the classpath forces SDN to instantiate Neo4jReactiveDataAutoConfiguration which is what is causing the other reactive beans to instantiate.
Apparently, neo4j-harness brings io.projectreactor (where Flux.class belongs) as an indirect dependency through neo4j-fabric which is the root of our problems.
The Spring Data Neo4j will be fixing this issue in a patch later this week.
I am trying to synchronize declarative transactions (i.e. methods annotated with #Transactional) using AspectJ like so:
...
import org.aspectj.lang.annotation.Aspect;
...
#Component
#Aspect
public class TransactionMonitor extends TransactionSynchronizationAdapter {
#Before("execution(#org.springframework.transaction.annotation.Transactional * *.*(..))")
private void registerTransactionSynchronizationOnAnnotation(JoinPoint joinPoint) {
TransactionSynchronizationManager.registerSynchronization(this);
}
}
This currently fails with java.lang.IllegalStateException: Transaction synchronization is not active which indicates that the synchronization is not run inside the transaction execution, but before. I want to ensure that this is the other way round, of course.
I found this answer, however #Order(Ordered.LOWEST_PRECEDENCE) had no effect, and
#DeclarePrecedence(
"org.springframework.transaction.aspectj.AnnotationTransactionAspect, xxx.xxx.TransactionMonitor, *"
)
led to this during startup:
java.lang.IllegalArgumentException: DeclarePrecedence not presently supported in Spring AOP
I have the feeling this is AOP and AspectJ not being happy with each other, but I am not sure. I am thankful for any ideas.
EDIT: I have to use #EnableTransactionManagement(proxyTargetClass = true), can this be related to the issue?
For #DeclarePrecedence you need to switch to native AspectJ. Spring AOP is just "AOP lite" and technologically has little in common with AspectJ other than its syntax which is basically an AspectJ subset. The Spring manual describes how to use native AspectJ in Spring via LTW (load-time weaving). Precedence declaration for Spring components rather works using #Order, BTW.
I am not a Spring user at all, but as for declarative transaction management, it already knows proxy-based Spring AOP versus native AspectJ mode, see EnableTransactionManagement.mode and the enum constants in AdviceMode. Besides, EnableTransactionManagement also has an order property. Reading Javadoc and the Spring manual helps, I guess.
Upgrading an existing system to Spring Boot with Auto config. Currently the system is all Java config. I'm confused over whether to continue the use of #Profile. Is this annotation no longer needed? I searched extensively about upgrading and found only references to non-Spring Java migration and creating new projects.
Typical #Profile usage in our configuration classes looks something like:
#Bean
#Profile("is-standalone")
public Service unsecuredService(SomeApi someApi) {
return new ...
}
I inferred from the Spring Boot examples that using one of the #Conditional annotations is recommended like this:
#Bean
#ConditionalOnProperty("unsecured.enabled")
public Service unsecuredService(SomeApi someApi) {
return new ...
}
Then in a YAML file the is-standalone Profile enables or disables all the various properties for that Profile. Is this the proper way to upgrade? To repeat a question from above differently, can the #Profile usage be left as is? This is for a fairly large project, the upgrade is non-trivial, so I would like to do this only once!
Depends where your previous #Profile annotation is coming from. If you're using Spring's #Profile, the functionality is as follows:
Annotating a class with #Profile("dev") will load the class and register it in the Spring context only when the dev profile is active
Annotating a class with #Profile("!dev") will load the class and register it in the Spring context only when the dev profile is inactive
If this sounds like what you have already, no change is needed.
I want to make a call to a Spring bean (a #Component) from my message-driven bean (MDB) but have problems getting a reference to it. I've tried with a class implementing org.springframework.context.ApplicationContextAware which stores the Spring ApplicationContext in a static field in a class MyAppContext. The static field in MyAppContext is then accessed from the MDB. But MyAppContext is loaded from different classloaders. The Spring application context is correctly set in the web module classloader context, but in the MDB's classloader context, it's null.
Can I somehow instruct JBoss to use the same classloader for the web app and the MDB?
Or is there a better way than storing the Spring application context in a static field?
Thanks for any advice!
A static holder for the context is not really a good idea. To make your beans available to other applications in a Java EE environment, you should consider making use of JNDI.
Unfortunately, there is no plain JNDI exporter available out of the box, but it's fairly easy to write one yourself, as shown in this blog post: http://maestro-lab.blogspot.ro/2009/01/how-to-export-spring-managed-bean-to.html
There is however a JndiRmiServiceExporter that you may want to look at.
Once your beans are bound to names in JNDI, they can be referenced using standard CDI in your message bean without worrying about class loading issues.
Why not use "ClassPathXmlApplicationContext" to load and look up for the Spring bean you require in your MBean?
I'm trying to use Spring AOP to intercept methods of my GWT-RPC application (using GWT-Server library, so RPC service doesn't extend RemoteServiceServlet). When I deploy my war to tomcat and start the application, CGLIB fails for some reason. But I don't understand why CGLIB is being used for proxying at the first place. Since my RPC class implements the interface, shouldn't it be using JDK dynamic proxies?
Is there anything I need to do to debug this issue? Kindly advise.
Note: FYI, Spring encounters this exception, but I believe that's a different problem, I'm unable to understand why CGLIB proxy is in the picture.
Caused by: net.sf.cglib.core.CodeGenerationException: net.sf.ehcache.CacheException-->Another unnamed CacheManager already exists
in the same VM. Please provide unique names for each CacheManager in the config
or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
Answering for the sake of other (rare) folks who might do the same mistake.
The aspect setup for spring AOP wasn't correct and was in fact trying to target almost all the classes in the context, which is why EhCache started causing problems as there were more than one CacheManager instances (because of CGLIB proxies as CacheManager doesn't implement an interface)