Search code examples
javaspring-bootdependency-injectionjaxbxmladapter

Compile-Time and Load-Time Weaving and Autowired on XMLAdapter not working


I have an XML-based database and I have defined a User model with a list of references to Role (another model). I have attached an XMLAdapter to roles property to auto-populate roles. For that, I've @autowired the RoleRepository in this adapter.

However, the repository is never autowired (always null), no matter what I do. I have configured Compile-Time Weaving, Load-Time Weaving, and also tried an instrumentation java agent that is able to load itself into the running JVM invesdwin-instrument.

@Configurable(autowire = Autowire.BY_TYPE)
public class RoleAdapter extends XmlAdapter<String, List<Role>> {

    @Autowired
    protected RoleRepository roleRepository;

    public RoleAdapter() {
    }

    @Override
    public List<Role> unmarshal(String nameList) throws Exception {
        // code using roleRepository
    }

    @Override
    public String marshal(List<Role> roles) throws Exception {
        // some code
    }
}
@SpringBootApplication
@EnableConfigurationProperties({MyProperties.class})
@EntityScan(basePackages = { ... })
@EnableDiscoveryClient
@EnableLoadTimeWeaving(aspectjWeaving=EnableLoadTimeWeaving
    .AspectJWeaving.ENABLED)
@EnableSpringConfigured // tried this in a separate config
public class MyApplication {

    static { // this was not here, added in a desperate move
        DynamicInstrumentationLoader.waitForInitialized();
        DynamicInstrumentationLoader.initLoadTimeWeavingContext();
    }

    // some code

    /**
     * Main method, used to run the application.
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        // dynamically attach java agent to jvm if not already present
        DynamicInstrumentationLoader.waitForInitialized();
        // weave all classes before they are loaded as beans
        DynamicInstrumentationLoader.initLoadTimeWeavingContext();

        if (!InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
            throw new IllegalStateException("Instrumentation not available!");
        } else { // it always gets here
            System.out.println("Instrumentation available!");
        }

        SpringApplication app = new SpringApplication(MyApplication.class);
        DefaultProfileUtil.addDefaultProfile(app);
        Environment env = app.run(args).getEnvironment();
        logApplicationStartup(env);
    }

    // more code
}

And the roles field in User

@XmlElement(type = String.class)
@XmlJavaTypeAdapter(RoleAdapter.class)
@XmlSchemaType(name = "IDREFS")
protected List<Role> roles;

I would like to know what I've missed here with weaving. A simpler way to achieve these auto-populating properties is also welcome.


Solution

  • "Solved" the problem by forgetting Compile-Time Weaving and Load-Time Weaving, and injecting RolesRepository into UsersRepository, initializing an instance of the RoleAdapter with the injected repository and add this instance into the unmarshaller.

    @Repository
    public class UserRepository extends TimestampRepository<User> {
    
        public static final String USERS_BY_USERNAME_CACHE = "usersByUsername";
        public static final String USERS_BY_EMAIL_CACHE = "usersByEmail";
    
        public UserRepository(
                MyProperties myProperties,
                ExistTemplate existTemplate,
                Jaxb2Marshaller marshaller,
                Jaxb2Marshaller unmarshaller,
                RoleRepository roleRepository) {
            super(
                    new UserEntityInformation(myProperties.getDatabase().getDbname()),
                    existTemplate, marshaller, unmarshaller);
            marshaller.setAdapters(new RoleAdapter(roleRepository));
            unmarshaller.setAdapters(new RoleAdapter(roleRepository));
        }
    
        // more code
    }
    
    public class RoleAdapter extends XmlAdapter<String, List<Role>> {
    
        protected final RoleRepository roleRepository;
    
        public RoleAdapter(RoleRepository roleRepository) {
            this.roleRepository = roleRepository;
        }
    
        @Override
        public List<Role> unmarshal(String nameList) throws Exception {
            // code using roleRepository
        }
    
        @Override
        public String marshal(List<Role> roles) throws Exception {
            // some code
        }
    }