开发环境

  1. JDK 1.8
  2. 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>

关键代码

实体类

@Entitypublic 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> {}

自定义服务

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

启动类

@SpringBootApplicationpublic class BootHibernateInterceptorProblemApplication {    public static void main(String[] args) {        SpringApplication.run(BootHibernateInterceptorProblemApplication.class, args);    }}

配置

## DataSourcespring.datasource.url=jdbc:mysql://localhost:3306/demo?characterEncoding=utf8&useSSL=truespring.datasource.username=rootspring.datasource.password=123456789## hibernatespring.jpa.hibernate.ddl-auto=update## add interceptorspring.jpa.properties.hibernate.ejb.interceptor=com.rjh.interceptor.SimpleInterceptor

单元测试类

@RunWith(SpringRunner.class)@SpringBootTestpublic 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)@SpringBootTestpublic 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实例的工具类,通过这个工具类调用需要注入的服务的方法

工具类

@Componentpublic 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 callsql:select user0_.id as id1_0_, user0_.name as name2_0_ from user user0_[]