We are currently facing a little conundrum with Spring Boot that's actually not a rare situation:
Spring Security OAuth2 Client has a critical vulnerability that our production systems might be vulnerable to; the vulnerability is fixed in the latest patch release of Spring Security. Naturally, we want to update our production systems ASAP, but this means we need to override the Spring Boot (Gradle) dependency management system if we don't want to wait until the next Spring Boot patch release.
I know that this can be done quite easily, in this case e.g. by setting something like this in gradle.properties:
spring-security-oauth2-client.version=5.7.5
The problem with this is that this dependency is now pinned to a specific version; I need to remember to remove this property as soon as a Spring Boot patch release is available. This means extra coordination effort because we need to document this in our backlog, and even with good documentation on our part there is a risk that we forget to do it, which means the dependency will eventually be outdated - which is the exact opposite of what we wanted to achieve in the first place.
What I'd rather do is specify a minimum version of the dependency, that gets ignored if it is older than what the Spring Boot dependency management plugin's default version.
Can this be done? Or is there a better strategy to handle a situation like this?
This is possible using gradle's dynamic versions.
For instance, you can have:
dependencies {
implementation 'org.springframework.security:spring-security-oauth2-client:5.+'
}
But keep in mind that dynamic versions add nondeterminism to your build and can introduce unexpected behaviour changes to the system.
Using dynamic versions in a build bears the risk of potentially
breaking it. As soon as a new version of the dependency is released
that contains an incompatible API change your source code might stop
compiling.
References:
Version ranges in gradle
Related
I was trying to create a new Spring Boot project using start.spring.io. Searching for dependencies, I found that there was no AspectJ starter available. Has this dependency removed/deprecated from Spring Boot starters? Here is a screen shot:
I, however, was able to find the dependency on maven repositories website:
It was removed indeed. #jwenting explained in a nutshell why. This starer is required if you want to create your own aspect or if you want to use some advanced AOP mode.
Most users don't need it and whenever a library requires it, its starter brings it automatically. Having a dedicated entry was confusing as we saw a very large amount of users picking this up for no good reason.
Also, please keep in mind that start.spring.io is not an exhaustive list of what you can do with Spring. We're focusing on the getting started experience only and avoiding cases that could lead to confusion. This one is a good example of the latter.
it's an implicit dependency, meaning you don't have to include it because it's automatically pulled in by anything that needs it.
You can still add it explicitly, but there's no need to (and afaik it's never been needed).
I want to reuse and centralize the utils I created for my Spring REST API for my future projects. That's why I thought I'd outsource them to my own project and make them available as a Maven dependency.
These Util files e.g. a basic service, basic controllers also contain Spring annotations, i.e. I need some Spring dependencies in my Util dependency. Now I'm a bit unsure whether I'm making a mistake or not.
First of all, I'm not sure if I should even use spring dependencies in a utility dependency or try to remove everything. Otherwise, I'll have to specify a spring version, but it might differ from the version I want to use later in the project it's included in. How am I supposed to solve this?
It is perfectly reasonable to have dependencies for your dependencies (these are called transitive dependencies). Of course, you should keep the number as low as possible, but on the other hand, you do not want to reinvent the wheel.
When somebody uses your dependency, they will automatically draw the transitive dependency on spring. Now, several cases can occur:
If this is the only reference to spring, the version is just used as you stated it.
If at some other point, a different version of spring is given, Maven dependency mediation kicks in. It decides by a "nearest is best" rule which version to take.
But: You can always set the spring version in <dependencyManagement> and then overwrite all transitively given version numbers.
That is the main concept of Maven. Your utility module must shipped together with Spring dependencies. It's called transitive dependencies.
Try to imagine that situation when all dependencies had excluded. In that case nobody will never know what kind and which version of Spring dependencies are needed.
Maven has a very good dependency conflict resolution. It's based on nearest-newest principle. So you can override those Spring versions easily and your application will use only one of that.
Take a look at these:
[1] Dependency Mechanism
[2] Dependency Mediation and Conflict Resolution
When we run a sample main program which reads a applicationContext.xml with a single bean..
how does Spring do the logging..and how can one overwrite the default logging.
I didnt see any log4j.xml in the spring dependencies as well..
Regards
This is described in the documentation:
Logging is a very important dependency for Spring because a) it is the only mandatory external dependency, b) everyone likes to see some output from the tools they are using, and c) Spring integrates with lots of other tools all of which have also made a choice of logging dependency. One of the goals of an application developer is often to have unified logging configured in a central place for the whole application, including all external components. This is more difficult than it might have been since there are so many choices of logging framework.
The mandatory logging dependency in Spring is the Jakarta Commons Logging API (JCL). We compile against JCL and we also make JCL Log objects visible for classes that extend the Spring Framework. It's important to users that all versions of Spring use the same logging library: migration is easy because backwards compatibility is preserved even with applications that extend Spring. The way we do this is to make one of the modules in Spring depend explicitly on commons-logging (the canonical implementation of JCL), and then make all the other modules depend on that at compile time. If you are using Maven for example, and wondering where you picked up the dependency on commons-logging, then it is from Spring and specifically from the central module called spring-core.
The nice thing about commons-logging is that you don't need anything else to make your application work. It has a runtime discovery algorithm that looks for other logging frameworks in well known places on the classpath and uses one that it thinks is appropriate (or you can tell it which one if you need to). If nothing else is available you get pretty nice looking logs just from the JDK (java.util.logging or JUL for short). You should find that your Spring application works and logs happily to the console out of the box in most situations, and that's important.
(emphasis mine)
Follow several sections describing how to use various logging frameworks.
I am developing a simple web application, using Spring Framework.
When I add Spring framework to my class path, I see that it has lot of jars which I never use (for example: spring-aop-3.2.3.RELEASE.jar).
Is it a good idea to keep the entire framework intact or remove unused jars?
If you need to remove unused jars, the best way is to use some dependency management tool like Ivy or Maven, and let the tool decide what the required dependencies are. Otherwise it will not be apparent what is really unused or not until you break something.
For instance, if you are using declarative transactions, then removing the AOP jar will cause breakage, because AOP is used to implement that functionality.
If you would rather not use dependency management, it's better to leave everything intact.
There are some cases where you do want to remove/exclude jars. Replacing commons-logging with slf4j is one example. Another example is excluding the log4j dependencies that get dragged in on account of some appender that's packaged with log4j but that you know you will never use. Dependency management tools allow you to tell them what needs to be excluded.
Doing without dependency management management and removing things because you never use them directly is too dangerous.
I have a bunch of top level packages that are owned by the same team. All top level packages use spring. Then there are some libraries (jars) that are shared functionality and utilities between the top level packages. All pretty standard stuff.
In some cases it makes sense to make use of spring in a library package. Let's say I have a series of Aspects that are shared and I want to use spring's #Aspect/#Before/#After etc. annotations.
The problem is that there are fairly large top-level packages that are written with spring 2.5.6 dependencies, and the newer top-level packages are being created in spring 3. I generally think this is a good thing.
But then there's this little problem that I now have libraries that depend on spring-2.5.6, and those can't be used by spring-3 top level packages because of the mis-matching versions (I'm making the assumption here that sucking in two different spring versions is a very bad idea). It seems that my options are to avoid spring dependencies in libraries, or to accept that my libraries will need to provide multiple versions (1 for each version of spring used at the top package level). Option 1 seems preferable to option 2, but I'm hoping there's some interesting trick that gives me the best of both worlds.
If there was java standards for some of these things (#Inject?) then I could depend on the javax stuff and not on the spring specific stuff. Unfortunately #Inject etc. is only supported for spring 3+ so that fixes my problem at some future point but as long as I have 2.5.6 apps it doesn't help.
Ideas?
I should note that there are some existing libraries that depend on spring-2.5.6 and I view that as a deterrent to upgrading the top level package to spring-3. Because upgrading the top level package means also version bumping N libraries that depend on spring, which is annoying, given that I'm not even aware of the complete set of consumers of these libraries and what they might think of the spring version suddenly being bumped.
EDIT:
I'm wondering if I can create some library, and make a spring dependency with scope of provided. The spring version would be the minimum version that I can pick to get the feature set I want, so ideally I'd use 2.5.6 to make it compatible with a spring 2.5.6 app and a spring-3.0.5 app. Then when a consuming app creates a dependency on my library, it will also create a spring dependency for the real spring version in effect for that application. I'm thinking this should work, so long as the spring version is >= the version I pick for my library (and I happen to know that 2.5.6 is the lowest version in use for all apps).
My other option is to not create a spring dependency at all but DO provide a spring.xml that can be imported in the calling app. This means I can't use annotations or stuff like InitializingBean, but generally spring has provided a way to do things via annotation, or xml, or both, so this should work as well.
Thoughts?
As you rightly mentioned, having both 2.5.6 and 3.0 Spring dependencies will create problems. Only one version will be picked up at runtime which means either your top level packaged modules will fail or the other shared utilities will fail (depending on the version of Spring).
A similar issue was discussed in Java Classloader - how to reference different versions of a jar. It is not directly related to Spring but I cannot think of a direct solution to your problem. OSGi is a possible solution but not sure how feasible it is in your environment since it requires change in the container itself.
a) #Aspect #Before #After: These annotations are all in aspectjrt.jar, not Spring itself. They should work with either version
b) #Inject won't work but #Autowired will.