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

  • 开发环境

    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)

相关推荐

  • 使用 Java 发送邮件

    在我们的应用程序中有时需要给用户发送邮件,例如激活邮件.通知邮件等等.那么如何使用 Java 来给用户发送邮件呢? 使用 java 代码发送邮件 使用工具类发送邮件 使用Spring进行整合发送邮件 ...

  • eclipse构建maven+spring+mybatis控制台应用。

    使用maven+spring+mybatis构建控制台应用,方便调试spring或者mybatis代码,或者编写测试代码. 一,首先使用eclipse构建maven项目.详细教程. 二,在你的 pom ...

  • SpringBoot 整合 Shiro 实现登录拦截

    一.搭建一个SpringBoot 项目. 二.导入shiro 相关坐标: <dependency> <groupId>org.apache.shiro</groupId& ...

  • jackson学习之九:springboot整合(配置文件)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 系列文章汇总 jackson学习之一:基本信息 jac ...

  • 手把手教你SpringBoot整合MybatisPlus 代码生成器

    一.在pom.xml中添加所需依赖 <!-- MyBatis-Plus代码生成器--><dependency> <groupId>com.baomidou</ ...

  • quartz与springboot整合无法暂停job问题

    很多项目中都会用到定时任务的场景.起初项目中只是简单的使用了spring提供的@Scheduled注解.随着定时任务越来越多,我们需要对定时任务进行可视化管理,于是就单独建立了一个工程,用quartz ...

  • SpringBoot整合RabbitMQ 手动应答 简单demo

    版本说明 JDK 1.8 RabbitMQ 3.7.15 Erlang 22.0 SpringBoot 2.3.3.RELEASE // Update 2021年1月19日16:50:16 CentO ...

  • springboot 打jar 包部署时 读取外部配置文件

    案例:本文主要描述linux系统执行jar包读取jar包同级目录的外部配置文件 方法一:相对路径设置配置文件 (1)在jar包同级目录创建配置文件conf.properties并写入配置数据: con ...

  • SpringBoot整合Shiro权限框架实战

    什么是ACL和RBAC ACL Access Control list:访问控制列表 优点:简单易用,开发便捷 缺点:用户和权限直接挂钩,导致在授予时的复杂性,比较分散,不便于管理 例子:常见的文件系 ...

  • 说说在 Python 中如何实现输出指定函数运行时长的装饰器

    假设我们需要一个可以输出某个函数运行时长的装饰器. 1 基础实现 一种可能的定义方式为: 这里利用函数装饰器,在 clock(func) 函数内部定义了一个 clock(*args) 函数,定义好后直 ...

  • SpringBoot整合阿里短信服务

    导读 由于最近手头上需要做个Message Gateway,涉及到:邮件(点我直达).短信.公众号(点我直达)等推送功能,网上学习下,整理下来以备以后使用. 步骤 点我直达 登录短信服务控制台 点我直 ...