分享

Springboot整合Hibernate拦截器时无法向拦截器注入Bean

 三十的狼 2021-07-22


  • 开发环境

    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_
    []
    文章来源:segmentfault,作者:Null。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多