2
votes

I am using Spring and Hibernate and I am successfully autowiring a Repository inside the contructor of a Service class. When i try to add @Transactional methods in my Service class i get an AopConfigException that it is about the generation of CGLib class.


My Configuration consists in 3 files. An AppInitializer class that implements WebApplicationInitializer , a WebMvcConfig class that extends WebMvcConfigurerAdapter and lastly a PersistentContext class.

AppInitializer

public class AppInitializer implements WebApplicationInitializer {

    private static final String CONFIG_LOCATION = "com.project.app.config";
    private static final String MAPPING_URL = "/";

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        WebApplicationContext context = getContext();   
        servletContext.addListener(new ContextLoaderListener(context));
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet",
                new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping(MAPPING_URL);

    }

    private AnnotationConfigWebApplicationContext getContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation(CONFIG_LOCATION);
        return context;
    }

WebMvcConfig

@EnableWebMvc
@Configuration
@ComponentScan(basePackages = { "com.project.app" })
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private Environment env;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {            
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("hello");
    }

    @Bean
    public ApplicationContextProvider applicationContextProvider() {
        return new ApplicationContextProvider();
    }
}

PersistentContext

@Component
@EnableJpaRepositories("com.project.app.services.repositories")
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class PersistenceContext {

    @Autowired
    private Environment env;

    @Bean
    @Primary
    public DataSource dataSource() throws ClassNotFoundException {
        DataSource ds = new DataSource();
        ds.setUrl(env.getProperty(SystemSettings.DS_URL));
        ds.setUsername(env.getProperty(SystemSettings.DS_USERNAME));
        ds.setPassword(env.getProperty(SystemSettings.DS_PASSWORD));
        ds.setDriverClassName(env.getProperty(SystemSettings.DS_DRIVER));    
        return ds;
    }

    @Bean
    LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManagerFactoryBean.setPackagesToScan("com.project.app.services.entities");
        // .. Set Properties..  
        entityManagerFactoryBean.setJpaProperties(jpaProperties);    
        return entityManagerFactoryBean;
    }

    @Bean
    JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

}

The Repository INTERFACE extends CrudRepository

StopRepository

@Repository
@RepositoryRestResource(collectionResourceRel = "stop", path = "stop")
public interface StopRepository extends CrudRepository<StopJPA, Long> {   
    @Override
    StopJPA save(StopJPA persisted);    
}

The Entity class.

StopJPA

@Entity
@Table(name = "stop")
public class StopJPA implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Column(name = "stop_description")
    private String stopDescription;

    @Column(name = "id_stop", nullable = false)
    private String idStop;

    public StopJPA() {
    }

    public StopJPA(String stopDescription, String idStop) {
        this.stopDescription = stopDescription;
        this.idStop = idStop;
    }

    // .. Getters & Setters ..

}

And the Service class implementation:

StopService

@Service
final class RepoStopService {
    private StopRepository stopRepository;

    @Autowired
    RepoStopService(StopRepository stopRepository) {
        this.stopRepository = stopRepository;
    }

    @Transactional
    public StopDTO create(StopDTO newEntry) {
        StopJPA created = new StopJPA(newEntry.getStopDescription(), newEntry.getIdStop());
        created = stopRepository.save(created);
        return EntitiesConverter.mapEntityIntoDTO(created);
    }
}

Unfortunately when i try to run it on server i get this exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'repoStopService' defined in file [C:..\RepoStopService.class]: Initialization of bean failed; Caused by: org.springframework.aop.framework.AopConfigException:Could not generate CGLIB subclass of class [class ..RepoStopService]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class ..RepoStopService

I understand that Spring uses either JDK proxy or CGLib. The default behavior if we autowire an interface (here its the repository) is to have a JDK proxy? what need to be changed in order to make it work?

1
Did you see the Spring forum about this problem? forum.spring.io/forum/spring-projects/web/social/…Diogo de Góes Zanetti
Have you actually read the exception? It is about your service it isn't complaining about the repositories.M. Deinum

1 Answers

9
votes

The exception message is quite clear

AopConfigException:Could not generate CGLIB subclass of class [class ..RepoStopService

First of all it is complaining about the fact that it cannot create a proxy for your service it doesn't mention your repositories at all. Your service is a class without an interface and it is also final.

The @Transactional is on a method in your service. Spring needs to create a proxy for your service to be able to start and commit the transaction at that point. As your service isn't implementing an interface it tries to create a class based proxy, however it cannot because your class is marked final.

To fix, remove the final or create an interface to be able to use JDK Dynamic proxies instead of Cglib based class proxies.

Note: Depending on the version of Spring used, it will still fail when removing the final keyword as on earlier versions it is also required to have a no-arg default constructor and you only have a single argument constructor.