开发环境
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>
关键代码
实体类
@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> {
}
自定义服务
@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);
}
}
启动类
@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
单元测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class BootHibernateInterceptorProblemApplicationTests {
@Resource
private UserRepository userRepository;
@Test
public void contextLoads() {
System.out.println(userRepository.findAll());
}
}
运行结果
java.lang.NullPointerException
at com.rjh.interceptor.SimpleInterceptor.onPrepareStatement(SimpleInterceptor.java:20)
...
...
...
分析
根据异常信息,猜测是注入MyService
失败
修改单元测试
@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());
}
}
运行结果
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=)}
...
分析
根据异常信息可知,Spring的IoC容器中并没有SimpleInterceptor
这个Bean
,从此处可知spring.jpa.properties.hibernate.ejb.interceptor=com.rjh.interceptor.SimpleInterceptor
并没有把这个拦截器注册到Spring容器中
失败方案
在SimpleInterceptor
上添加@Component
注解,将SimpleInterceptor
注册到Spring容器中。同时注释spring.jpa.properties.hibernate.ejb.interceptor
配置
失败:SimpleInterceptor
的构造方法触发了两次,添加到Hibernate
中的SimpleInterceptor
实例和注册到Spring容器中的SimpleInterceptor
实例并不是同一个实例
解决方法
增加一个获取Spring的ApplicationContext
实例的工具类,通过这个工具类调用需要注入的服务的方法
工具类
@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);
}
}
执行结果
MyService call
sql:select user0_.id as id1_0_, user0_.name as name2_0_ from user user0_
[]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。