Suppose the following code:
#Service
public class SearchService {
#Autowired
DependencyService dependencyService;
}
#Service
public class DependencyService {
private final Util util;
DependencyService(Util util){
this.util = util;
execute();
}
public void execute(){
util.execte();
}
}
#Component
public class ConcreteUtil implements Util{
#Override
public void execte() {
System.out.println("I'm the first concrete Util");
}
}
#Component
public class SecondConcreteUtil implements Util{
#Override
public void execte() {
System.out.println("I'm the second concrete Util");
}
}
In Plain Java I can do something like this:
public class SearchService {
DependencyService first = new DependencyService(new ConcreteUtil());
DependencyService second = new DependencyService(new SecondConcreteUtil());
}
But in Spring, it's not resolved by the client. We instruct Spring which bean to take from inside DependencyService:
DependencyService(#Qualifier("concreteUtil")Util util){
this.util = util;
execute();
}
And not like that:
#Autowired
#Qualifier("concreteUtil")
DependencyService dependencyService;
Why? To me this approach sounds like the opposite of decoupling. What do I miss? And how can Plain Java's result be achieved?
Edit:
I want this behaviour
public class SomeSerice {
DependencyService firstConcrete = new DependencyService(new ConcreteUtil());
}
public class OtherService {
DependencyService SecondConcrete = new DependencyService(new SecondConcreteUtil());
}
So I can reuse the code
You can declare multiple beans of type Dependency service inside some configuration class, like
#Qualifier("ConcreteUtilDepService")
#Bean
public DependencyService concreteUtilDS(#Qualifier("ConcreteUtil")Util util){
return new DependencyService (util);
}
I have two classes from custom library, that i can't change. Bass class have only constructor with custom param, that's not a bean. I want to pass param via child constructor, but i have no idea how to do that, so please help)
I tried this, but doesn't work. Idea underline param in Child constructor.
#Bean
public ChildClass childClass() {
return new ChildClass(new CustomParam(5));
}
Base class- can't use #Component, that class from library
public abstract class BaseClass {
private CustomParam customParam;
protected BaseClass(CustomParam customParam) {
this.customParam = customParam;
}
public Integer getCustomParam() {
return customParam.getParamValue();
}
}
Child class. My own extension
#Component
public class ChildClass extends BaseClass {
//idea underline customParam "could not autowire"
public ChildClass(CustomParam customParam) {
super(customParam);
}
}
Param class- can't use #Component, that class from library
public class CustomParam {
private Integer paramValue;
public CustomParam(Integer paramValue) {
this.paramValue = paramValue;
}
public Integer getParamValue() {
return paramValue;
}
public void setParamValue(Integer paramValue) {
this.paramValue = paramValue;
}
}
CustomParam need not to be annotated with #Component annotation, still you can declare it as bean using#Bean annotation
Config class
#Bean
public ChildClass childClass() {
return new ChildClass(customParam());
}
#Bean
public CustomParam customParam() {
return new CustomParam(5);
}
This should work. If you instantiate your bean like this you don't need the #Component annotation on your ChildClass. Make sure your bean definition is in a configurationclass (#Configuration) and your config is part of the component scan.
#Configuration
public class Config {
#Bean
public BaseClass childClass() {
return new ChildClass(new CustomParam(5));
}
}
I'm using Spring Beans with annotations and I need to choose different implementation at runtime.
#Service
public class MyService {
public void test(){...}
}
For example for windows's platform I need MyServiceWin extending MyService, for linux platform I need MyServiceLnx extending MyService.
For now I know only one horrible solution:
#Service
public class MyService {
private MyService impl;
#PostInit
public void init(){
if(windows) impl=new MyServiceWin();
else impl=new MyServiceLnx();
}
public void test(){
impl.test();
}
}
Please consider that I'm using annotation only and not XML config.
1. Implement a custom Condition
public class LinuxCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux"); }
}
Same for Windows.
2. Use #Conditional in your Configuration class
#Configuration
public class MyConfiguration {
#Bean
#Conditional(LinuxCondition.class)
public MyService getMyLinuxService() {
return new LinuxService();
}
#Bean
#Conditional(WindowsCondition.class)
public MyService getMyWindowsService() {
return new WindowsService();
}
}
3. Use #Autowired as usual
#Service
public class SomeOtherServiceUsingMyService {
#Autowired
private MyService impl;
// ...
}
Let's create beautiful config.
Imagine that we have Animal interface and we have Dog and Cat implementation. We want to write write:
#Autowired
Animal animal;
but which implementation should we return?
So what is solution? There are many ways to solve problem. I will write how to use #Qualifier and Custom Conditions together.
So First off all let's create our custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
public #interface AnimalType {
String value() default "";
}
and config:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class AnimalFactoryConfig {
#Bean(name = "AnimalBean")
#AnimalType("Dog")
#Conditional(AnimalCondition.class)
public Animal getDog() {
return new Dog();
}
#Bean(name = "AnimalBean")
#AnimalType("Cat")
#Conditional(AnimalCondition.class)
public Animal getCat() {
return new Cat();
}
}
Note our bean name is AnimalBean. why do we need this bean? because when we inject Animal interface we will write just #Qualifier("AnimalBean")
Also we crated custom annotation to pass the value to our custom Condition.
Now our conditions look like this (imagine that "Dog" name comes from config file or JVM parameter or...)
public class AnimalCondition implements Condition {
#Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
if (annotatedTypeMetadata.isAnnotated(AnimalType.class.getCanonicalName())){
return annotatedTypeMetadata.getAnnotationAttributes(AnimalType.class.getCanonicalName())
.entrySet().stream().anyMatch(f -> f.getValue().equals("Dog"));
}
return false;
}
}
and finally injection:
#Qualifier("AnimalBean")
#Autowired
Animal animal;
You can move the bean injection into the configuration, as:
#Configuration
public class AppConfig {
#Bean
public MyService getMyService() {
if(windows) return new MyServiceWin();
else return new MyServiceLnx();
}
}
Alternatively, you may use profiles windows and linux, then annotate your service implementations with the #Profile annotation, like #Profile("linux") or #Profile("windows"), and provide one of this profiles for your application.
Autowire all your implementations into a factory with #Qualifier annotations, then return the service class you need from the factory.
public class MyService {
private void doStuff();
}
My Windows Service:
#Service("myWindowsService")
public class MyWindowsService implements MyService {
#Override
private void doStuff() {
//Windows specific stuff happens here.
}
}
My Mac Service:
#Service("myMacService")
public class MyMacService implements MyService {
#Override
private void doStuff() {
//Mac specific stuff happens here
}
}
My factory:
#Component
public class MyFactory {
#Autowired
#Qualifier("myWindowsService")
private MyService windowsService;
#Autowired
#Qualifier("myMacService")
private MyService macService;
public MyService getService(String serviceNeeded){
//This logic is ugly
if(serviceNeeded == "Windows"){
return windowsService;
} else {
return macService;
}
}
}
If you want to get really tricky you can use an enum to store your implementation class types, and then use the enum value to choose which implementation you want to return.
public enum ServiceStore {
MAC("myMacService", MyMacService.class),
WINDOWS("myWindowsService", MyWindowsService.class);
private String serviceName;
private Class<?> clazz;
private static final Map<Class<?>, ServiceStore> mapOfClassTypes = new HashMap<Class<?>, ServiceStore>();
static {
//This little bit of black magic, basically sets up your
//static map and allows you to get an enum value based on a classtype
ServiceStore[] namesArray = ServiceStore.values();
for(ServiceStore name : namesArray){
mapOfClassTypes.put(name.getClassType, name);
}
}
private ServiceStore(String serviceName, Class<?> clazz){
this.serviceName = serviceName;
this.clazz = clazz;
}
public String getServiceBeanName() {
return serviceName;
}
public static <T> ServiceStore getOrdinalFromValue(Class<?> clazz) {
return mapOfClassTypes.get(clazz);
}
}
Then your factory can tap into the Application context and pull instances into it's own map. When you add a new service class, just add another entry to the enum, and that's all you have to do.
public class ServiceFactory implements ApplicationContextAware {
private final Map<String, MyService> myServices = new Hashmap<String, MyService>();
public MyService getInstance(Class<?> clazz) {
return myServices.get(ServiceStore.getOrdinalFromValue(clazz).getServiceName());
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
myServices.putAll(applicationContext.getBeansofType(MyService.class));
}
}
Now you can just pass the class type you want into the factory, and it will provide you back the instance you need. Very helpful especially if you want to the make the services generic.
Simply make the #Service annotated classes conditional:
That's all. No need for other explicit #Bean methods.
public enum Implementation {
FOO, BAR
}
#Configuration
public class FooCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.FOO == implementation;
}
}
#Configuration
public class BarCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Implementation implementation = Implementation.valueOf(context.getEnvironment().getProperty("implementation"));
return Implementation.BAR == implementation;
}
}
Here happens the magic.
The condition is right where it belongs: At the implementating classes.
#Conditional(FooCondition.class)
#Service
class MyServiceFooImpl implements MyService {
// ...
}
#Conditional(BarCondition.class)
#Service
class MyServiceBarImpl implements MyService {
// ...
}
You can then use Dependency Injection as usual, e.g. via Lombok's #RequiredArgsConstructor or #Autowired.
#Service
#RequiredArgsConstructor
public class MyApp {
private final MyService myService;
// ...
}
Put this in your application.yml:
implementation: FOO
👍 Only the implementations annotated with the FooCondition will be instantiated. No phantom instantiations. 👍
Just adding my 2 cents to this question. Note that one doesn't have to implement so many java classes as the other answers are showing. One can simply use the #ConditionalOnProperty. Example:
#Service
#ConditionalOnProperty(
value="property.my.service",
havingValue = "foo",
matchIfMissing = true)
class MyServiceFooImpl implements MyService {
// ...
}
#ConditionalOnProperty(
value="property.my.service",
havingValue = "bar")
class MyServiceBarImpl implements MyService {
// ...
}
Put this in your application.yml:
property.my.service: foo
MyService.java:
public interface MyService {
String message();
}
MyServiceConfig.java:
#Configuration
public class MyServiceConfig {
#Value("${service-type}")
MyServiceTypes myServiceType;
#Bean
public MyService getMyService() {
if (myServiceType == MyServiceTypes.One) {
return new MyServiceImp1();
} else {
return new MyServiceImp2();
}
}
}
application.properties:
service-type=one
MyServiceTypes.java
public enum MyServiceTypes {
One,
Two
}
Use in any Bean/Component/Service/etc. like:
#Autowired
MyService myService;
...
String message = myService.message()
I'm not sure how to use custom qualifier interface with component scanning and autowire in Spring. I have an interface:
#Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface BigBean {
}
a bean I want to injected to:
#Component
public class Bean {
#Autowired
#BigBean("A")
private SomeBean sb;
public SomeBean getSb() {
return sb;
}
public void setSb(SomeBean sb) {
this.sb = sb;
}
}
and beans of the same type to be distinguished by custom qualifier:
#Component
#BigBean("A") //<-????
public class SmallBeanA implements SomeBean{
}
#Component
public class SmallBeanB implements SomeBean{
}
What I found in spring documentation doesn't compile in my case. How to use this custom qualifier I have?
You will need to add attribute value to BigBean annotation as
#Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface BigBean {
String value() default "";
}
This a bit old thread now, not sure if my answer will be of any help or not but let me try. Below is code to inject a selected instance of SomeBean.
#Qualifier
#Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface BigBean {
String value() default "";
}
public interface SomeBean {
}
#Component
#BigBean("A")
public class SmallBeanA implements SomeBean {
private SmallBeanA() {
System.out.println("SmallBeanA constructed");
}
#Override
public String toString() {
return "SmallBeanA";
}
}
#Component
#BigBean("B")
public class SmallBeanB implements SomeBean{
private SmallBeanB() {
System.out.println("SmallBeanB constructed");
}
#Override
public String toString() {
return "SmallBeanB";
}
}
#Component
public class Bean {
#Autowired
#BigBean("A")
private SomeBean sb;
public SomeBean getSb() {
return sb;
}
#Override
public String toString() {
return "Bean [sb=" + sb + "]";
}
}
public class QualifierTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Module.xml");
Bean myBean = context.getBean(Bean.class);
System.out.println(myBean.getSb().toString());
//OR
System.out.println(myBean.toString());
}
}
For a simple POJO:
#Component
public class Foo
{
private final String string;
public Foo()
{
this("Secondary ComponentScan??");
}
public Foo(String string)
{
this.string = string;
}
#Override
public String toString()
{
return string;
}
}
and this configuration
#Configuration
#ComponentScan(basePackageClasses = Foo.class)
public class TestConfiguration
{
#Primary
#Bean
public Foo foo()
{
return new Foo("Primary bean!!");
}
}
I would expect the following test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfiguration.class)
public class Test
{
#Autowired
private Foo foo;
#Test
public void test()
{
System.out.println(foo);
}
}
to print out Primary Bean!! but it returns Secondary ComponentScan?? instead...
How come? Nowhere does the documentation for #Primary say it fails against component-scanned beans!
The reason is that both beans actually have the same name foo, so internally one bean definition is getting overridden with the other one, essentially the one with #Bean is getting overridden by the one being scanned by #ComponentScan.
The fix is simply to give one of them a different name and you should see the correct behavior of the #Primary bean getting injected.
#Primary
#Bean
public Foo foo1()
{
return new Foo("Primary bean!!");
}
OR
#Component("foo1")
public class Foo
{
..