When you declare:
@Mapper(
uses = {StudentMapper.class}
)
public interface ProgramMapper {
@Mapper(
uses = {ProgramMapper.class}
)
public interface StudentMapper {
You're causing the cyclic reference because Spring gets stuck in a loop trying to resolve both beans at creation time. Spring can't resolve this for interfaces because it creates proxy implementations at runtime and has no way to inject one mapper into another after creation.
You can remove it and try to make it an abstract class i.e.
@Mapper(componentModel = "spring", uses = {})
public abstract class ProgramMapper {
@Autowired @Lazy
protected StudentMapper studentMapper;
public abstract Program toProgram(ProgramDTO dto, @Context CycleAvoidingMappingContext context);
public abstract ProgramDTO toProgramDTO(Program entity, @Context CycleAvoidingMappingContext context);
}
Alternatively Use interfaces + a mapping service class to handle dependencies explicitly.
side note: removing the @builder when using mapstruct is cleaner — just use either one