if I have a class like this:
#Service
#Scope("prototype")
public class TraderStarter {
private String address;
}
and TraderStarter should be created X times, X is dynamically determined by databases. How should I get these beans?
Only like this?
#Component("SpringContextUtil")
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
#SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
}
using SpringContextUtil .getBean() and set parameters for every prototype TraderStarter?
Thanks a lot.
In a nutshell, prototype means that the new bean is created upon each request for this bean (injections into different classes / getBean call).
So if you want to create these prototype beans, someone triggers this process.
Of course one way is to use the ApplicationContext but IMO its a bad approach, since your code becomes coupled to spring.
You cannot inject prototype into the singleton, this doesn't work.
However you can use javax.inject.Provider interface that is integrated with Spring:
Here is an example:
// from your example
// you can use injection here and everything, its a regular spring bean
#Service
#Scope("prototype")
public class TraderStarter {
private String address;
public void setAddress(String address) {
this.address = address;
}
}
///////////////////////
#Component
public class MyDbManager {
private Provider<TraderStarter> traderStarterProvider;
public List<TraderStarter> dynamicallyCreateBeans() {
List<String> addresses = dbManager.findAllAddresses();// to to the db, get the data
return
addresses.stream()
.map(this::createTraderStarter) // <-- creates different beans!
.collect(Collectors.toList());
}
private TraderStarter createTraderStarter(String address) {
TraderStarter ts = provider.get();
ts.setAddress(address);
return ts;
}
}
There are other methods as well with factories (see ObjectFactory) and creating proxies (Lookup method and scoped proxy), but IMO this is the most clean approach.
In any case if you opt for other solutions, read this tutorial
Related
I want Spring to create 2 instances of FooController. Requests to /foo should be handled by one of the instances and requests to /bar should be handled by the other instance. I want something like the below, but of course #RequestMapping doesn't work that way and also Spring gives me the ambiguous mapping error on FooController as well.
#RestController
public class FooController {
String name;
public FooController(String name) { this.name = name; }
}
#Configuration
public class FooControllerConfig {
#Bean
#RequestMapping("/foo")
public FooController getFooFooController(){
return new FooController("foo");
}
#Bean
#RequestMapping("/bar")
public FooController getBarFooController(){
return new FooController("bar");
}
}
I'm really confused by why you need this requirement? Can you please explain why this is required? Is it that each mapping requires a different name?
First you do not map Beans to a RequestMapping. While I am not even sure the spring application would start it would potentially create a new Bean with an identical name every time you access one of these mappings which would probably throw an error.
You could potentially overcome the duplicate names with your own annotation processing but that is way more work then this looks like it is worth.
Just looking at what you have there is there any reason why the following will not meet your requirements?
#RestController
public class FooController {
private static final fooName = "fooName";
private static final barName = "barName";
#RequestMapping("/foo")
public String getFoo(){
return fooName;
}
#RequestMapping("/bar")
public String getBar(){
return barName;
}
}
Don't try this at home. This code was performed by a bored, trained professional...
You can have multiple instances of the same controller class, each of which handles a different URL through the same or a different method in the controller. The only thing is, I don't know how to do it with just annotations. The way I just did it was to dynamically register each request mapping at initialization time. The FooController becomes a prototype bean (defined with annotations) so you can have Spring instantiate it multiple times, once for each mapping
FooController.java
#Controller
#Scope("prototype")
public class FooController {
private String name;
public FooController() {}
public FooController(String name) {
this.name = name;
}
public ResponseEntity<String> handleRequests() throws Exception {
return new ResponseEntity<>("Yo: " + name + " " + this.hashCode(), HttpStatus.OK);
}
EndpointService.java
#Service
public class EndpointService {
#Autowired
private BeanFactory beanFactory;
#Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
public void addFooController(String urlPath, String name) throws NoSuchMethodException {
RequestMappingInfo requestMappingInfo = RequestMappingInfo
.paths(urlPath)
.methods(RequestMethod.GET)
.produces(MediaType.APPLICATION_JSON_VALUE)
.build();
requestMappingHandlerMapping.registerMapping(requestMappingInfo,
beanFactory.getBean(FooController.class, name),
FooController.class.getDeclaredMethod("handleRequests"));
}
#EventListener
public void handleContextRefreshEvent(ContextRefreshedEvent ctxStartEvt) {
try {
addFooController("/blah1", "blahblah1");
addFooController("/blah2", "blahblah2");
addFooController("/blah3", "blahblah3");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
Results:
http://localhost:8080/blah1 returns: Yo: blahblah1 1391627345
http://localhost:8080/blah3 returns: Yo: blahblah3 2078995154
I have this non-managed class that I want to inject spring beans (that I don't known a-priory what they are). How can I do that?
For example, let's say I have the following class:
public class NonManagedClass extends APIClass {
#Resource
private Service1 service;
#Resource
private Service2 service2;
// here i can declare many different dependencies
#Resource
private ServiceN serviceN;
#Override
public void executeBusinessStuffs() {
// business logics
}
}
I need in someway to let spring inject these dependencies in my class. I have access to these objects after created, so it's easy to me call any method that can accomplish this functionality. For example:
#Service
public void SomeAPIService {
#Resource
private BeanInjector beanInjector; // I'm looking for some funcionality of spring like this
public void someProcessingFunction(Class<? extends APIClass> clazz) throws Exception {
APIClass instance = clazz.getConstructor().newInstance();
beanInjector.injectBeans(instance);
instance.executeBusinessStuffs();
}
}
Does Spring have such functionality to inject beans based on fields annotation for a non-managed class?
Replace BeanInjector with ApplicationContext and you are almost there. From there you can get the AutowireCapableBeanFactory which provides some handy methods like createBean and autowireBean.
#Service
public void SomeAPIService {
#Resource
private ApplicationContext ctx;
public void someProcessingFunction(Class<? extends APIClass> clazz) throws Exception {
APIClass instance = ctx.createBean(clazz);
instance.executeBusinessStuffs();
}
}
or if you really like to construct stuff yourself instead of using the container:
#Service
public void SomeAPIService {
#Resource
private ApplicationContext ctx;
public void someProcessingFunction(Class<? extends APIClass> clazz) throws Exception {
APIClass instance = clazz.getConstructor().newInstance();
ctx.getAutowireCapableBeanFactory().autowireBean(instance);
instance.executeBusinessStuffs();
}
}
Is it possible to Autowire fields in a dynamic class?
I am getting a class name from the database and I want to autowire this class
Short Answer
That's not possible. Spring needs to know what Beans there are for injecting them.
Long Answer
You could #Autowire every possible bean into a class and then cache them in a Map, where the Class represents the key, and the Object the value. See below simplified example:
public class MyClass{
private final Map<Class<?>, Object> cache = new HashMap<>();
#Autowired
public MyClass(Service1 s1, Service2 s2){
// registering the beans
cache.put(Service1.class, s1);
cache.put(Service2.class, s2);
}
public <T> T getService(String className) throws ClassNotFoundException{
// getting the bean
Class<?> clazz = Class.forName(className);
return (T) cache.get(clazz);
}
}
Not sure it's a good idea, but you can inject a class like mentionned here :
Injecting beans into a class outside the Spring managed context
You can try this:
import javax.annotation.PostConstruct;
#Component
public class ApplicationContextAccessor {
private static ApplicationContextAccessor instance;
#Autowired
private ApplicationContext applicationContext;
public static T getBean(Class clazz) {
return instance.applicationContext.getBean(clazz);
}
#PostConstruct
private void registerInstance() {
instance = this;
}
}
Read this post : https://www.helicaltech.com/uses-of-springs-applicationcontext-while-using-reflection/
Is there some way to use #Autowired with static fields. If not, are there some other ways to do this?
In short, no. You cannot autowire or manually wire static fields in Spring. You'll have to write your own logic to do this.
#Component("NewClass")
public class NewClass{
private static SomeThing someThing;
#Autowired
public void setSomeThing(SomeThing someThing){
NewClass.someThing = someThing;
}
}
#Autowired can be used with setters so you could have a setter modifying an static field.
Just one final suggestion... DON'T
Init your autowired component in #PostConstruct method
#Component
public class TestClass {
private static AutowiredTypeComponent component;
#Autowired
private AutowiredTypeComponent autowiredComponent;
#PostConstruct
private void init() {
component = this.autowiredComponent;
}
public static void testMethod() {
component.callTestMethod();
}
}
Create a bean which you can autowire which will initialize the static variable as a side effect.
Wanted to add to answers that auto wiring static field (or constant) will be ignored, but also won't create any error:
#Autowired
private static String staticField = "staticValue";
You can achieve this using XML notation and the MethodInvokingFactoryBean. For an example look here.
private static StaticBean staticBean;
public void setStaticBean(StaticBean staticBean) {
StaticBean.staticBean = staticBean;
}
You should aim to use spring injection where possible as this is the recommended approach but this is not always possible as I'm sure you can imagine as not everything can be pulled from the spring container or you maybe dealing with legacy systems.
Note testing can also be more difficult with this approach.
You can use ApplicationContextAware
#Component
public class AppContext implements ApplicationContextAware{
public static ApplicationContext applicationContext;
public AppBeans(){
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
then
static ABean bean = AppContext.applicationContext.getBean("aBean",ABean.class);
Disclaimer This is by no means standard and there could very well be a better spring way of doing this. None of the above answers address the issues of wiring a public static field.
I wanted to accomplish three things.
Use spring to "Autowire" (Im using #Value)
Expose a public static value
Prevent modification
My object looks like this
private static String BRANCH = "testBranch";
#Value("${content.client.branch}")
public void finalSetBranch(String branch) {
BRANCH = branch;
}
public static String BRANCH() {
return BRANCH;
}
We have checked off 1 & 2 already now how do we prevent calls to the setter, since we cannot hide it.
#Component
#Aspect
public class FinalAutowiredHelper {
#Before("finalMethods()")
public void beforeFinal(JoinPoint joinPoint) {
throw new FinalAutowiredHelper().new ModifySudoFinalError("");
}
#Pointcut("execution(* com.free.content.client..*.finalSetBranch(..))")
public void finalMethods() {}
public class ModifySudoFinalError extends Error {
private String msg;
public ModifySudoFinalError(String msg) {
this.msg = msg;
}
#Override
public String getMessage() {
return "Attempted modification of a final property: " + msg;
}
}
This aspect will wrap all methods beginning with final and throw an error if they are called.
I dont think this is particularly useful, but if you are ocd and like to keep you peas and carrots separated this is one way to do it safely.
Important Spring does not call your aspects when it calls a function. Made this easier, to bad I worked out the logic before figuring that out.
Generally, setting static field by object instance is a bad practice.
to avoid optional issues you can add synchronized definition, and set it only if private static Logger logger;
#Autowired
public synchronized void setLogger(Logger logger)
{
if (MyClass.logger == null)
{
MyClass.logger = logger;
}
}
:
Solution 1 : Using Constructor #Autowired For Static Field
#Component
public class MyClass {
private static MyService service;
#Autowired
public MyClass(MyService service) {
TestClass.service= service;
}
}
Solution 2 : Using #PostConstruct to set the value to Static Field
#Component
public class MyClass {
private static MyService service;
#Autowired
private MyService srv;
#PostConstruct
public void init() {
this.service= srv;
}
}
Refer here for more detail
I use private static inner Component: FieldSetter, to inject static field: MyBean, at last SelfDestroyBean will help me remove redundant FiledSetter bean
public final class MyClass {
private static MyBean myBean;
#Component
private static class FieldSetter extends SelfDestroyBean {
public FieldSetter(MyBean myBean) {
MyClass.myBean = myBean;
}
}
}
#SuppressWarnings("SpringJavaAutowiredMembersInspection")
public abstract class SelfDestroyBean {
#Autowired
private ApplicationContext context;
#PostConstruct
public void destroy() {
final String[] beanNames = context.getBeanNamesForType(this.getClass());
final BeanDefinitionRegistry registry =
((BeanDefinitionRegistry) context.getAutowireCapableBeanFactory());
for (String beanName : beanNames) {
registry.removeBeanDefinition(beanName);
}
}
}
private static UserService userService = ApplicationContextHolder.getContext().getBean(UserService.class);
I have a Controller class which is invoked first in my application. There I was planning to retrieve a value from a Map from a Service class.
Here's the controller:
#Controller
public class AppController {
public Service doSomethingWithTheMap(String key) {
return ServiceImpl.getMapValueFor(key).exec();
}
}
I get issues because during the initialization, well during the put of values to the Service's Map to be more precise, I require the BeanFactory because the values in the Map are Service implementations.
Doing it in a static block will cause the BeanFactory to be null because it is not injected yet I would guess.
So ending up with this initMap() call makes me feel a bit like ... there should be a better solution.
Any hints somebody?
I have to admit that I am new to Spring and maybe I mess things up here. FYI the Map came into my mind after having endless if else checks deciding which Service to call based on a String input. Therefore I replaced it with the Map and a simple one liner in the Controller.
ServiceImpl.getMapValueFor(key).exec();
Here' the Service class:
#Service
public class ServiceImpl {
private static Map<String, Service> map;
private static ApplicationContext context;
#Autowired
public void setApplicationContext(ApplicationContext factory) {
this.context = factory;
}
public static Service getMapValueFor(String key) {
if (map == null) {
initMap();
}
return map.get(key);
}
private static void initMap() {
/*
* FIXME: We can not init the map in a static block or directly
* initialize it since the factory is not injected until execution of a
* static block and will be null.
*/
BeanFactory factory = context;
map = new HashMap<String, Service>();
map.put("key", factory.getBean(SomeService.class));
}
}
The first thing I want to say is that you have a bug, because you are using a HashMap with no synchronization! - Don't be alarmed many (if not most) java developers would make the same mistake.
Unless you have oversimplified the code, your service looks more like a command than a service; A service is a singleton. It is not impossible for services to have methods without arguments, but I would say it is uncommon. Are you sure you should not be using prototype beans instead of singletons ?
Typically the number of services are finite, and if you have multiple services of the same type you would use the #Qualifier when autowiring them. In any case this code looks dodgy to me, so perhaps you should try to explain the problem at a higher level, because there may be a better way than you current code-path.
Your service class ServiceImpl must implement the interface org.springframework.context.ApplicationContextAware to get the instance of Spring's application context.
Here is a very basic solution, it uses the fact that the name of a #Bean is the name of the method which creates it, you will probably need a better strategy. The idea is to hide getBean inside a Provider class which can then be Autowired (and tested)
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
CallableProvider provider = ctx.getBean(CallableProvider.class);
System.out.println(provider.getCommand("aCommand").call());
System.out.println(provider.getCommand("bCommand").call());
}
public static class Config {
#Bean
public ACommand aCommand() {
return new ACommand();
}
#Bean
public BCommand bCommand() {
return new BCommand();
}
#Bean
public CallableProvider callableProvider() {
return new CallableProvider();
}
}
public static class CallableProvider implements ApplicationContextAware {
private ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
public Command getCommand(String name) {
return context.getBean(name, Command.class);
}
}
public static class ACommand implements Command {
// autowire stuff
#Override
public String call() {
return "A";
}
}
public static class BCommand implements Command {
// autowire stuff
#Override
public String call() {
return "B";
}
}
public interface Command {
String call();
}
}