Skip to content

Commit 0784186

Browse files
committed
Prefer constructor injection over field injection.
Fixes #13
1 parent 74cd7d3 commit 0784186

File tree

4 files changed

+116
-104
lines changed

4 files changed

+116
-104
lines changed

docs/src/docs/asciidoc/chapter-3-mapper-as-converter.asciidoc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,21 @@ The generated Adapter class will look like this:
5353
----
5454
@Component
5555
public class ConversionServiceAdapter {
56-
@Autowired
57-
private ConversionService conversionService;
56+
private final ConversionService conversionService;
57+
58+
public ConversionServiceAdapter(final ConversionService conversionService) {
59+
this.conversionService = conversionService;
60+
}
5861
5962
public CarDto mapCarToCarDto(final Car source) {
6063
return conversionService.convert(source, CarDto.class);
6164
}
6265
63-
public SeatConfigurationDto mapSeatConfigurationToSeatConfigurationDto(final SeatConfiguration source) {
66+
public SeatConfigurationDto mapSeatConfigurationToSeatConfigurationDto(
67+
final SeatConfiguration source) {
6468
return conversionService.convert(source, SeatConfigurationDto.class);
6569
}
66-
}
67-
----
70+
}----
6871
====
6972

7073
Since this class' methods match the signature that MapStruct expects, we can now add it to the CarMapper:
@@ -96,6 +99,8 @@ import org.mapstruct.extensions.spring.example.adapter.MyAdapter;
9699
public interface MapperSpringConfig {
97100
}
98101
----
102+
103+
Note: If you do _not_ specify the `conversionServiceAdapterPackage` element, the generated Adapter class will reside in the same package as the annotated Config.
99104
====
100105
[[customConversionService]]
101106
=== Specifying The Conversion Service Bean Name
Lines changed: 85 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package org.mapstruct.extensions.spring.converter;
22

3-
import static java.util.stream.Collectors.toList;
4-
import static javax.lang.model.element.Modifier.*;
5-
63
import com.squareup.javapoet.*;
74
import org.apache.commons.lang3.StringUtils;
85

@@ -12,93 +9,101 @@
129
import java.time.Clock;
1310
import java.time.ZonedDateTime;
1411
import java.time.format.DateTimeFormatter;
15-
import java.util.ArrayList;
16-
import java.util.List;
12+
13+
import static java.util.stream.Collectors.toList;
14+
import static javax.lang.model.element.Modifier.*;
1715

1816
public class ConversionServiceAdapterGenerator {
19-
private final Clock clock;
17+
private final Clock clock;
18+
19+
public ConversionServiceAdapterGenerator(final Clock clock) {
20+
this.clock = clock;
21+
}
2022

21-
public ConversionServiceAdapterGenerator(final Clock clock) {
22-
this.clock = clock;
23-
}
23+
public void writeConversionServiceAdapter(
24+
ConversionServiceAdapterDescriptor descriptor, Writer out) {
25+
try {
26+
JavaFile.builder(
27+
descriptor.getAdapterClassName().packageName(),
28+
createConversionServiceTypeSpec(descriptor))
29+
.build()
30+
.writeTo(out);
31+
} catch (IOException e) {
32+
throw new UncheckedIOException(e);
33+
}
34+
}
35+
36+
private TypeSpec createConversionServiceTypeSpec(
37+
final ConversionServiceAdapterDescriptor descriptor) {
38+
final FieldSpec conversionServiceFieldSpec = buildConversionServiceFieldSpec();
39+
return TypeSpec.classBuilder(descriptor.getAdapterClassName())
40+
.addModifiers(PUBLIC)
41+
.addAnnotation(buildGeneratedAnnotationSpec())
42+
.addAnnotation(ClassName.get("org.springframework.stereotype", "Component"))
43+
.addField(conversionServiceFieldSpec)
44+
.addMethod(buildConstructorSpec(descriptor, conversionServiceFieldSpec))
45+
.addMethods(buildMappingMethods(descriptor, conversionServiceFieldSpec))
46+
.build();
47+
}
2448

25-
public void writeConversionServiceAdapter(
26-
ConversionServiceAdapterDescriptor descriptor, Writer out) {
27-
try {
28-
JavaFile.builder(
29-
descriptor.getAdapterClassName().packageName(),
30-
createConversionServiceTypeSpec(descriptor))
31-
.build()
32-
.writeTo(out);
33-
} catch (IOException e) {
34-
throw new UncheckedIOException(e);
49+
private static MethodSpec buildConstructorSpec(final ConversionServiceAdapterDescriptor descriptor, final FieldSpec conversionServiceFieldSpec) {
50+
final ParameterSpec constructorParameterSpec = buildConstructorParameterSpec(descriptor, conversionServiceFieldSpec);
51+
return MethodSpec.constructorBuilder().addModifiers(PUBLIC).addParameter(constructorParameterSpec).addStatement("this.$N = $N", conversionServiceFieldSpec, constructorParameterSpec).build();
3552
}
36-
}
3753

38-
private TypeSpec createConversionServiceTypeSpec(
39-
final ConversionServiceAdapterDescriptor descriptor) {
40-
final FieldSpec injectedConversionServiceFieldSpec = buildInjectedConversionServiceFieldSpec(descriptor);
41-
return TypeSpec.classBuilder(descriptor.getAdapterClassName())
42-
.addModifiers(PUBLIC)
43-
.addAnnotation(buildGeneratedAnnotationSpec())
44-
.addAnnotation(ClassName.get("org.springframework.stereotype", "Component"))
45-
.addField(injectedConversionServiceFieldSpec)
46-
.addMethods(buildMappingMethods(descriptor, injectedConversionServiceFieldSpec))
47-
.build();
48-
}
54+
private static ParameterSpec buildConstructorParameterSpec(final ConversionServiceAdapterDescriptor descriptor, final FieldSpec conversionServiceFieldSpec) {
55+
final ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(conversionServiceFieldSpec.type, conversionServiceFieldSpec.name, FINAL);
56+
if (StringUtils.isNotEmpty(descriptor.getConversionServiceBeanName())) {
57+
parameterBuilder.addAnnotation(buildQualifierANnotation(descriptor));
58+
}
59+
return parameterBuilder.build();
60+
}
4961

50-
private static Iterable<MethodSpec> buildMappingMethods(
51-
final ConversionServiceAdapterDescriptor descriptor,
52-
final FieldSpec injectedConversionServiceFieldSpec) {
53-
return descriptor.getFromToMappings().stream()
54-
.map(
55-
sourceTargetPair -> {
56-
final ParameterSpec sourceParameterSpec =
57-
buildSourceParameterSpec(sourceTargetPair.getLeft());
58-
return MethodSpec.methodBuilder(
59-
"map"
60-
+ sourceTargetPair.getLeft().simpleName()
61-
+ "To"
62-
+ sourceTargetPair.getRight().simpleName())
63-
.addParameter(sourceParameterSpec)
64-
.addModifiers(PUBLIC)
65-
.returns(sourceTargetPair.getRight())
66-
.addStatement(
67-
"return $N.convert($N, $T.class)",
68-
injectedConversionServiceFieldSpec,
69-
sourceParameterSpec,
70-
sourceTargetPair.getRight())
71-
.build();
72-
})
73-
.collect(toList());
74-
}
62+
private static AnnotationSpec buildQualifierANnotation(ConversionServiceAdapterDescriptor descriptor) {
63+
return AnnotationSpec
64+
.builder(ClassName.get("org.springframework.beans.factory.annotation", "Qualifier"))
65+
.addMember("value", "$S", descriptor.getConversionServiceBeanName())
66+
.build();
67+
}
7568

76-
private static ParameterSpec buildSourceParameterSpec(final TypeName sourceClassName) {
77-
return ParameterSpec.builder(sourceClassName, "source", FINAL).build();
78-
}
69+
private static Iterable<MethodSpec> buildMappingMethods(
70+
final ConversionServiceAdapterDescriptor descriptor,
71+
final FieldSpec injectedConversionServiceFieldSpec) {
72+
return descriptor.getFromToMappings().stream()
73+
.map(
74+
sourceTargetPair -> {
75+
final ParameterSpec sourceParameterSpec =
76+
buildSourceParameterSpec(sourceTargetPair.getLeft());
77+
return MethodSpec.methodBuilder(
78+
"map"
79+
+ sourceTargetPair.getLeft().simpleName()
80+
+ "To"
81+
+ sourceTargetPair.getRight().simpleName())
82+
.addParameter(sourceParameterSpec)
83+
.addModifiers(PUBLIC)
84+
.returns(sourceTargetPair.getRight())
85+
.addStatement(
86+
"return $N.convert($N, $T.class)",
87+
injectedConversionServiceFieldSpec,
88+
sourceParameterSpec,
89+
sourceTargetPair.getRight())
90+
.build();
91+
})
92+
.collect(toList());
93+
}
7994

80-
private static FieldSpec buildInjectedConversionServiceFieldSpec(ConversionServiceAdapterDescriptor descriptor) {
81-
List<AnnotationSpec> annotations = new ArrayList<>();
82-
annotations.add(AnnotationSpec.builder(ClassName
83-
.get("org.springframework.beans.factory.annotation", "Autowired"))
84-
.build());
85-
if (StringUtils.isNotEmpty(descriptor.getConversionServiceBeanName())) {
86-
annotations.add(AnnotationSpec
87-
.builder(ClassName.get("org.springframework.beans.factory.annotation", "Qualifier"))
88-
.addMember("value", "$S", descriptor.getConversionServiceBeanName())
89-
.build());
95+
private static ParameterSpec buildSourceParameterSpec(final TypeName sourceClassName) {
96+
return ParameterSpec.builder(sourceClassName, "source", FINAL).build();
9097
}
9198

92-
return FieldSpec.builder(ClassName.get("org.springframework.core.convert","ConversionService"),
93-
"conversionService", PRIVATE)
94-
.addAnnotations(annotations)
95-
.build();
96-
}
99+
private static FieldSpec buildConversionServiceFieldSpec() {
100+
return FieldSpec.builder(ClassName.get("org.springframework.core.convert", "ConversionService"), "conversionService", PRIVATE, FINAL).build();
101+
}
97102

98-
private AnnotationSpec buildGeneratedAnnotationSpec() {
99-
return AnnotationSpec.builder(ClassName.get("javax.annotation", "Generated"))
100-
.addMember("value", "$S", ConversionServiceAdapterGenerator.class.getName())
101-
.addMember("date", "$S", DateTimeFormatter.ISO_INSTANT.format(ZonedDateTime.now(clock)))
102-
.build();
103-
}
103+
private AnnotationSpec buildGeneratedAnnotationSpec() {
104+
return AnnotationSpec.builder(ClassName.get("javax.annotation", "Generated"))
105+
.addMember("value", "$S", ConversionServiceAdapterGenerator.class.getName())
106+
.addMember("date", "$S", DateTimeFormatter.ISO_INSTANT.format(ZonedDateTime.now(clock)))
107+
.build();
108+
}
104109
}
Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
package org.mapstruct.extensions.spring.converter;
22

33
import javax.annotation.Generated;
4-
import org.springframework.beans.factory.annotation.Autowired;
54
import org.springframework.core.convert.ConversionService;
65
import org.springframework.stereotype.Component;
76
import test.Car;
87
import test.CarDto;
98

109
@Generated(
11-
value = "org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator",
12-
date = "2020-03-29T15:21:34.236Z"
13-
)
10+
value = "org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator",
11+
date = "2020-03-29T15:21:34.236Z")
1412
@Component
1513
public class ConversionServiceAdapter {
16-
@Autowired
17-
private ConversionService conversionService;
14+
private final ConversionService conversionService;
1815

19-
public CarDto mapCarToCarDto(final Car source) {
20-
return conversionService.convert(source, CarDto.class);
21-
}
16+
public ConversionServiceAdapter(final ConversionService conversionService) {
17+
this.conversionService = conversionService;
18+
}
19+
20+
public CarDto mapCarToCarDto(final Car source) {
21+
return conversionService.convert(source, CarDto.class);
22+
}
2223
}
Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
package org.mapstruct.extensions.spring.converter;
22

33
import javax.annotation.Generated;
4-
import org.springframework.beans.factory.annotation.Autowired;
54
import org.springframework.beans.factory.annotation.Qualifier;
65
import org.springframework.core.convert.ConversionService;
76
import org.springframework.stereotype.Component;
87
import test.Car;
98
import test.CarDto;
109

1110
@Generated(
12-
value = "org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator",
13-
date = "2020-03-29T15:21:34.236Z"
14-
)
11+
value = "org.mapstruct.extensions.spring.converter.ConversionServiceAdapterGenerator",
12+
date = "2020-03-29T15:21:34.236Z")
1513
@Component
1614
public class ConversionServiceAdapter {
17-
@Autowired
18-
@Qualifier("myConversionService")
19-
private ConversionService conversionService;
15+
private final ConversionService conversionService;
2016

21-
public CarDto mapCarToCarDto(final Car source) {
22-
return conversionService.convert(source, CarDto.class);
23-
}
17+
public ConversionServiceAdapter(
18+
@Qualifier("myConversionService") final ConversionService conversionService) {
19+
this.conversionService = conversionService;
20+
}
21+
22+
public CarDto mapCarToCarDto(final Car source) {
23+
return conversionService.convert(source, CarDto.class);
24+
}
2425
}

0 commit comments

Comments
 (0)