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 个文件。
实现 WebApplicationInitializer
的 AppInitializer
class,扩展 WebMvcConfigurerAdapter
的 WebMvcConfig
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
关键字时仍然会失败,因为在早期版本中它也是必需的有一个无参数的默认构造函数,而你只有一个参数构造函数。
我正在使用 Spring 和 Hibernate,并且我在 Service 的构造函数中成功地自动装配了 Repository class。当我尝试在我的 Service class 中添加 @Transactional
方法时,我得到一个 AopConfigException
,它是关于 CGLib 的生成 class。
我的配置包含 3 个文件。
实现 WebApplicationInitializer
的 AppInitializer
class,扩展 WebMvcConfigurerAdapter
的 WebMvcConfig
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
关键字时仍然会失败,因为在早期版本中它也是必需的有一个无参数的默认构造函数,而你只有一个参数构造函数。