Home > other >  how to use jpa repository in spring OncePerRequestFilter?
how to use jpa repository in spring OncePerRequestFilter?

Time:10-13

I wanna make a logging system using HTTP requests and responses. After processing some data, I want to finally save it to the database. But repository injection is not possible.

class LogFilter() : OncePerRequestFilter() {
    private val log: Logger = LoggerFactory.getLogger(LogFilter::class.java)

//    @Autowired
//    lateinit var actionLogRepository: ActionLogRepository // <- injection failed

    @Throws(ServletException::class, IOException::class)
    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain,
    ) {
        val uri = request.requestURI
        var contentRequest = request
        var contentResponse = response

        if (uri.startsWith("/swagger-ui", true) ||
            uri.startsWith("/v3/api-docs", true)
            || uri.startsWith("/api/health-check", true)
        ) {
            filterChain.doFilter(request, response)
            return
        }
        val start: Long = System.currentTimeMillis()
        try {
            contentRequest = ContentCachingRequestWrapper(request)
            contentResponse = ContentCachingResponseWrapper(response)
            filterChain.doFilter(contentRequest, contentResponse)
        } finally {
            val actionLog = ActionLog()
            logRequest(contentRequest, actionLog)
            logResponse(contentRequest, contentResponse, actionLog)
            actionLog.saveSpentTime(System.currentTimeMillis() - start)
//            actionLogRepository.save(actionLog) // <- it's face error by null injection
        }
    }

    fun logRequest(request: HttpServletRequest, actionLog: ActionLog)
    fun logResponse(request: HttpServletRequest, actionLog: ActionLog)

}

I can't even do constructor injection. How can this work?

CodePudding user response:

There is not much code, but what I can see is that there is no @Component annotation here.

You have to add this class to Spring context, so the Spring can find it, and inject dependencies. There is couple of ways to do that. In your case, I would prefer the first option:

  1. The simplest way is to mark this class with @Component annotation. Then Spring will find the class, create an instance and make any required dependency injection.
  2. You can create @Configuration class with @Bean method. In this method you can create an instance of your filter. In this case, you can make some initial configuration to the created class if required. But in this case, you have to inject the dependency to this filter by yourself. Spring can inject it to your @Configuration class or method parameter, example:
@Configuration
public class LoggingFilterConfig {

    @Bean
    fun createLogFilter(actionLogRepository: ActionLogRepository): LogFilter {
        return LogFilter(actionLogRepository)
    }

}

The method parameter actionLogRepository will be injected by Spring.


As a side note, I can add, that marking the field with @Autowired annotation is not the preferred way of doing dependency injection. If the dependency is required better make it through constructor (in this case, you don't have to mark it as @Autowired)

CodePudding user response:

I understand that you are reading data from the server and trying to write it to the database. But you need to upload your specific example because of the complexity. You should create your example on github and tag it in the question

  • Related