Springboot整合Hibernate攔截器時沒法向攔截器注入Bean

開發環境

  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>

關鍵代碼

實體類

@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失敗java

修改單元測試

@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容器中mysql

失敗方案

SimpleInterceptor上添加@Component註解,將SimpleInterceptor註冊到Spring容器中。同時註釋spring.jpa.properties.hibernate.ejb.interceptor配置spring

失敗:SimpleInterceptor的構造方法觸發了兩次,添加到Hibernate中的SimpleInterceptor實例和註冊到Spring容器中的SimpleInterceptor實例並非同一個實例sql

解決方法

增長一個獲取Spring的ApplicationContext實例的工具類,經過這個工具類調用須要注入的服務的方法app

工具類

@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_
[]
相關文章
相關標籤/搜索