I use spring boot 2.
I need to validate many condition, instead of creating many if, I create a class for every condition who implements isValid method.
public interface Rulecondition<T>{
boolean isValid(){
}
public class ClientGroup implements Rulecondition<Billing>{
private Billing billing;
public ClientGroup(Billing billing){
this.billing=billing;
}
#Override
public boolean isValid(){
return true; //only for example...
}
}
I create a class who use a stream of all condition and verify if everything is valid
#Component
public class ConditionRuleEngine{
private List<Rulecondition> rules = new ArrayList<>();
public ConditionRuleEngine(){
}
public void addRule(Rulecondition rule){
rules.add(rule);
}
public boolean conditionApply(){
retunr rules.stream().allMatch(r->.isValid()));
}
}
In a service class, I autowired ConditionRuleEngine
#Service
public class OperationService(){
private final ConditionRuleEngine conditionRuleEngine;
#Autowired
public OperationService OperationService(final ConditionRuleEngine conditionRuleEngine){
this.conditionRuleEngine=conditionRuleEngine;
}
public void execute(Billing billing){
//need to add condition
conditionRuleEngine.run();
}
}
Is there any better way to add condition then creating a new?
ClientGroup cg = new ClientGroup(billing);
conditionRuleEngine.add(cg);
Yes, but there is one change that would help your situation: Removing the generic type argument, T, from the Rulecondition interface. Since Rulecondition only contains a isValid method that does not depend on the generic parameter, T, that parameter can be safely removed:
public interface RuleCondition {
boolean isValid();
}
The reason for this removal is that it simplifies the Spring-based solution to your problem. Without that generic parameter, you can now mark each of your RuleCondition implementations with #Component (making them eligible for dependency injection) and autowire a List<RuleCondition> into your ConditionRuleEngine class:
#Component
public class ConditionRuleEngine {
#Autowired
private List<RuleCondition> rules;
// ...other implementation details...
}
Spring will inject all of the components that implement RuleCondition into the rules field.
While removing the generic parameter, T, simplifies the solution, it is not required.
If you do need to maintain the generic parameter, T, then you can autowire the RuleCondition for each generic parameter by specifying the parameter in the List type:
public interface RuleCondition<T> {
boolean isValid(T value);
}
#Component
public class ClientGroup implements RuleCondition<Billing> {
#Override
public boolean isValid(Billing value) {
// ...
}
}
#Component
public class ConditionRuleEngine {
#Autowired
private List<RuleCondition<Billing>> billingRules;
#Autowired
private List<RulecCndition<Other>> otherRules;
// ...other implementation details...
}
Spring will inject all RuleCondition objects that match the generic parameter. For example, billingRules will contain a List of all RuleCondition<Billing> (and will not include any RulecCndition<Other> objects.
Related
I have a configuration providing a single bean and a configuration providing a list of beans. All these beans have the same type.
When I start up an application context with these configurations, I see that an autowired list of the bean type only contains the single bean. I want it to include all beans of that type. I use Spring 5.2.0.
I have boiled it down to one configuration: if I provide a single bean and a list of beans, only the single bean will be used.
This is reproduced in the following test. It fails, because the list only contains "A" and "D" (which shows it did not autowire the list of beans):
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = { TestConfiguration.class })
class AutowiringListsTest {
#Autowired
private List<TestBean> testBeanList;
#Test
void testThatBothConfigurationsContributeToBeanList() {
final List<String> idList = testBeanList.stream().map(TestBean::getId).sorted().collect(Collectors.toList());
assertThat(idList, hasItems("A", "B", "C", "D"));
}
#Configuration
public static class TestConfiguration {
#Bean
public TestBean someBean() {
return new TestBean("A");
}
#Bean
public List<TestBean> someMoreBeans() {
return Arrays.asList(new TestBean("B"), new TestBean("C"));
}
#Bean
public TestBean anotherBean() {
return new TestBean("D");
}
}
public static class TestBean {
private final String id;
public TestBean(final String id) {
this.id = id;
}
private String getId() {
return id;
}
}
}
I want to get this to run so that multiple modules can provide beans of a certain type.
Some modules want to provide multiple beans and their number depends on a property.
Some modules will always provide one bean.
The module using the beans (autowiring them as list) should autowire all beans.
How can I get this to run? In what scenario does Spring's behavior make sense?
I can work around the issue by introducing a TestBeanFactory. Each configuration that wants to contribute to the list of TestBeans instead provides a factory.
#Configuration
public static class TestConfiguration {
/** Implemented once in the configuration that defines <code>TestBean</code>. */
#Bean
public List<TestBean> testBeansFromFactory(Collection<TestBeanFactory> factories) {
return factories.stream().map(TestBeanFactory::createTestBeans).flatMap(Collection::stream)
.collect(toList());
}
// Further methods can be defined in various configurations that want to add to the list of TestBeans.
#Bean
public TestBeanFactory someBean() {
return () -> Arrays.asList(new TestBean("A"));
}
#Bean
public TestBeanFactory someMoreBeans() {
return () -> Arrays.asList(new TestBean("B"), new TestBean("C"));
}
#Bean
public TestBeanFactory anotherBean() {
return () -> Arrays.asList(new TestBean("D"));
}
}
public static class TestBean { ... }
public static interface TestBeanFactory {
public Collection<TestBean> createTestBeans();
}
That works and is only slightly more code.
M.Deinum makes a point in the comments for Spring's behavior being consistent:
As you defined a bean of type List it will be used. Autowiring is based on type, it will try to detect the bean of the certain type. The collection (and map as well) is a special one looking up all dependencies of the given type.
In my application, I have two classes having the same name, but of course in different packages.
Both classes need to be injected in the application; Unfortunately, I get the following error message:
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'myFeature' for bean class [org.pmesmeur.springboot.training.service.feature2.MyFeature] conflicts with existing, non-compatible bean definition of same name and class [org.pmesmeur.springboot.training.service.feature1.MyFeature]
My issue can be reproduced by the following sample:
#Component
#EnableConfigurationProperties(ServiceProperties.class)
public class MyService implements IService {
private final ServiceProperties serviceProperties;
private final IProvider provider;
private final org.pmesmeur.springboot.training.service.feature1.IMyFeature f1;
private final org.pmesmeur.springboot.training.service.feature2.IMyFeature f2;
#Autowired
public MyService(ServiceProperties serviceProperties,
IProvider provider,
org.pmesmeur.springboot.training.service.feature1.IMyFeature f1,
org.pmesmeur.springboot.training.service.feature2.IMyFeature f2) {
this.serviceProperties = serviceProperties;
this.provider = provider;
this.f1 = f1;
this.f2 = f2;
}
...
package org.pmesmeur.springboot.training.service.feature1;
public interface IMyFeature {
void print();
}
package org.pmesmeur.springboot.training.service.feature1;
import org.springframework.stereotype.Component;
#Component
public class MyFeature implements IMyFeature {
#Override
public void print() {
System.out.print("HelloWorld");
}
}
package org.pmesmeur.springboot.training.service.feature2;
public interface IMyFeature {
void print();
}
package org.pmesmeur.springboot.training.service.feature2;
import org.springframework.stereotype.Component;
#Component
public class MyFeature implements IMyFeature {
#Override
public void print() {
System.out.print("FooBar");
}
}
If I use different names for my classes MyFeature, my problem disappears!!!
I am used to work with Guice and this framework does not have this kind of problem/limitation
It seems that the spring dependencies injection framework uses only
the class-name instead of package-name + class-name in order to
select its classes.
In "real-life" I have this problem with a far-bigger project and I would strongly prefer not to have to rename my classes: can anyone help me?
One last point, I would prefer to avoid "tricks" such as using
#Qualifier(value = "ABC") when injecting my classes: in my sample,
there should be no ambiguity for finding the correct instance of
MyFeature as they do not implement the same interface
Simply re-implementing BeanNameGenerator adds a new problem for beans declared/instantiated by names
#Component("HelloWorld")
class MyComponent implements IComponent {
...
}
#Qualifier(value = "HelloWorld") IComponent component
I solved this issue by extending AnnotationBeanNameGenerator and redefining method buildDefaultBeanName()
static class BeanNameGeneratorIncludingPackageName extends AnnotationBeanNameGenerator {
public BeanNameGeneratorIncludingPackageName() {
}
#Override
public String buildDefaultBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) {
return beanDefinition.getBeanClassName();
}
}
You can assigna a value for each component e.g. #Component(value="someBean") and then inject it with #Qualifier e.g.
#Autowired
public SomeService(#Qualifier("someBean") Some s){
//....
}
Spring provides autowire by type and name. Your classname are same. By default spring considers only className not package. But you can override this behaviour by defining custom implementation of BeanNameGenerator interface in which you can generate name using both package and name. I am not providing code solution because i think you should explore more on this.
You can do something like this;
in package a
public class MyFeature implements IMyFeature {
#Override
public void print() {
System.out.print("FooBar");
}
}
in package b
public class MyFeature implements IMyFeature {
#Override
public void print() {
System.out.print("HelloWorld");
}
}
and in some config class;
#Configuration
public class Configuration {
#Bean
public a.MyFeature f1() {
return new a.MyFeature();
}
#Bean
public b.MyFeature f2() {
return new b.MyFeature();
}
}
Then you can autowire them with names f1 and f2, that are the names of their respective bean constructor methods.
You can do the similar thing with #Component("f1") &
#Component("f2")
Even though different interfaces are implemented and are in different packages, identical bean name causes this trouble, and you have to utilize some sort of custom naming to distinguish. Utilizing some custom Spring logic would be way too ugly compared to what you'd do with above solutions.
We have a web service that one of its parameters is called origin and this origin is always validated against a code in the database.
For each one of our services I have to validate this code. This code does not change so I want to keep it in a constant, but I still have to validate it to prevent clients from sending a wrong code.
Basically what I want is this:
#Service
public class Service {
#Autowired
private LogBS logBS;
// I know this cannot be used in a static context.
public static final Long CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
public void validateOriginCode(final Long origin) {
if (!origin.equals(CODE)) {
throw new ServiceException("Wrong origin code!");
}
}
}
I know something similar can be done with Spring caching, but is it possible to do it with a constant?
I would rather go with this:
#Service
public class CodeValidatorService {
private LogBS logBS;
private Long CODE;
#Autowired
public CodeValidatorService(LogBS logBS){
this.logBS = logBS;
CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
if (CODE == null){
throw new ServiceException("Code cannot be read from DB!");
}
}
public void validateOriginCode(final Long origin) {
if (!origin.equals(CODE)) {
throw new ServiceException("Wrong origin code!");
}
}
}
Just as a code review, I prefer injecting dependencies in the constructor rather than using #Autowired in the field directly, it makes the service testable. You could also try to read the code in a #PostConstruct method, but I think it's better to do it in the constructor so you always have the service in a ready-to-go state.
For using it in the rest of your services, inject the CodeValidatorService instance on them:
#Service
public class OtherService {
private CodeValidatorService codeValidatorService;
#Autowired
public OtherService(CodeValidatorService codeValidatorService){
this.codeValidatorService = codeValidatorService;
}
public void performAction(final Long origin) {
codeValidatorService.validateOriginCode(origin);
//do the rest of your logic here
}
}
See also:
Spring Beans and dependency injection
Setter injection versus constructor injection
You can have a constantsProvider class
#Component
public class ConstantsProvider {
#Autowired
private LogBS logBS;
private String CODE;
#PostConstruct
public void init() {
CODE = this.logBS.retrieveLogWebServiceCode("webServiceName");
}
public String getCode() {
return CODE;
}
}
Add this snippet of code to Service class
#Autowired
private ConstantsProvider constantsProvider;
You can use constantsProvider.getCode() in your services. This way CODE is going to be immutable and not defined in a static context.
Note: If you have more constants similar to this, there is a better way to define the ConstantsProvider class. If there is only one, I would stick to the above implementation.
Edit 1:
If you need it in all the service classes, make the constantsProvider a spring bean and initialize the CODE there itself. Updated the answer
I need to inject a spring service class in the generated mapper implementation, so that I can use it via
#Mapping(target="x", expression="java(myservice.findById(id))")"
Is this applicable in Mapstruct-1.0?
As commented by brettanomyces, the service won't be injected if it is not used in mapping operations other than expressions.
The only way I found to this is :
Transform my mapper interface into an abstract class
Inject the service in the abstract class
Make it protected so the "implementation" of the abstract class has access
I'm using CDI but it should be the samel with Spring :
#Mapper(
unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
componentModel = "spring",
uses = {
// My other mappers...
})
public abstract class MyMapper {
#Autowired
protected MyService myService;
#Mappings({
#Mapping(target="x", expression="java(myservice.findById(obj.getId())))")
})
public abstract Dto myMappingMethod(Object obj);
}
It should be possible if you declare Spring as the component model and add a reference to the type of myservice:
#Mapper(componentModel="spring", uses=MyService.class)
public interface MyMapper { ... }
That mechanism is meant for providing access to other mapping methods to be called by generated code, but you should be able to use them in the expression that way, too. Just make sure you use the correct name of the generated field with the service reference.
Since 1.2 this can be solved with a combination of #AfterMapping and #Context.. Like this:
#Mapper(componentModel="spring")
public interface MyMapper {
#Mapping(target="x",ignore = true)
// other mappings
Target map( Source source, #Context MyService service);
#AfterMapping
default void map( #MappingTarget Target.X target, Source.ID source, #Context MyService service) {
target.set( service.findById( source.getId() ) );
}
}
The service can be passed as context.
A nicer solution would be to use an #Context class which wrap MyService in stead of passing MyService directly. An #AfterMapping method can be implemented on this "context" class: void map( #MappingTarget Target.X target, Source.ID source ) keeping the mapping logic clear of lookup logic. Checkout this example in the MapStruct example repository.
What's worth to add in addition to the answers above is that there is more clean way to use spring service in mapstruct mapper, that fits more into "separation of concerns" design concept, called "qualifier". Easy re-usability in other mappers as a bonus.
For sake of simplicity I prefer named qualifier as noted here http://mapstruct.org/documentation/stable/reference/html/#selection-based-on-qualifiers
Example would be:
import org.mapstruct.Mapper;
import org.mapstruct.Named;
import org.springframework.stereotype.Component;
#Component
#Mapper
public class EventTimeQualifier {
private EventTimeFactory eventTimeFactory; // ---> this is the service you want yo use
public EventTimeQualifier(EventTimeFactory eventTimeFactory) {
this.eventTimeFactory = eventTimeFactory;
}
#Named("stringToEventTime")
public EventTime stringToEventTime(String time) {
return eventTimeFactory.fromString(time);
}
}
This is how you use it in your mapper:
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
#Mapper(componentModel = "spring", uses = EventTimeQualifier.class)
public interface EventMapper {
#Mapping(source = "checkpointTime", target = "eventTime", qualifiedByName = "stringToEventTime")
Event map(EventDTO eventDTO);
}
I am using Mapstruct 1.3.1 and I have found this problem is easy to solve using a decorator.
Example:
#Mapper(unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
componentModel = "spring")
#DecoratedWith(FooMapperDecorator.class)
public interface FooMapper {
FooDTO map(Foo foo);
}
public abstract class FooMapperDecorator implements FooMapper{
#Autowired
#Qualifier("delegate")
private FooMapper delegate;
#Autowired
private MyBean myBean;
#Override
public FooDTO map(Foo foo) {
FooDTO fooDTO = delegate.map(foo);
fooDTO.setBar(myBean.getBar(foo.getBarId());
return fooDTO;
}
}
Mapstruct will generate 2 classes and mark the FooMapper that extends FooMapperDecorator as the #Primary bean.
I can't use componentModel="spring" because I work in a large project that doesn't use it. Many mappers includes my mapper with Mappers.getMapper(FamilyBasePersonMapper.class), this instance is not the Spring bean and the #Autowired field in my mapper is null.
I can't modifiy all mappers that use my mapper. And I can't use particular constructor with the injections or the Spring's #Autowired dependency injection.
The solution that I found: Using a Spring bean instance without using Spring directly:
Here is the Spring Component that regist itself first instance (the Spring instance):
#Component
#Mapper
public class PermamentAddressMapper {
#Autowired
private TypeAddressRepository typeRepository;
#Autowired
private PersonAddressRepository personAddressRepository;
static protected PermamentAddressMapper FIRST_INSTANCE;
public PermamentAddressMapper() {
if(FIRST_INSTANCE == null) {
FIRST_INSTANCE = this;
}
}
public static PermamentAddressMapper getFirstInstance(){
return FIRST_INSTANCE;
}
public static AddressDTO idPersonToPermamentAddress(Integer idPerson) {
//...
}
//...
}
Here is the Mapper that use the Spring Bean accross getFirstInstance method:
#Mapper(uses = { NationalityMapper.class, CountryMapper.class, DocumentTypeMapper.class })
public interface FamilyBasePersonMapper {
static FamilyBasePersonMapper INSTANCE = Mappers.getMapper(FamilyBasePersonMapper.class);
#Named("idPersonToPermamentAddress")
default AddressDTO idPersonToPermamentAddress(Integer idPerson) {
return PermamentAddressMapper.getFirstInstance()
.idPersonToPermamentAddress(idPersona);
}
#Mapping(
source = "idPerson",
target="permamentAddres",
qualifiedByName="idPersonToPermamentAddress" )
#Mapping(
source = "idPerson",
target = "idPerson")
FamilyDTO toFamily(PersonBase person);
//...
Maybe this is not the best solution. But it has helped to decrement the impact of changes in the final resolution.
I'm having an issue when trying to mock a property of a service from within a Junit test:
#ContextConfiguration("classpath:application-config.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class FooServiceTests {
#Autowired
private FooServiceImpl fooService;
#Test
public void testFoo() {
String str = fooService.foo();
assertEquals("Var", str);
}
#Before
public void mockFooDao() throws Exception {
FooDao mockFooDao = Mockito.mock(FooDao.class);
Mockito.when(mockFooDao.foo()).thenReturn("Var");
ReflectionTestUtils.setField(fooService, "fooDao", mockFooDao);
}
}
Mocking fooDao has no effect since the the result is not the expected. Here is the code of both the service and the dao:
#Service("fooService")
public class FooServiceImpl implements FooService {
#Autowired
protected FooDao fooDao;
#Override
public String foo() {
return fooDao.foo();
}
}
#Repository
public class FooDaoImpl implements FooDao {
#Override
public String foo() {
return "foo";
}
}
As we can see the actual service is meant to return "foo", but the test mocks the dao so the service returns "var". I know it's a CGLIB proxy related thing but I can't figure out how to make it work without using a setter for the fooDao property. Any help would be appreciated.
Regards and thanks in advance.
Short answer
You have to unwrap the proxy and set the field on the target object:
ReflectionTestUtils.setField(unwrapFooService(), "fooDao", mockFooDao);
The unwrapFooService() can be defined as follows:
private FooServiceImpl unwrapFooService() {
if(AopUtils.isAopProxy(fooService) && fooService instanceof Advised) {
Object target = ((Advised) fooService).getTargetSource().getTarget();
return (FooServiceImpl)target;
}
return null;
}
...long one
The problem is quite complex, but solvable. As you have guessed this is a side-effect of CGLIB proxies being used. In principle, Spring creates a subclass of your FooServiceImpl named similar to FooServiceImpl$EnhancerByCGLIB. This subclass contains a reference to the original FooServiceImpl as well as... all the fields FooServiceImpl has (which is understandable - this is a subclass).
So there are actually two variables: FooServiceImpl$EnhancerByCGLIB.fooDao and FooServiceImpl.fooDao. You are assigning a mock to the former but your service uses the latter... I wrote about this pitfalls some time ago.