It seems that the setter on my bean is not working.
This is my Spring java configuration, SpringConfig.java:
#Configuration
#ComponentScan("com.xxxx.xxxxx")
public class SpringConfig {
#Bean(name="VCWebserviceClient")
public VCWebserviceClient VCWebserviceClient() {
VCWebserviceClient vCWebserviceClient = new VCWebserviceClient();
vCWebserviceClient.setSoapServerUrl("http://localhost:8080/webservice/soap/schedule");
return vCWebserviceClient;
}
The VCWebserviceClient.java:
#Component
public class VCWebserviceClient implements VCRemoteInterface {
private String soapServerUrl;
public String getSoapServerUrl() {
return soapServerUrl;
}
public void setSoapServerUrl(String soapServerUrl) {
this.soapServerUrl = soapServerUrl;
}
// Implemented methods...
}
My app.java:
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
VCWebserviceClient obj = (VCWebserviceClient) context.getBean("VCWebserviceClient");
System.out.println("String: "+obj.getSoapServerUrl()); // returns NULL
Why is obj.getSoapServerUrl() returning NULL?
This example shows how it should work.
The instance returned by VCWebserviceClient is not the one actually used by your application. It is a way for Spring to know what class to instanciate.
Any way, for you issue, use the #Value (http://docs.spring.io/spring/docs/3.0.x/reference/expressions.html)
#Component
public class VCWebserviceClient implements VCRemoteInterface {
// spring resolves the property and inject the result
#Value("'http://localhost:8080/webservice/soap/schedule'")
private String soapServerUrl;
// spring automatically finds the implementation and injects it
#Autowired
private MyBusinessBean myBean;
public String getSoapServerUrl() {
return soapServerUrl;
}
public void setSoapServerUrl(String soapServerUrl) {
this.soapServerUrl = soapServerUrl;
}
// Implemented methods...
}
Related
I have a legacy application which uses Spring for bean management (AnnotationConfigWebApplicationContext) and CDI (inject, brought by jersey-spring dependency) for DI.
I have the following situation:
#Service
#RequestScoped
#Scope(value = "request")
public class InjectedClass {
private SomeEnum someAttribute;
public void getSomeAttribute() {
return someAttribute;
}
}
#Service
public class MiddleLayer {
#Inject
public MiddleLayer(InjectedClass injectedClass) {
}
private void middleLayerMethod() {
if (someAttribute == SomeEnum.Y) {
// do something specific
}
}
}
// controller
public class SomeController {
#Inject
// In this flow injectedClass instance is initialised with SomeAttribute = Y by ContainerRequestFilter
public SomeController(MiddleLayer middleLayer) {
}
public void someMethod () {
MethodResult result = middleLayer.middleLayerMethod();
// do some additional things with result
}
}
#Component
public class PeriodicActivity {
#Inject
// I need this MiddleLayer to be injected with injectedClass instance where SomeAttribute = X, since it doesn't go via request filter
public PeriodicActivity(MiddleLayer middleLayer) {
}
public void method () {
MethodResult result = middleLayer.middleLayerMethod();
// do some other things with result
}
}
Without DI what I need to happen would look like this:
public class PeriodicActivity() {
InjectedClass injectedClassObjA = new InjectedClass();
injectedClassObjA.setSomeAttribute(X);
MiddleLayer middleLayer = new MiddleLayer(injectedClassObjA);
middleLayer.middleLayerMethod();
}
I am looking to do something similar with dependencies.
After some reading I am starting to wonder whether it is possible.
I have following spring bean with Prototype scope. In the AppRunner class, I want a new bean to injected by spring within the for loop (if loop count is 2, then i want only 2 new beans to be injected).
But spring injects a new bean every time the setter methods of the SimpleBean is called.
SimpleBean.java
#Component
#Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode =
ScopedProxyMode.TARGET_CLASS)
public class SimpleBean {
private String id;
private Long value;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getValue() {
return value;
}
public void setValue(Long value) {
this.value = value;
}
}
AppRunner.java
#Component
public class AppRunner {
#Autowired
SimpleBean simpleBean;
public void execute(List<Output> results){
List<SimpleBean> finalResults = new ArrayList<SimpleBean>();
for(Output o : results){
simpleBean.setId(o.getAppId());
simpleBean.setValue(o.getAppVal());
finalResults.add(simpleBean);
}
}
}
Output.java
public class Output {
private String appId;
private Long appVal;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public Long getAppVal() {
return appVal;
}
public void setAppVal(Long appVal) {
this.appVal = appVal;
}
}
Unfortunately prototype scope doesn't work like this. When your AppRunner bean is instantiated by the container it is asking for its dependencies. Then a new instance of SimpleBean is created. This instance stays as dependency. Prototype scope starts working when you will have multiple beans with dependency on SimpleBean. Like:
#Component
class BeanOne {
#Autowired
SimpleBean bean; //will have its own instance
}
#Component
class BeanTwo {
#Autowired
SimpleBean bean; //another instance
}
There is one rather straightforward update which can lead to your desired behaviour. You can remove autowired dependency and ask for a new dependency in your loop from context. It would look like this.
#Component
public class AppRunner {
#Autowired
ApplicationContext context;
public void execute(List<Output> results){
List<SimpleBean> finalResults = new ArrayList<SimpleBean>();
for(Output o : results) {
SimpleBean simpleBean = context.getBean(SimpleBean.class);
simpleBean.setId(o.getAppId());
simpleBean.setValue(o.getAppVal());
finalResults.add(simpleBean);
}
}
}
Other option could be technique called Method injection. It is described in the relevant documentation for prototype scope. You can take a look here 7.5.3 Singleton beans with prototype-bean dependencies
Assume I am creating a PrinterService class that has a AbstractPrinter object. AbstractPrinter is subclassed by classes such as HPPrinter, FilePrinter etc.
The exact kind of printer object to be used is mentioned in the RequestParam object passed to my Controller (it is a request attribute).
Is there any way I can inject the right kind of concrete printer class using Spring?
All the other dependencies are injected using #Autowired annotation. How to inject this one?
You can create and load a factory of AbstractPrinter objects during container startup as shown below and dynamically call the respective the AbstractPrinter's print() (or your own method) based on the input parameter (comes from controller) to the service.
In the below code for PrinterServiceImpl class, the main point is that all of the List<AbstractPrinter> will be injected by Spring container (depends upon how many implementation classes you provide like HPPrinter, etc..). Then you will load those beans into a Map during container startup with printerType as key.
#Controller
public class YourController {
#Autowired
private PrinterService printerService;
public X myMethod(#RequestParam("input") String input) {
printerService.myServiceMethod(input);
//return X
}
}
PrinterServiceImpl class:
public class PrinterServiceImpl implements PrinterService {
#Autowired
private List<AbstractPrinter> abstractPrinters;
private static final Map<String,AbstractPrinter> myPrinters = new HashMap<>();
#PostConstruct
public void loadPrinters() {
for(AbstractPrinter printer : abstractPrinters) {
myPrinters.put(printer.getPrinterType(), printer);
}
}
//Add your other Autowired dependencies here
#Override
public void myServiceMethod(String input){//get input from controller
AbstractPrinter abstractPrinter= myPrinters.get(input);
abstractPrinter.print();//dynamically calls print() depending on input
}
}
HPPrinter class:
#Component
public class HPPrinter implements AbstractPrinter {
#Override
public String getPrinterType() {
return "HP";
}
#Override
public void print() {
// Your print code
}
}
FilePrinter class:
#Component
public class FilePrinter implements AbstractPrinter {
#Override
public String getPrinterType() {
return "FILE";
}
#Override
public void print() {
// Your print code
}
}
You could create a dedicated PrinterService instance per AbstractPrinter concrete class. For example you could achieve this using Spring configuration which follow the factory pattern:
#Configuration
public class PrinterServiceConfiguration {
#Autowired
private HPPrinter hpPrinter;
#Autowired
private FilePrinter filePrinter;
#Bean
public PrinterService hpPrinterService() {
return new PrinterService(hpPrinter);
}
#Bean
public PrinterService filePrinterService() {
return new PrinterService(filePrinter);
}
public PrinterService findPrinterService(PrinterType type){
if (type == HP)
return hpPrinterService();
....
}
}
Then in your controller, inject PrinterServiceConfiguration then call findPrinterService with the right printer type.
Don't forget to add PrinterServiceConfiguration at your configuration #Import.
If the list of printer is dynamic you could switch to prototype bean :
#Configuration
public class PrinterServiceConfiguration {
#Autowired
private List<AbstractPrinter> printers;
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public PrinterService createPrinterService(PrinterType type){
return new PrinterService(findPrinterByType(type));
}
private Printer findPrinterByType(PrinterType type) {
// iterate over printers then return the printer that match type
// throw exception if no printer found
}
}
I am trying to inject a value into a Custom Annotation but Spring doesn't seem to be evaluating.
Here is my annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomAnno {
String var1();
String var2();
}
Here is my Bean (it is created in a Spring Configuration file) with Annotation:
public class MyClass {
#MyCustomAnno(var1 = "${some.property.one}",
var2 = "${some.property.two}")
public void someMethod() {
// do something here
}
}
Here is the Aspect where I am trying to use the values passed into the annotation:
#Aspect
public class MyAop {
#Around(value="#annotation(myCustomAnno)",argNames="myCustomAnno")
public Object aroundMethod(MyCustomAnno myCustomAnno) {
int intVar1 = Integer.parseInt(myCustomAnno.var1());
int intVar2 = Integer.parseInt(myCustomAnno.var2());
// ....
}
}
In the around method I am receiving a NumberFormatException: For input string: ${some.property.one}. This means that Spring didn't inject the value for some reason.
In case you are wondering, in the same class I can do the normal #Value annotation and the value gets injected properly:
#Value("${some.property.one}")
private propertyOne; // This works
Is it possible to do what I want to and if so, how?
AFAIK, placeholders are not resolved in custom annotations. However you could resolve them in the Aspect itself.
For example:
#Aspect
class MyAop implements EmbeddedValueResolverAware {
private StringValueResolver resolver;
#Around(value="#annotation(myCustomAnno)",argNames="myCustomAnno")
public void play(MyCustomAnno ann) {
System.out.println(resolver.resolveStringValue(ann.var1()));
}
#Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
}
// My Factory class
#Component
public class UserRewardAccountValidatorFactory {
#Autowired
private VirginAmericaValidator virginAmericaValidator;
private static class SingletonHolder {
static UserRewardAccountValidatorFactory instance = new UserRewardAccountValidatorFactory();
}
public static UserRewardAccountValidatorFactory getInstance() {
return SingletonHolder.instance;
}
private UserRewardAccountValidatorFactory() {}
public PartnerValidator getPartnerValidator(Partner partner){
return virginAmericaValidator;
}
}
// My Validator class
#Service
public class VirginAmericaValidator implements PartnerValidator {
#Override
public void validate(String code) throws InvalidCodeException{
//do some processing if processing fails throw exception
if (code.equals("bad".toString())){
throw new InvalidCodeException();
}
}
}
//Usage:
PartnerValidator pv = UserRewardAccountValidatorFactory.getInstance().getPartnerValidator(partner);
if (pv != null){
try {
pv.validate(userRewardAccount);
} catch (InvalidCodeException e){
return buildResponse(ResponseStatus.INVALID_USER_REWARD_ACCOUNT, e.getMessage());
}
}
My package scan level is at much higher level. Whats happening is my virginAmericaValidator is always empty. Why is #Autowired annotation not working.
Your current approach will not work with Spring as you are ultimately using new UserRewardAccountValidatorFactory to create the instance which essentially bypasses Spring context altogether. Two approaches that should possibly work are these:
a. Using a factory-method and using xml to define your bean:
<bean class="package.UserRewardAccountValidatorFactory" name="myfactory" factory-method="getInstance"/>
This will essentially return the instance that you are creating back as a Spring bean and should get autowired cleanly.
b. Using Java #Configuration based mechanism:
#Configuration
public class MyBeanConfiguration {
#Bean
public UserRewardAccountValidatorFactory myFactory() {
return UserRewardAccountValidatorFactory.getInstance();
}
}