Open alex-konkov opened 10 years ago
Would be interesting what do people think on that and if there is a chance to get it done in spring-boot some day. Any thoughts? thx
I would certainly welcome this as an enhancement. This is the one limitation that prevents some of my apps from being contained in a single jar file.
This would be very much appreciated. So far we've been stuck with Spring dynamic proxy based AOP and its limitations which has lead to all kinds of ugliness such as static support classes duplicating the aspect's behaviour etc.
The reason for this has been that we have been running our applications with Maven Jetty plugin and don't want to ship the aspectj agent jar separately with the application. Ideally it should come from repository just like any other dependency without the need to specify it's exact location on command line.
We're at the moment changing over to spring boot, gradle and other cool stuff and this would be a huge plus.
Any progress on this? Looks like it keeps slipping milestones...
Would be cool if this makes it in 1.2.0.
I have some doubts about the usability of this feature. Perhaps those who are interested in it can allay my fears. My primary concern is that launching your app via java -jar
would give you load-time weaving automatically, whereas launching it via the main method in your IDE would not. A similar problem would exist when running integration tests (there's an issue open against Spring's Test framework for this).
While having to configure the agent is clunky, I like the fact that it's consistent.
wilkinsona, your point is valid but it is rather consistently not implemented in either usage case. The change towards LTW support appears to be evolutionary as long as spring boot brings the idea of self-sufficient package.
Also, a quick comment above as "being consistent" - if you have a spring-boot project with Eclipselink which does require LTW, running it with "mvn spring-boot:run" will work and running the packaged jar file with "java -jar" will just fail.
An example of this problem is demonstrated with this project: https://github.com/dsyer/spring-boot-sample-data-eclipselink - clone it, mvn package and java -jar the built jar, it fails badly.
It only works with mvn spring-boot:run
because the agent is configured in the pom.xml
:
<configuration>
<agent>${settings.localRepository}/org/springframework/spring-instrument/${spring.version}/spring-instrument-${spring.version}.jar</agent>
</configuration>
If you don't supply the equivalent configuration when running the application using java -jar
it will, quite reasonably, fail.
is there anything we can do to make this happen?
I've yet to see a compelling argument against my usability concerns described above.
hmm, not sure the miss of a feature on the test framework is a compelling argument...
For us, this missing feature means we can't use load time weaving on cloudfoundry with any spring boot application. Because the deploy unit is a single JAR - if we wanna change this, then we have to fork the java_buildpack and add this functionality (that's nothing I really wanna do). Looking at the importance of spring for cloudfoundry (in both ways) I would assume this argument is stronger then the one about the test framework.
In fact implementing it here will maybe have @sbrannen to take an other look at the mention issue in the test framework too.
hmm, not sure the miss of a feature on the test framework is a compelling argument...
As I said above, that's a secondary concern. My primary concern is about the complexity of having things behave differently when you run using java -jar
, vs launching the main method in your IDE, vs launching via our Maven or Gradle plugins. Rather than having to document different behaviour depending on how you launch your application, I like the consistency of the current behaviour. As things stand, you consistently have to configure the agent. Adding some magic to java -jar
would break that consistency.
The Java buildpack already has support for a number of Java agents. Another way to tackle your particular problem would be via an enhancement to the buildpack. /cc @cgfrost
This is what I did to make it work:
pom.xml
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>ZIP</layout>
<mainClass>org.springframework.boot.SpringApplication</mainClass>
</configuration>
</plugin>
ZIP layout so that it uses the org.springframework.boot.loader.PropertiesLauncher
application.properties
loader.classLoader=packageName.TransformingClassLoader
loader.main=org.springframework.boot.SpringApplication
spring.main.sources=packageName.Config
Here I tell the PropertiesLauncher to use my own classLoader, and that the main program is the standard SpringApplication
, with packageName.Config
as my Spring configuration file.
TransformingClassLoader.java
https://gist.github.com/lukiano/114cb01a59ddd9880aa7
based on org.springframework.instrument.classloading.ShadowingClassLoader
and org.springframework.core.OverridingClassLoader
And then my Config file implements LoadTimeWeavingConfigurer
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver(Thread.currentThread().getContextClassLoader());
}
So basically it loads the entire Spring context under my own ClassLoader which implements the transformer methods that ReflectiveLoadTimeWeaver
expects. And it still works when it creates a fat-jar, so it doesn't depend on being run with mvn spring-boot:run
@lukiano nice, that looks like a solution which would also solve the concerns @wilkinsona has, with some further work this might be doable even without the ZIP packaging and therefore integrate it into boot directly. I just found an other solution which might be interesting too: https://github.com/subes/invesdwin-instrument
I see, that one uses tools.jar to inject the agent itself by retrieving the jvm's process id. A solution I considered before, but discarded to avoid a dependency on the JDK
+1 count me in. I want to use Spring's Cache Abstraction in aspectj mode inside a Spring boot web application with embedded tomcat. But because I can't use aspectj load time weaving, I can't use the @Cacheable
annotation if I am to call the method from within the service class. This sets me up for a round of refactoring to ensure that a @Cacheable
method is always called from a proxy.
@lukiano : i tried the same thing which you tried to make it work but I am still unable to make aspectJ working.
Getting following error; org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
@manojtailor that doesn't look like an AspectJ issue
@manojtailor That looks like you're missing a dependency on Tomcat, Undertow, or Jetty. spring-boot-starter-web
is the typical way to add such a dependency.
I think a really nice solution that should make this scenario "just work" would be to make the various Spring classloaders implement org.springframework.instrument.classloading.LoadTimeWeaver. With that change, org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver would work making @EnableLoadTimeWeaving work as expected (and in turn make not only AspectJ weaving work, but any other load time weaving as well).
I think these classloaders need to be modified:
@candrews Unfortunately, that wouldn't help when you're running the app in your IDE without DevTools as neither RestartClassLoader
not LaunchedURLClassLoader
is involved.
@wilkinsona for such cases, how about using the a simple LoadTimeWeaver classloader when not using the Restarter in SpringApplication?
It's an interesting suggestion, but I think it would require SpringApplication
to know about parts of the system that it shouldn't know about. For example, it would need to understand that the Launcher may or may not be used and that DevTools
may or may not be used.
There may also be a problem with making the decision in SpringApplication
. Once it's been called, some of the application's classes have already been loaded. Loading the same classes on two separate ClassLoaders is likely to be painful.
No more +1
comments please (I've removed the existing ones). They just make the issue harder to read. Just click the "thumbs up" reaction on the top comment if you want to vote.
As I said above, that's a secondary concern. My primary concern is about the complexity of having things behave differently when you run using
java -jar
, vs launching the main method in your IDE, vs launching via our Maven or Gradle plugins.
But what about applying also the following:
+1
loader.classLoader=packageName.TransformingClassLoader loader.main=org.springframework.boot.SpringApplication spring.main.sources=packageName.Config
Here I tell the PropertiesLauncher to use my own classLoader, and that the main program is the standard
SpringApplication
, withpackageName.Config
as my Spring configuration file.
@lukiano, this whole approach is not working for me. I started with my existing playground project based on Boot 2.1.8. But even after upgrading to 2.5.6, it is not working. Could we please have a chat on Gitter and sort this out together? I am an experienced AspectJ user (and AspectJ co-maintainer), but unexperienced in Spring Core and Spring Boot. I really would like to know if there is any way to get simple Spring Boot applications working from IDE or via java -jar
after packaging without having to use a Java agent on the command line. It is just an FAQ when people consult me, and all I can answer is that the Spring documentation says that you can configure Spring to run without -javaagent:
, but in reality it simply does not work in Spring Boot, which is what most developers use nowadays when starting to experiment with AOP, both Spring AOP and AspectJ LTW.
I'd love to help truly but I haven't worked on this project (or any Spring project really) for the last 5.5 years. My memories on how Spring and in particular AspectJ worked are long gone.
Now have to use javaagent instead
It was a very painful process in getting this working before and enabling javaagent was the only possible option (not my favourite).
Not sure how relevant in getting load time AOP working anymore.
Considering all the progress that spring has achieved in switching to AOT compilation for spring boot 2.6 and 3.0 and hence native-image support Maybe now aspects can be easily compiled ahead of time and easily included in the application.
On Wed, 23 Feb 2022 at 07:12, vangogh-ken @.***> wrote:
Now have to use javaagent instead
— Reply to this email directly, view it on GitHub https://github.com/spring-projects/spring-boot/issues/739#issuecomment-1048501227, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPIXOGNBJBW2SRIUCUTKHTU4SCFXANCNFSM4AOXLDEQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
You are receiving this because you commented.Message ID: @.***>
@ade90036, your comment is a bit cryptic. What was painful and how did you achieve what you wanted? Certainly, adding --javaagent
is not difficult. It is just not super convenient and sometimes not an option, depending on who rules the runtime environment, if that is what you mean.
I cannot relate to your comments about Spring Native, because that is a whole different world with GraalVM and the rest of its infrastructure. It is not helpful to discuss it here, because I think it is off-topic. Let us try not to compare apples and oranges. Starting a Spring Boot application from an IDE or from an uber JAR is quite different from setting up the infrastructure for Spring Native and wait for a lengthy compilation process before being able to use the then runtime-optimised application. One does not replace the other, the two concepts rather complement each other for different scenarios.
Let me add some context.
I'm only using AspectJ with spring boot to enable @Evict @Cachable @Scheduled @Asych
annotation to get around at a limitation of spring proxy
where in certain case a user can bypass the triggering of the annotation
and calling the plain method.
Sorry I didn't mean to inconvenient this conversation with technologies we fully fully grasp. But unfortunately that is the directions that everything is moving to (compile time, micronaut, quarkus, native-image Graalvm)
Which makes you wonder if there is place for AspectJ introspection if we, for example, can create physical classes at compile time to pretty much model the intended behaviour without needing reflection or introspection (like a spring proxy) and added overheads... (Significant)
micronaut and quarkus are already proving this, spring boot is catching up.
Going back to my problem:
Adding javaagent
was the easiest thing I have done. But that also open
a can of worms for security reason, I won't get in details.
jar specified in javaagent needed to be available before the application launched.
Hence I couldn't use spring boot gradle plugin to build a fat jar
Hence it had to be maintain a static jar stored in my project
Therefore I lost the ability to use use gradle to track version compatibility with my current version of spring and adjacent dependencies
because it was a static external dependency I couldn't use convenient spring boot / gradle plugins to generate docker image out of the box.
I had to build a custom docker image to add static files
Now, i hope I'm not the only one to realise that all these "extra" steps, for their intended purpose, are only in place to enable aspectJ working and are another thing that can break down, why not fix what was broken in the first place, spring?
On Wed, 23 Feb 2022, 15:17 Alexander Kriegisch, @.***> wrote:
@ade90036 https://github.com/ade90036, your comment is a bit cryptic. What was painful and how did you achieve what you wanted? Certainly, adding --javaagent is not difficult. It is just not super convenient and sometimes not an option, depending on who rules the runtime environment, if that is what you mean.
I cannot relate to your comments about Spring Native, because that is a whole different world with GraalVM and the rest of its infrastructure. It is not helpful to discuss it here, because I think it is off-topic. Let us try not to compare apples and oranges. Starting a Spring Boot application from an IDE or from an uber JAR is quite different from setting up the infrastructure for Spring Native and wait for a lengthy compilation process before being able to use the then runtime-optimised application. One does not replace the other, the two concepts rather complement each other for different scenarios.
— Reply to this email directly, view it on GitHub https://github.com/spring-projects/spring-boot/issues/739#issuecomment-1048891370, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPIXOCJDSIV2CMUPTWLTPLU4T273ANCNFSM4AOXLDEQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
You are receiving this because you were mentioned.Message ID: @.***>
Hi all!
If you are running your applications as executable JARs on JRE 9+ (Spring Boot ones or other types), probably Agent Embedder Maven Plugin is what you want.
It enables you to embed a java agent in your executable JAR and have it started automatically using the Launcher-Agent-Class
JVM mechanism.
Unique features added by this plugin and unavailable via the original JVM mechanism: You can
Spoiler: I am the author of the plugin and also happen to be the current maintainer of AspectJ.
Edit: I forgot to mention, that in the case of the AspectJ weaver, as the JVM starts the agent very early, weaving will be active without extra Spring configuration and it should work for all classloaders - no more ClassLoader [...] does NOT provide an 'addTransformer(ClassFileTransformer)' method
errors as seen when hot-attaching the weaver via spring-instrument.jar
.
AspectJ based load time weaving (LTW) can be set up with Spring Boot, however when running as a dead simple "java -jar app.jar" the classes are not woven until Java agent is specified. (AspectJ + LTW + self contained JAR) is a potentially cool combination for many Spring Boot powered projects that need to leverage AOP without needing to play any additional tricks (or worrying about not forgetting to).
This feature request is about adding an AspectJ LTW bootstrapping code straight into the Spring Boot launcher where a specific class-loader is initialised. Then, the client projects that would like to stick with the simplest "java -jar app.jar" startup way would be able to leverage power of AspectJ LTW weaving feature out of the box.
http://stackoverflow.com/questions/23219772/spring-boot-and-aspectj-based-ltw-without-java-agent/23248830?noredirect=1#comment35709788_23248830