目录
9.1、*装饰器映射
在进行映射时可以通过装饰器模式,为目标对象设置一些,不能由源对象直接生成或者源对象没有的属性。
spring注入策略的装饰器映射
使用@DecoratedWith注解制定装饰器。装饰器中使用@Qualifier("delegate")指定注入的bean。
@Mapper
@DecoratedWith(TestDecotator.class)
public interface TestMapper {
TestBO toTestBO(TestPO testPO);
}
public abstract class TestDecorator implements TestMapper {
@Autowired
@Qualifier("delegate")
private TestMapper testMapper;
@Override
public TestBO toTestBO(TestPO testPO) {
TestBO testBO = testMapper.toTestBO(testPO);
testBO.setName(testPO.getNameString() + testPO.getCreateTimeString());
return testBO;
}
}
@Component
@Primary
public class TestMapperImpl extends TestDecorator implements TestMapper {
}
@Component
@Qualifier("delegate")
public class TestMapperImpl_ implements TestMapper {
@Override
public TestBO toTestBO(TestPO testPO) {
if ( testPO == null ) {
return null;
}
TestBO testBO = new TestBO();
if ( testPO.getPriceString() != null ) {
testBO.setPrice( testPO.getPriceString().toString() );
}
testBO.setName( testPO.getNameString() );
testBO.setId( testPO.getId() );
return testBO;
}
}
TestPO testPO = new TestPO();
testPO.setId(20L);
testPO.setNameString("test");
testPO.setPriceString(new BigDecimal("12.3"));
testPO.setCreateTimeString(new Date(System.currentTimeMillis()));
System.out.println(testMapper.toTestBO(testPO));
结果
生成的TestMapperImpl被@Primary注解,使用注入是被选为第一候选,TestMapperImpl_被@Qualifier("delegate")注解,指明了bean名称,所以在TestDecorator指明名称注入。
9.2、映射前置/后置方法
可以在抽象mapper类、Mapper#uses引入的类或被@Context注解的上下文对象类中设置映射前后的回调方法。
@BeforeMapping注解的方法是前置方法,@AfterMapping注解的方法是后置方法。
如果前后置方法具有参数,返回的类型能被赋予映射方法的返回类型并且参数都可以用源/目标参数获取,才会调用前后置方法。
前后置方法中用@MappingTarget注解的参数获得的是目标实例,@TargetType获取的是目标类型,@Context可以获取上下文对象,其他的参数被赋予源参数。
然后前后置方法不是void的,返回的值不为null的情况下将作为映射方法的返回值。
同样,若前后置方法能匹配到多个,将会都调用,可以通过给前置方法添加@Named和映射方法上添加@BeanMapping#qualifiedByName制定要调用的前后置方法;一旦使用了@BeanMapping#qualifiedByName就必须指定自己选用调用的所有前后置方法的@Named名。
不带@MappingTarget注解参数的@BeforeMapping会在源参数进行null检查并且构造新的目标bean之前调用;
带@MappingTarget注解参数的@BeforeMapping会在构造新的目标bean之后调用;
@AfterMapping会在return前最后调用。
在使用建造者时,@BeforeMapping、@AfterMapping中要想获取目标对象,@MappingTarget注解的就必须是建造者。
@Mapper
@Named("baseMapper")
public class BaseMapper {
@BeforeMapping
@Named("before")
public void before(BasePO basePO, @MappingTarget BaseBO baseBO) {
System.out.println(basePO);
System.out.println(baseBO);
}
@AfterMapping
public <T> T after(@TargetType Class<T> clazz, @Context ThreadLocalContext threadLocalContext){
System.out.println(threadLocalContext);
T t = null;
try {
t = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return t;
}
}
@Data
public class ThreadLocalContext {
private ThreadLocal<Object> threadLocal;
public ThreadLocalContext() {
threadLocal = new ThreadLocal<>();
}
@BeforeMapping
@Named("before2")
public static void before2(BasePO basePO, @MappingTarget BaseBO baseBO) {
System.out.println(basePO);
System.out.println(baseBO);
}
}
@Mapper(uses = BaseMapper.class)
public interface TestMapper {
@BeanMapping(qualifiedByName = { "baseMapper"})
TestSixBO toTestBO(TestFourPO testPO, @Context ThreadLocalContext threadLocalContext);
}
@Component
public class TestMapperImpl implements TestMapper {
@Autowired
private BaseMapper baseMapper;
@Override
public TestSixBO toTestBO(TestFourPO testPO, ThreadLocalContext threadLocalContext) {
if ( testPO == null ) {
return null;
}
TestSixBO testSixBO = new TestSixBO();
baseMapper.before( testPO, testSixBO );
testSixBO.setId( testPO.getId() );
testSixBO.setName( testPO.getName() );
if ( testPO.getCreateTime() != null ) {
testSixBO.setCreateTime( new SimpleDateFormat().format( testPO.getCreateTime() ) );
}
TestSixBO target = baseMapper.after( TestSixBO.class, threadLocalContext );
if ( target != null ) {
return target;
}
return testSixBO;
}
}
9.3、循环嵌套对象调用前后置方法
若有类似于权限菜单的List嵌套结构的对象,mapstruct也可以自动映射。
@Data
public class TestBOS {
private TestBOS testS;
private List<TestBOS> list;
}
@Data
public class TestPOS {
private TestPOS testS;
private List<TestPOS> list;
}
@Mapper
public class BaseMapper {
@BeforeMapping
public <T> T before(Object source, @TargetType Class<T> targetType) {
T t;
try {
t = targetType.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
return null;
}
return t;
}
}
@Mapper(uses = BaseMapper.class)
public interface TestMapper {
TestBOS toBean(TestPOS testPOS);
}
@Component
public class TestMapperImpl implements TestMapper {
@Autowired
private BaseMapper baseMapper;
@Override
public TestBOS toBean(TestPOS testPOS) {
TestBOS target = baseMapper.before( testPOS, TestBOS.class );
if ( target != null ) {
return target;
}
if ( testPOS == null ) {
return null;
}
TestBOS testBOS = new TestBOS();
testBOS.setTestS( toBean( testPOS.getTestS() ) );
testBOS.setList( testPOSListToTestBOSList( testPOS.getList() ) );
return testBOS;
}
protected List<TestBOS> testPOSListToTestBOSList(List<TestPOS> list) {
List<TestBOS> target = baseMapper.before( list, List.class );
if ( target != null ) {
return target;
}
if ( list == null ) {
return null;
}
List<TestBOS> list1 = new ArrayList<TestBOS>( list.size() );
for ( TestPOS testPOS : list ) {
list1.add( toBean( testPOS ) );
}
return list1;
}
}
看自动生成的testPOSListToTestBOSList的映射方法中也调用了前置方法,因为前置方法对于任意的Object源类型,泛型的目标类型都会调用,方法体中list1.add( toBean( testPOS ) )继续调用toBean映射方法,toBean映射方法中循环调用testBOS.setTestS( toBean( testPOS.getTestS() ) ),且继续调用testBOS.setList( testPOSListToTest