Spring Data - Service类中的@Transactional注释引发了AopConfigException

yea*_*amf 2 java spring spring-aop cglib

我正在使用Spring和Hibernate,并且我成功地在Service类的构造函数内自动装配了一个Repository.当我尝试在我的Service类中添加方法时,我得到的是关于CGLib类的生成.@TransactionalAopConfigException


我的配置包含3个文件.一个AppInitializer实现类WebApplicationInitializer,一个WebMvcConfig扩展类WebMvcConfigurerAdapter和最后一PersistentContext类.

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;
    }
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

存储库INTERFACE扩展CrudRepository

StopRepository

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

实体类.

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 ..

}
Run Code Online (Sandbox Code Playgroud)

而Service类的实现:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,当我尝试在服务器上运行它时,我得到以下异常:

org.springframework.beans.factory.BeanCreationException:在文件[C:..\RepoStopService.class]中定义名称为'repoStopService'的bean时出错:bean的初始化失败; 引起:org.springframework.aop.framework.AopConfigException:无法生成类[class ..RepoStopService]的CGLIB子类:此问题的常见原因包括使用final类或不可见类; 嵌套的例外是java.lang.IllegalArgumentException异常:不能继承final类类..RepoStopService

我知道Spring使用JDK代理或CGLib.如果我们自动装配一个接口(这里是它的存储库)的默认行为是有一个JDK代理?需要改变什么才能使其发挥作用?

M. *_*num 6

异常消息非常清楚

AopConfigException:Could not generate CGLIB subclass of class [class ..RepoStopService
Run Code Online (Sandbox Code Playgroud)

首先,它抱怨它无法为您的服务创建代理,它根本没有提到您的存储库.您的服务是没有界面的类,它也是final.

这@Transactional是您服务中的方法.Spring需要为您的服务创建一个代理,以便能够在此时启动和提交事务.由于您的服务未实现接口,因此它会尝试创建基于类的代理,但它不能,因为您的类已被标记final.

要修复,删除final或创建一个接口,可以使用JDK动态代理,而不是基于CGLIB类代理.

注意:根据所使用的Spring版本,在删除final关键字时仍然会失败,因为在早期版本中,它还需要具有no-arg默认构造函数,并且您只有一个参数构造函数.