When trying to inject an Optional<T>, Spring never calls my bean, and instead injects an Optional.empty().
Here is some sample code:
#Configuration
public class Initialize {
#Value("optionalValue")
private String testString;
#Bean (name = "getOptionalString")
public Optional<String> getOptionalString() {
return Optional.of(this.testString); //breakpoint put here, is never called
}
}
#Component
public class Test {
public Test(#Qualifier("getOptionalString") Optional<String> optional) {
// optional's value is Optional.empty() here
}
I noticed that (by putting a breakpoint) the #Bean is never called. If I were to remove the Optional<String>, and simply return a String, then it works!
I know Spring has its own optional dependency but I am perplexed as to why this doesn't work (whatever I read online says it should), and I also don't understand how it initialized it to Optional.empty()?
The documentation says:
you can express the non-required nature of a particular dependency through Java 8’s java.util.Optional, as the following example shows:
public class SimpleMovieLister {
#Autowired
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
So using Optional as the type of a bean is not a good idea.
Related
I am trying to initialize a Spring component with a set of all beans of a certain type (well really, anything I can iterate).
The Spring core documentation talks about collection merging, but only in the context of annotation-based configuration.
Suppose I have the following configuration
#Configuration
public class MyConfig {
#Bean
public SomeInterface single() {
return new SomeInterface() {};
}
#Bean
public Set<SomeInterface> multi() {
return Collections.singleton(
new SomeInterface() {}
);
}
}
Where the interface is defined as
public interface SomeInterface {}
I would like this component to get an aggregate of both beans - some collection containing both anonymous classes.
#Component
public class MyComponent {
public MyComponent(Set<SomeInterface> allInterfaces) {
System.out.println(allInterfaces.size()); // expecting 2, prints 1
}
}
I see why Spring has come to the result it has; it sees this method is expecting a Set<SomeInterface> and MyConfig::multi is a bean of type Set<SomeInterface>, so it autowires with that.
If I change the signature to Collection<SomeInterface>, it autowires with MyConfig::single. Again, I see why: there's nothing matching exactly, but there's beans of type SomeInterface (in this case, just one) so it constructs a temporary collection of them and autowires with that. Fine, but not what I'm after.
I would like the solution to be extensible so that if another bean is added, the dependent component does not need to change. I've tried using two parameters, each with a #Qualifier, and that works but is not extensible.
How can I get this to work?
As you already mentioned, MyConfig::multi is a bean of type Set<SomeInterface>, so autowiring Collection<Set<SomeInterface>> would give you all of those sets. The following should work
public MyComponent(Collection<SomeInterface> beans,
Collection<Set<SomeInterface>> beanSets) {
// merge both params here
}
If you need all implementations in multiple places it might make sense to define another bean containing the merged collection and autowire that bean:
static class SomeInterfaceCollection {
final Set<SomeInterface> implementations;
SomeInterfaceCollection(Set<SomeInterface> implementations) {
this.implementations = implementations;
}
}
#Bean
public SomeInterfaceCollection collect(Collection<SomeInterface> beans,
Collection<Collection<SomeInterface>> beanCollections) {
final HashSet<SomeInterface> merged = ...
return new SomeInterfaceCollection(merged);
}
Consider following configuration with two beans of the same type created:
#Configuration
#ComponentScan(basePackageClasses = TwoStrings.class)
public class Config {
#Bean
public String one() {
return "one";
}
#Bean
public String two() {
return "two";
}
}
Another bean that depends on the two beans above is created by component scan:
#Component
public class TwoStrings {
public final String a;
public final String b;
#Autowired
public TwoStrings(String one, String two) {
this.a = one;
this.b = two;
}
}
The names of local variables/parameters are lost during compilation and are not available at runtime:
However, Spring somehow autowires two String beans correctly. The example test below
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Config.class )
public class Example {
#Autowired
private TwoStrings twoStrings;
#Test
public void test() {
System.out.println(twoStrings.a);
System.out.println(twoStrings.b);
}
}
prints
one
two
Given the name of constructor parameters are lost, I would expect Spring to throw the exception saying that there is more than 1 bean of type String, however Spring somehow autowires beans using parameters names.
The question is how Spring knows the names of constructor parameters?
Spring is using debug information in this case - it will fail if you compile your code without debug information, i.e. with -g:none flag of javac.
Extract from documentation:
Keep in mind that to make this work out of the box your code must be
compiled with the debug flag enabled so that Spring can look up the
parameter name from the constructor. If you can’t compile your code
with debug flag (or don’t want to) you can use #ConstructorProperties
JDK annotation to explicitly name your constructor arguments.
Java 8 has introduced a way for reading this information using Reflection API but the classes has to be compiled with a -parameters flag of javac
Hej,
I want to use the #Validated(group=Foo.class) annotation to validate an argument before executing a method like following:
public void doFoo(Foo #Validated(groups=Foo.class) foo){}
When i put this method in the Controller of my Spring application, the #Validated is executed and throws an error when the Foo object is not valid. However if I put the same thing in a method in the Service layer of my application, the validation is not executed and the method just runs even when the Foo object isn't valid.
Can't you use the #Validated annotation in the service layer ? Or do I have to do configure something extra to make it work ?
Update:
I have added the following two beans to my service.xml:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
and replaced the #Validate with #Null like so:
public void doFoo(Foo #Null(groups=Foo.class) foo){}
I know it is a pretty silly annotation to do but I wanted to check that if I call the method now and passing null it would throw an violation exception which it does. So why does it execute the #Null annotation and not the #Validate annotation ? I know one is from javax.validation and the other is from Spring but I do not think that has anything to do with it ?
In the eyes of a Spring MVC stack, there is no such thing as a service layer. The reason it works for #Controller class handler methods is that Spring uses a special HandlerMethodArgumentResolver called ModelAttributeMethodProcessor which performs validation before resolving the argument to use in your handler method.
The service layer, as we call it, is just a plain bean with no additional behavior added to it from the MVC (DispatcherServlet) stack. As such you cannot expect any validation from Spring. You need to roll your own, probably with AOP.
With MethodValidationPostProcessor, take a look at the javadoc
Applicable methods have JSR-303 constraint annotations on their
parameters and/or on their return value (in the latter case specified
at the method level, typically as inline annotation).
Validation groups can be specified through Spring's Validated
annotation at the type level of the containing target class, applying
to all public service methods of that class. By default, JSR-303 will
validate against its default group only.
The #Validated annotation is only used to specify a validation group, it doesn't itself force any validation. You need to use one of the javax.validation annotations like #Null or #Valid. Remember that you can use as many annotations as you would like on a method parameter.
As a side note on Spring Validation for methods:
Since Spring uses interceptors in its approach, the validation itself is only performed when you're talking to a Bean's method:
When talking to an instance of this bean through the Spring or JSR-303 Validator interfaces, you'll be talking to the default Validator of the underlying ValidatorFactory. This is very convenient in that you don't have to perform yet another call on the factory, assuming that you will almost always use the default Validator anyway.
This is important because if you're trying to implement a validation in such a way for method calls within the class, it won't work. E.g.:
#Autowired
WannaValidate service;
//...
service.callMeOutside(new Form);
#Service
public class WannaValidate {
/* Spring Validation will work fine when executed from outside, as above */
#Validated
public void callMeOutside(#Valid Form form) {
AnotherForm anotherForm = new AnotherForm(form);
callMeInside(anotherForm);
}
/* Spring Validation won't work for AnotherForm if executed from inner method */
#Validated
public void callMeInside(#Valid AnotherForm form) {
// stuff
}
}
Hope someone finds this helpful. Tested with Spring 4.3, so things might be different for other versions.
#pgiecek You don't need to create a new Annotation. You can use:
#Validated
public class MyClass {
#Validated({Group1.class})
public myMethod1(#Valid Foo foo) { ... }
#Validated({Group2.class})
public myMethod2(#Valid Foo foo) { ... }
...
}
Be careful with rubensa's approach.
This only works when you declare #Valid as the only annotation. When you combine it with other annotations like #NotNull everything except the #Valid will be ignored.
The following will not work and the #NotNull will be ignored:
#Validated
public class MyClass {
#Validated(Group1.class)
public void myMethod1(#NotNull #Valid Foo foo) { ... }
#Validated(Group2.class)
public void myMethod2(#NotNull #Valid Foo foo) { ... }
}
In combination with other annotations you need to declare the javax.validation.groups.Default Group as well, like this:
#Validated
public class MyClass {
#Validated({ Default.class, Group1.class })
public void myMethod1(#NotNull #Valid Foo foo) { ... }
#Validated({ Default.class, Group2.class })
public void myMethod2(#NotNull #Valid Foo foo) { ... }
}
As stated above to specify validation groups is possible only through #Validated annotation at class level. However, it is not very convenient since sometimes you have a class containing several methods with the same entity as a parameter but each of which requiring different subset of properties to validate. It was also my case and below you can find several steps to take to solve it.
1) Implement custom annotation that enables to specify validation groups at method level in addition to groups specified through #Validated at class level.
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ValidatedGroups {
Class<?>[] value() default {};
}
2) Extend MethodValidationInterceptor and override determineValidationGroups method as follows.
#Override
protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
final Class<?>[] classLevelGroups = super.determineValidationGroups(invocation);
final ValidatedGroups validatedGroups = AnnotationUtils.findAnnotation(
invocation.getMethod(), ValidatedGroups.class);
final Class<?>[] methodLevelGroups = validatedGroups != null ? validatedGroups.value() : new Class<?>[0];
if (methodLevelGroups.length == 0) {
return classLevelGroups;
}
final int newLength = classLevelGroups.length + methodLevelGroups.length;
final Class<?>[] mergedGroups = Arrays.copyOf(classLevelGroups, newLength);
System.arraycopy(methodLevelGroups, 0, mergedGroups, classLevelGroups.length, methodLevelGroups.length);
return mergedGroups;
}
3) Implement your own MethodValidationPostProcessor (just copy the Spring one) and in the method afterPropertiesSet use validation interceptor implemented in step 2.
#Override
public void afterPropertiesSet() throws Exception {
Pointcut pointcut = new AnnotationMatchingPointcut(Validated.class, true);
Advice advice = (this.validator != null ? new ValidatedGroupsAwareMethodValidationInterceptor(this.validator) :
new ValidatedGroupsAwareMethodValidationInterceptor());
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
4) Register your validation post processor instead of Spring one.
<bean class="my.package.ValidatedGroupsAwareMethodValidationPostProcessor"/>
That's it. Now you can use it as follows.
#Validated(groups = Group1.class)
public class MyClass {
#ValidatedGroups(Group2.class)
public myMethod1(Foo foo) { ... }
public myMethod2(Foo foo) { ... }
...
}
Hopefully my question is fairly self-explanatory - I will illustrate it with some example code.
#Component
#PropertySource("classpath:/my-properties.properties")
public class SomeProperties {
#Autowired
Environment env;
// private methods
public boolean isEnabled(Foo foo) {
// call private methods, call env.getProperty, return value
}
}
#Component // it makes no difference whether it's there or not
public class MyCondition implements Condition {
#Autowired // doesn't make a difference
private SomeProperties someProperties;
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// ...
boolean b = someProperties.isEnabled(foo); // get NPE on this line
// ...return
}
}
#Component
#Conditional(MyCondition.class)
public class Bar {
// stuff
}
(Here I'm using Spring Boot to configure Spring. Although I doubt it makes any difference - as #Component beans are definitely accessible post-bootstrap, so it doesn't seem to be a problem in the way Spring is configured.)
The problem is that I'm getting a NullPointerException on the indicated line, because someProperties is null. This is presumably because at the time that the condition is run, the autowiring/instantiation phase of Spring bootstrap has not happened yet.
Is there any way to access Spring Properties in this way - like force Spring to load a bean before it normally would? Or is the only way to use standard Java / Apache Commons properties code as opposed to Spring?
Here is my client:
class Client {
#Inject(optional=true) Service service;
}
Sometimes that Service isn't needed, and we know that information when the JVM starts (i.e before the binder is run).
How do I make the binding optional? If I don't specify a binding at all it tries to new the Service (and fails because there is no zero-argument constructor: "Error while injecting at package.Client.service(Service.java:40): Could not find a suitable constructor in package.Service."), and I can't do:
binder.bind(Service.class).toInstance(null);
because Guice seems to disallow nulls. Any ideas?
Are you using Guice 2.0? I've tried this both with Service being an interface (service field is always null) and with it being a class (null if it can't create a new instance with a JIT binding, an instance if it can). Both seem like what you'd expect.
In neither case did I use a binding like bind(Service.class).toInstance(null). If you do this, then you need to make sure that all injection points for Service specify that they allow it to be null. This can be done by annotating the injection point with any annotation called #Nullable (you can make your own or use an existing one):
class Client {
#Inject #Nullable Service service;
}
If you want to make the existence of a binding optional you can use #Inject(optional = true) for field and method injections. For contructor and other parameter type injections, you must use a helper class, for example:
class Foo {
public String hello(Helper helper) {
return Helper.string;
}
private static final class Helper {
#Inject(optional = true) public String string = "";
}
}
Note that the above doesn't allow null to be injected, so Foo#hello will never return null. If you do want to allow null, simply add the #Nullable annotation. Keep in mind that code like the following will fail unless you've provided a binding (to null, if nothing else) for String:
class Bar {
#Inject #Nullable public String hello(#Nullable String string) {
return string;
}
}
I couldn't reproduce this behavior. When I run this example code, the injector creates and the optional injection is left unsatisfied. The program prints null:
public class OptionalInjections {
static class Service {
public Service(Void v) {} // not injectable
}
static class Client {
#Inject(optional = true) Service service;
}
public static void main(String[] args) {
Injector i = Guice.createInjector();
Client client = i.getInstance(Client.class);
System.out.println(client.service);
}
}
Could you reproduce this in a JUnit test case and submit it to the Guice issue tracker?