開發(fā)環(huán)境
JDK 1.8
Springboot 2.1.1.RELEASE
pom配置
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
關(guān)鍵代碼實(shí)體類
@Entity public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Repository
public interface UserRepository extends JpaRepository<User,Integer> { }
自定義服務(wù)
@Service public class MyService { public void print(){ System.out.println(this.getClass().getSimpleName()+" call"); } }
攔截器
public class SimpleInterceptor extends EmptyInterceptor { @Resource private MyService myService; @Override public String onPrepareStatement(String sql) { myService.print(); System.out.println("sql:"+sql); return super.onPrepareStatement(sql); } }
啟動(dòng)類
@SpringBootApplication public class BootHibernateInterceptorProblemApplication { public static void main(String[] args) { SpringApplication.run(BootHibernateInterceptorProblemApplication.class, args); } }
配置
## DataSource spring.datasource.url=jdbc:mysql://localhost:3306/demo?characterEncoding=utf8&useSSL=true spring.datasource.username=root spring.datasource.password=123456789 ## hibernate spring.jpa.hibernate.ddl-auto=update ## add interceptor spring.jpa.properties.hibernate.ejb.interceptor=com.rjh.interceptor.SimpleInterceptor
單元測(cè)試類
@RunWith(SpringRunner.class) @SpringBootTest public class BootHibernateInterceptorProblemApplicationTests { @Resource private UserRepository userRepository; @Test public void contextLoads() { System.out.println(userRepository.findAll()); } }
運(yùn)行結(jié)果
java.lang.NullPointerException at com.rjh.interceptor.SimpleInterceptor.onPrepareStatement(SimpleInterceptor.java:20) ... ... ...
分析
根據(jù)異常信息,猜測(cè)是注入MyService失敗
修改單元測(cè)試
@RunWith(SpringRunner.class) @SpringBootTest public class BootHibernateInterceptorProblemApplicationTests { @Resource private UserRepository userRepository; @Resource private MyService myService; @Resource private SimpleInterceptor simpleInterceptor; @Test public void contextLoads() { Assert.assertNotNull(myService); Assert.assertNotNull(simpleInterceptor); System.out.println(userRepository.findAll()); } }
運(yùn)行結(jié)果
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.rjh.interceptor.SimpleInterceptor' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.annotation.Resource(shareable=true, lookup=, name=, description=, authenticationType=CONTAINER, type=class java.lang.Object, mappedName=)} ...
分析
根據(jù)異常信息可知,Spring的IoC容器中并沒有SimpleInterceptor這個(gè)Bean,從此處可知spring.jpa.properties.hibernate.ejb.interceptor=com.rjh.interceptor.SimpleInterceptor并沒有把這個(gè)攔截器注冊(cè)到Spring容器中
失敗方案
在SimpleInterceptor上添加@Component注解,將SimpleInterceptor注冊(cè)到Spring容器中。同時(shí)注釋spring.jpa.properties.hibernate.ejb.interceptor配置
失?。?strong>SimpleInterceptor的構(gòu)造方法觸發(fā)了兩次,添加到Hibernate中的SimpleInterceptor實(shí)例和注冊(cè)到Spring容器中的SimpleInterceptor實(shí)例并不是同一個(gè)實(shí)例
解決方法
增加一個(gè)獲取Spring的ApplicationContext實(shí)例的工具類,通過這個(gè)工具類調(diào)用需要注入的服務(wù)的方法
工具類
@Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.applicationContext=applicationContext; } public static ApplicationContext getApplicationContext() { return applicationContext; } }
修改攔截器
public class SimpleInterceptor extends EmptyInterceptor { @Override public String onPrepareStatement(String sql) { MyService myService= SpringContextUtil.getApplicationContext().getBean(MyService.class); myService.print(); System.out.println("sql:"+sql); return super.onPrepareStatement(sql); } }
執(zhí)行結(jié)果
MyService call sql:select user0_.id as id1_0_, user0_.name as name2_0_ from user user0_ []
文章來源:segmentfault,作者:Null。
聯(lián)系客服