How to fix xml-less autowiring of service

不想你离开。 提交于 2019-12-11 11:13:34

问题


When I call a service directly in my main() I can query the database and things work fine. When a jersey request comes in and maps the JSON to NewJobRequest I can't use my service because the @Autowire failed.

My app:

public class Main {

    public static final URI BASE_URI = getBaseURI();

    private static URI getBaseURI() {
        return UriBuilder.fromUri("http://localhost/").port(9998).build();
    }

    protected static HttpServer startServer() throws IOException {
        ResourceConfig rc = new PackagesResourceConfig("com.production.api.resources");
        rc.getFeatures()
            .put(JSONConfiguration.FEATURE_POJO_MAPPING, true);

        return GrizzlyServerFactory.createHttpServer(BASE_URI, rc);
    }

    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);

        //if this is uncommented, it'll successfully query the database
        //VendorService vendorService = (VendorService)ctx.getBean("vendorService");
        //Vendor vendor = vendorService.findByUUID("asdf"); 

        HttpServer httpServer = startServer();
        System.out.println(String.format("Jersey app started with WADL available at " + "%sapplication.wadl\nTry out %shelloworld\nHit enter to stop it...", BASE_URI, BASE_URI));
        System.in.read();
        httpServer.stop();
    }
}

My Resource (controller):

@Component
@Path("/job")
public class JobResource extends GenericResource {
    @Path("/new")
    @POST
    public String New(NewJobRequest request) {
        return "done";
    }
}

Jersey is mapping the JSON post to:

@Component
public class NewJobRequest {

    @Autowired
    private VendorService vendorService;

    @JsonCreator
    public NewJobRequest(Map<String, Object> request) {
        //uh oh, can't do anything here because @Autowired failed and vendorService is null
    }
}

VendorService:

@Service
public class VendorService extends GenericService<VendorDao> {

    public Vendor findByUUID(String uuid) {
        Vendor entity = null;
        try {
            return (Vendor)em.createNamedQuery("Vendor.findByUUID")
                    .setParameter("UUID", uuid)
                    .getSingleResult();
        } catch (Exception ex) {
            return null;
        }
    }
}

-

@Service
public class GenericService<T extends GenericDao> {

    private static Logger logger = Logger.getLogger(Logger.class.getName());

    @PersistenceContext(unitName = "unit")
    public EntityManager em;

    protected T dao;

    @Transactional
    public void save(T entity) {
        dao.save(entity);
    }
}

My service config:

@Configuration
public class Config {
    @Bean
    public VendorService vendorService() {
        return new VendorService();
    }
}

My config

@Configuration
@ComponentScan(basePackages = {
        "com.production.api",
        "com.production.api.dao",
        "com.production.api.models",
        "com.production.api.requests",
        "com.production.api.requests.job",
        "com.production.api.resources",
        "com.production.api.services"
})
@Import({
        com.production.api.services.Config.class,
        com.production.api.dao.Config.class,
        com.production.api.requests.Config.class
})
@PropertySource(value= "classpath:/META-INF/application.properties")
@EnableTransactionManagement
public class Config {
    private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
    private static final String PROPERTY_NAME_DATABASE_USER = "db.user";
    private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";

    private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
    private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
    private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";

    @Resource
    Environment environment;

    @Bean
    public DataSource dataSource() {
        MysqlDataSource dataSource = new MysqlDataSource();

        dataSource.setUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
        dataSource.setUser(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USER));
        dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));

        return dataSource;
    }

    @Bean
    public JpaTransactionManager transactionManager() throws ClassNotFoundException {
        JpaTransactionManager transactionManager = new JpaTransactionManager();

        transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());

        return transactionManager;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() throws ClassNotFoundException {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();

        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPersistenceUnitName("unit");
        entityManagerFactoryBean.setPackagesToScan(environment.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
        entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class);

        Properties jpaProperties = new Properties();
        jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
        jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
        jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));

        entityManagerFactoryBean.setJpaProperties(jpaProperties);

        return entityManagerFactoryBean;
    }
}

回答1:


The @Path and @POST annotations are JAX-RS, not Spring. So the container is instantiating your endpoints on its own, without any knowledge of Spring beans. You are most likely not getting any Spring logging because Spring is not being used at all.




回答2:


I've figured out the issue and blogged about it here: http://blog.benkuhl.com/2013/02/how-to-access-a-service-layer-on-a-jersey-json-object/

In the mean time, I'm also going to post the solution here:

I need to tap into the bean that Spring already created so I used Spring's ApplicationContextAware

public class ApplicationContextProvider implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public void setApplicationContext (ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}

And then used that static context reference within my object to be mapped to so I can perform lookups in the service:

public class NewJobRequest {

    private VendorService vendorService;

    public NewJobRequest() {
        vendorService = (VendorService) ApplicationContextProvider.getApplicationContext().getBean("vendorService");
    }

    @JsonCreator
    public NewJobRequest(Map<String, Object> request) {
        setVendor(vendorService.findById(request.get("vendorId")); //vendorService is null
    }
    ....
}


来源:https://stackoverflow.com/questions/15009641/how-to-fix-xml-less-autowiring-of-service

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!