I want to have something like this:
#Servise
public class BeanAFactory implements FactoryBean<BeanA>{
#Autowired <…>;
#Override
public BeanA getObject() throws Exception {
return new BeanAImpl();
}
#Override
public Class<BeanA> getObjectType() {
return BeanA.class;
}
#Override
public boolean isSingleton() {
return false;
}
private class BeanAImpl implements BeanA {
<…>
}
}
and to have autowiring of result of BeanAFactory.getObject() on BeanA: ctx.getBean(BeanA.class) should return result of BeanAFactory.getObject(). Is it possible?
Yes, Declare object of BeanA in BeanAFactory class and initialize it in using default constructor so you will get that object while autowiring BeanAFactory class.
Related
I have my service class which does a post call. I would like to instantiate that bean/ autowire it to create a object in another class which is not a component or configuration class.
#Service
public class SavePayload {
// Rest Post Call implementation
}
public class PayloadRecord
implements Record {
private String payload;
PayloadProcessor payloadProcessor = new PayloadProcessor();
public PayloadRecord(String payload) {
this.payload = payload;
}
#SneakyThrows
#Override
public boolean isValid() throws ValidationException {
payloadProcessor.savePayload(payload);
return true;
}
#Override
public byte[] getBytes(Charset charset) {
return payload.getBytes(StandardCharsets.UTF_8);
}
#Override
public String getID() {
return payload;
}
#Override
public String toString() {
return payload;
}
private static class PayloadProcessor {
#Autowired
private SavePayload savePayload;
}
}
I'm using a template which will do the record processing. As soon as I got message received I'm assigning it to Payload in Payload Record which is non component class. I would like to initialize the SavePayload service. Save payload service is returning null.
Create an application context aware class so you can get the current context, something like:
#Component
public class ContextAwareClass implements ApplicationContextAware {
private static ApplicationContext ctx;
public static ApplicationContext getApplicationContext() {
return ctx;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
ctx = applicationContext;
}
}
Then, just get the context and get the bean like:
public class YourRegularNoSpringComponentClass {
public void doSomething() {
System.out.println(ContextAwareClass
.getApplicationContext()
.getBean("savePayload")
);
}
}
Above will print the bean if it exist in your context. In your case you would simple use it rather than print it.
Hope this helps!
You will have to create an instance of ApplicationContext
You can explore
AnnotationConfigApplicationContext applicationContext= new AnnotationConfigApplicationContext();
and then use.
SavePayload savePayload = applicationContext.getBean("savePayload");
There are some class in jar (external library), that uses Spring internally.
So library class has structure like a:
#Component
public class TestBean {
#Autowired
private TestDependency dependency;
...
}
And library provides API for constructing objects:
public class Library {
public static TestBean createBean() {
ApplicationContext context = new AnnotationConfigApplicationContext(springConfigs);
return context.getBean(TestBean);
}
}
In my application, I have config:
#Configuration
public class TestConfig {
#Bean
public TestBean bean() {
return Library.createBean();
}
}
It's throw en exception: Field dependency in TestBean required a bean of type TestDependency that could not be found..
But Spring should not trying to inject something, because bean is already configured.
Can i disable Spring autowiring for a certain bean?
Based on #Juan's answer, created a helper to wrap a bean not to be autowired:
public static <T> FactoryBean<T> preventAutowire(T bean) {
return new FactoryBean<T>() {
public T getObject() throws Exception {
return bean;
}
public Class<?> getObjectType() {
return bean.getClass();
}
public boolean isSingleton() {
return true;
}
};
}
...
#Bean
static FactoryBean<MyBean> myBean() {
return preventAutowire(new MyBean());
}
This worked for me:
import org.springframework.beans.factory.FactoryBean;
...
#Configuration
public class TestConfig {
#Bean
public FactoryBean<TestBean> bean() {
TestBean bean = Library.createBean();
return new FactoryBean<TestBean>()
{
#Override
public TestBean getObject() throws Exception
{
return bean;
}
#Override
public Class<?> getObjectType()
{
return TestBean.class;
}
#Override
public boolean isSingleton()
{
return true;
}
};
}
}
It seems like it's impossible to disable autowiring for a specific bean.
So there is some workaround.
We can make wrapper for a target bean and use it instead of original bean:
public class TestBeanWrapper {
private final TestBean bean;
public TestBeanWrapper(TestBean bean) {
this.bean = bean;
}
public TestBean bean() {
return bean;
}
}
#Configuration
public class TestConfig {
#Bean
public TestBeanWrapper bean() {
return new TestBeanWrapper(Library.createBean());
}
}
#RestController
public class TestController {
#Autowired
private TestBeanWrapper bean;
...
}
Not exactly but you can add required=false (#Autowired(required=false)) in your autowired annotation. But be careful that might get you NullPointer exception
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 am investigating how does FactoryBean works in spring framework.
As I understand it allow configure instantiation process.
I have the following beans:
#Component
public class MyInjectionClass {
String name;
Integer age;
//get and set methods
}
and
#Component
public class MyComponent {
#Autowired
MyInjectionClass myInjectionClass;
public MyInjectionClass getMyInjectionClass() {
return myInjectionClass;
}
}
and following cutom FactoryBean:
#Component
public class MyInjectionClassFactoryBean implements FactoryBean<MyInjectionClass> {
#Override
public MyInjectionClass getObject() throws Exception {
MyInjectionClass myInjectionClass = new MyInjectionClass();
myInjectionClass.setName("name");
myInjectionClass.setAge(12);
return myInjectionClass;
}
#Override
public Class<?> getObjectType() {
return MyInjectionClass.class;
}
#Override
public boolean isSingleton() {
return false;
}
}
Also I have wrote following code in my main method:
MyComponent bean = context.getBean(MyComponent.class);
System.out.println(bean.getMyInjectionClass().getAge());
It returns null.
What did I forget to do ?
P.S.
I use #ComponentScan("com.example.domain")
All beans and FactoryBean located there.
Solution
remove #Component above MyInjectionClass
If I'm writing a static factory method to create objects, how do I use the '#Component' annotation for that factory class and indicate (with some annotation) the static factory method which should be called to create beans of that class? Following is the pseudo-code of what I mean:
#Component
class MyStaticFactory
{
#<some-annotation>
public static MyObject getObject()
{
// code to create/return the instance
}
}
I am afraid you can't do this currently. However it is pretty simple with Java Configuration:
#Configuration
public class Conf {
#Bean
public MyObject myObject() {
return MyStaticFactory.getObject()
}
}
In this case MyStaticFactory does not require any Spring annotations. And of course you can use good ol' XML instead.
You need to use the spring interface FactoryBean.
Interface to be implemented by objects used within a BeanFactory which
are themselves factories. If a bean implements this interface, it is
used as a factory for an object to expose, not directly as a bean
instance that will be exposed itself.
Implement the interface and declare a bean for it. For example :
#Component
class MyStaticFactoryFactoryBean implements FactoryBean<MyStaticFactory>
{
public MyStaticFactory getObject()
MyStaticFactory.getObject();
}
public Class<?> getObjectType() {
return MyStaticFactory.class;
}
public boolean isSingleton() {
return true;
}
}
Through #Component and component scanning, this class will be discovered. Spring will detect that it is a FactoryBean and expose the object you return from getObject as a bean (singleton if you specify it).
Alternatively, you can provide a #Bean or <bean> declaration for this FactoryBean class.
Bean:
public class MyObject {
private String a;
public MyObject(String a) {
this.a = a;
}
#Override
public String toString() {
return a;
}
}
FactoryBean:
#Component
public class MyStaticFactory implements FactoryBean<MyObject> {
#Override
public MyObject getObject() throws Exception {
return new MyObject("StaticFactory");
}
#Override
public Class<?> getObjectType() {
return MyObject.class;
}
#Override
public boolean isSingleton() {
return true;
}
}
Use:
#Component
public class SomeClass{
#Autowired
MyObject myObject;
}
Spring boot:static factory method:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
enum ParsersConst {
bofa, jpm, wellsforgo
}
interface Parser {
String readFromFile(String file);
}
class JPM implements Parser {
#Override
public String readFromFile(String file) {
System.out.println("From JPM Parser");
return "JPM";
}
}
class Bofa implements Parser {
#Override
public String readFromFile(String file) {
System.out.println("From Bofa Parser");
return "BOFA";
}
}
class WellsForgo implements Parser {
#Override
public String readFromFile(String file) {
System.out.println("From Wellsforgo Parser");
return "WellsForgo";
}
}
class ParserCreator {
public static Parser createParser(ParsersConst parsConst) {
if (ParsersConst.bofa.equals(parsConst)) {
return new Bofa();
} else if (ParsersConst.jpm.equals(parsConst)) {
return new JPM();
} else if (ParsersConst.wellsforgo.equals(parsConst)) {
return new WellsForgo();
}
throw new IllegalArgumentException("Unknown Parser");
}
}
#Configuration
class ParserConfig{
#Bean
public Parser bofa() {
return ParserCreator.createParser(ParsersConst.bofa);
}
#Bean
public Parser wellsforgo() {
return ParserCreator.createParser(ParsersConst.wellsforgo);
}
#Bean
public Parser jpm() {
return ParserCreator.createParser(ParsersConst.jpm);
}
}
#Component
public class StaticFacotryDemo implements CommandLineRunner{
#Autowired
private ApplicationContext context;
#Override
public void run(String... args) throws Exception {
Parser parser = (Parser) context.getBean(ParsersConst.jpm.toString());
System.out.println(parser);
System.out.println(parser.readFromFile("jan_stmt.pdf"));
}
}