Grails dependency injection with package private classes - spring

Using Grails (3.x) I would like to make implementations unavailable for users of a certain plugin. Functionality provided with the plugin is made avaible with depenency injection/inversion of control.
I therefore define a public interface and a package private implementation of this interface.
Secondly I would like to register the bean through resources.groovy however this needs importing the class to use - and it's package private.
In Spring i would annotate the class itself with "#Service" making it available through dependency injection (and thus references to package private classes is avoided).
How would one overcome this in Grails (3)?

Apparently it is possible to use spring annotations: Spring - The foundation for grails

Related

Partially using Spring Dependency Injection during migration

I´m currently migrating an existing project to Spring. I´ve started to use dependency injection on some newer components that are loosely coupled, the project itself is however very messy, tightly coupled and it´s impossible for me to use DI entirely and I need to change the code step by step.
The "starting point" of where dependency injection is used is a component, that itself has also some dependencies which cannot be injected and have to be passed through a constructor. See this example
#Component
public class FromHellToDependencyInjectionHeaven extends ComplicatedMessyBaseComponent{
#Autowired
private InjectedComponentFromHeavenOne componentOne;
#Autowired
private InjectedComponentFromHeavenTwo componentTwo;
private UninjectableMess uninjectableMess;
/*
* Constructor that has to stay for now because of the ComplicatedMessyBaseComponent
* clashes with #Component
*/
public FromHellToDependencyInjectionHeaven(MessyDependencyOne dependencyOne,
MessyDependencyTwo dependencyTwo, UninjectableMess uninjectableMess){
super(dependencyOne,dependencyTwo);
this.uninjectableMess = uninjectableMess;
}
}
Here comes the problem:
To use #Autowired, the component that uses the dependencies has to be either a #Service or a #Component, otherwise I get
Autowired members must be defined in valid Spring bean
(#Component|#Service|...)
But when I do that, I run into complications with the existing constructor that I cannot remove.
Class doesn't contain matching constructor for autowiring
One idea was to tell Spring to disable the constructor dependency injection for this class, but I haven´t found any possible solution for that.
How can I partially use dependency injection in my project?

Import by name in Spring Java configs

Say I have 3 Spring/Maven projects:
api-spec: Contains interface MyService.
api-impl: Contains class MyServiceImpl which implements MyService. Also contains class MyServiceConfiguration which is a Spring #Configuration, that defines a bean of type MyServiceImpl.
main: Contains a Spring application setup with Spring JavaConfig (e.g. a #SpringBootApplication). It has a bean with an #Autowired MyService myService field, which works as its configuration class is annotated with #Import(MyServiceConfiguration.class).
I would like the main-project to have api-spec as a Maven compile dependency and to have api-impl as a runtime dependency (to prevent us from making "hard" dependencies from the main project to the api-impl project by mistake). This is not possible, because #Import takes an array of Classes - e.g.: #Import(MyServiceConfiguration.class). I would like something like #Import("my.package.MyServiceConfiguration") instead.
Using class path scanning is not an option (we have seen too many beans getting picked up by accident), and I would prefer not having to use XML files. We could use SpringApplicationBuilder.source(..) as it accepts a class name as a String - but I can't find a way to use that in my tests...
Compile time check is one of the advantages of java config, so I don't think that it's possible to do such thinks with Java. As for me you should use XML to handle this. It doesn't mean that you should do all your configuration in XML, most of the beans of your api-impl module can be in Java and just imported to XML where will be only beans that you are going to change in runtime.
If you don't want to use XML maybe you should consider to use Groovy config instead:
https://spring.io/blog/2014/03/03/groovy-bean-configuration-in-spring-framework-4

External Java Library issue with Autowiring and injecting bean

I have created a Spring Boot application managed by Maven.
I'm retrieving an company's library from our Maven repository.
In this library, we have a service interface, not being annotated with '#Service':
public interface MyService {
//...
}
This service has only one implementation :
public class DefaultMyService implements MyService {
//...
}
This library context is managed the old Spring way (in applicationContext.xml file).
I read that normally, Spring Boot is able to find the implementation if there's only one in the scope.
When I try to run "spring-boot:run" on my project, it will fail with the following error :
No qualifying bean of type 'com.pharmagest.saml.SAMLService'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I tried:
To add a #ComponentScan on the configuration class, including packages in error : #ComponentScan(basePackages={"com.mycompany.web", "com.mycompany.thelibrary.client.*", "com.mycompany.thelibrary.services.*"})
To add the bean definition in applicationContext.xml (if I add the interface it tells me it can define it, thus I heard that Spring can find the default implementation if there is only one ?)
To add library at "runtime" in projects options
To add the library as external resource not via maven
In all cases I just can maven build but can't run the project.
Do you have any advice to help me ? thanks!
Won't work as the DefaultMyService has no #Component (or #Service) annotation will not be detected.
Bean definition has to be a concrete instance so use DefaultMyService instead of the interface. Spring will not detect anything for you your understanding is wrong
and 4. Will not change anything only adding dependencies without proper 1. or 2. will do nothing.
Just add a #Bean to your configuration
#Bean
public DefaultMyService myService() {
return new DefaultMyService();
}
Or import the other libraries applicatiponContext.xml which is what you probably should do.
#ImportResource("classpath:/applicationContext.xml")
Add this next to the #SpringBootApplication.

Implementing an interface is tight coupling?

It says here that
I would not recommend to use InitializingBean and DisposableBean
interface, because it will tight coupled your code to Spring
Does it make sense? I thought this would be just the opposite to tight-coupling.
Here the author means that if you let your application classes implement InitializingBean and DisposableBean interfaces (that are spring specific interfaces), then you are coupling your code with spring.
In future if spring renames these interfaces (unlikely though), or you stop using spring you will have to update your class code.
Instead if you use init-method and destroy-method attributes in your bean config, your class is independent of spring, i.e. there is no depedency of your class on spring specific classes.
Hope it helps.
I think the idea here is to not create any dependency on your code to spring annotations, see
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
When you explicitly declare this kind of dependency, you're coupling your code to spring jars.
In this other example - http://www.mkyong.com/spring/spring-init-method-and-destroy-method-example/ - it shows how you could use convention methods defined in the XML. No spring imports.

Combine two maven based projects on two frameworks

I have two maven projects say MvnSpring and MvnGuice.MvnSpring is working on spring and hibernate frame works.
And MvnGuice is working on google guice and mybatis. I need to combine both the features together.
Both are following singleton pattern. I need to get some class of MvnSpring in MvnGuice while coding. So that I created a jar of MvnSpring and put it in .m2 repository and give the dependacy details in MvnGuice. Now I can import classes of MvnSpring in MvnGuice classes.MvnSpring uses spring dependency injection and MvnGuice uses guice dependency injection for object creation. Now in MvnSpring flow is MSserviceImpl(implements MSservice) > MSdaoImpl(implements MSdao). Now I need to call MSService class from MvnGuice. Then at run time it shows error like MSService class is null. Then I made a guice dependency injection for MSService class in MvnGuice. Now the control reaches MSserviceImpl but now MSdao is null at here. Is it possible to start MvnSpring along with MvnGuice. I hope then I can solve the issue.
While Spring and Guice are targeted at the same problem, IoC, they take very different approaches to solve it. They differ both in functionality and in how they are configured, where Spring has bean definitions and Guice uses bindings.
Fortunately they do have common grounds in that they both support JSR-330, a standards specification that defines a set of annotations. This enables you to write your singletons and describe the injections that they need without depending on either Spring or Guice.
This way you can share your singletons between projects irregardless of the framework you use in a particular project. I would not recommend using both Guice and Spring in the same project, except if there's a clearly defined separation between them. For instance you might use Guice for a module that is used by Spring code via a defined API that hides the fact that it internally is based on Guice.
There was already mentioned JSR-330.
For some cases it can be not enough, e.g., you have code:
final String className = config.getProperty(«serviceImpl»);
// Class.forName(name) and check required interface for type safety
final Class<? extends Service> serviceClass = Reflection.classForName(className, Service.class);
final Service service = injector.getInstance(serviceClass);
In different DI environments you are supposed to support both com.guice.inject.Injector.getInstance() and org.springframework.context.ApplicationContext.getBean() implementations.
There is the draft solution sdif4j Simple Dependency Injection Facade.
The idea of this project is to encapsulate different DI frameworks logic with own abstraction to extend default JSR-330 possibilities. Note, there is no public releases yet, but you can find ideas how to solve your problem or make an internal release in a fork.
The general issue, is that your both MvnSpring and MvnGuice projects are supposed to be based on JSR-330 (instead of guice/spring annotations) and org.sdif4j:sdif4j-api (or your own abstraction; only if Injector functionality is required). It is recommended to make guice and spring dependencies optional (to compile but not export) to allow the library clients to choose the DI themselves.
In your MvnCompineGuiceAndSpring you just declare sdif4j-guice or sdif4j-spring dependency (it is similar to slf4j usage) and configure your DI environment. You can find different examples in testing subproject.
Some more notes:
Spring default scope is singleton, Guice - prototype (Spring terminology). So, if you want a prototype bean, you can use:
#org.springframework.context.annotation.Scope("prototype")
#javax.inject.Named
public class TestPrototype {
}
The Spring #Scope annotation should be ignored by guice even if spring does not present in your classpath.
Also you have to declare all your Singleton beans with #javax.inject.Named and #javax.inject.Singleton annotation to support both Spring and Guice, like this:
#javax.inject.Named
#javax.inject.Singleton
public class TestSingleton implements ITestSingleton {
public TestSingleton() {
}
}
As with #Scope annotation, you can use #ImplementedBy(#ProvidedBy) guice annotations on your code (when feasible; be careful with it, in general it is not a good practice), that should be also ignored in Spring DI (in both cases if Spring exists in classpath or not).
Hope, that's clear.

Resources