Spring 数据 - 服务中的@Transactional 注释 class 引发 AopConfigException

Spring Data - @Transactional annotation in Service class raises AopConfigException

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


我的配置包含 3 个文件。 实现 WebApplicationInitializerAppInitializer class,扩展 WebMvcConfigurerAdapterWebMvcConfig class,最后是 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;
    }

}

存储库 INTERFACE 扩展 CrudRepository

StopRepository

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

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

}

和服务 class 实现:

停止服务

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

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

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

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

异常信息很清楚

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

首先,它抱怨它无法为您的服务创建代理,它根本没有提及您的存储库。你的服务是class没有接口也是final

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

要修复,请删除 final 或创建一个接口,以便能够使用 JDK 动态代理而不是基于 Cglib 的 class 代理。

注意: 根据使用的 Spring 版本,删除 final 关键字时仍然会失败,因为在早期版本中它也是必需的有一个无参数的默认构造函数,而你只有一个参数构造函数。