Spring中的@Import注解
介绍
@Import注解更多是用在写公共模块、自动装配的时候用到,比如说公共模块有请求日志记录、限流、加密等等功能,这些功能打包后都在一个jar中,而在一些项目中,只需要启动公共模块中的某些功能,比如只需要启用日志记录等,不用把所有功能都加载,这时候就可以用@Import注解,超级方便
记录请求日志案例
1、新建HttpLogHandlerInterceptor.java请求日志打印类,用于记录打印日志
import cn.hutool.core.date.SystemClock;
import com.bingo.study.common.core.utils.IPUtil;
import com.bingo.study.common.core.utils.JsonMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @Author h-bingo
* @Date 2023-05-28 14:47
* @Version 1.0
*/
@ConditionalOnMissingBean(HttpLogHandlerInterceptor.class)
public class HttpLogHandlerInterceptor implements HandlerInterceptor, InitializingBean {
private static final Logger log = LoggerFactory.getLogger("[ === Request LOG === ]");
private static final NamedThreadLocal<Long> COST_TIME = new NamedThreadLocal<>("HttpLogHandlerInterceptor");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
COST_TIME.set(SystemClock.now());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) throws Exception {
try {
Map<Object, Object> paramMap = new HashMap<>();
// 请求方法
String method = request.getMethod();
// 请求路径
String servletPath = request.getServletPath();
// ip地址
String ipAddr = IPUtil.getIpAddr(request);
// 耗时
long costTime = SystemClock.now() - COST_TIME.get();
paramMap.put("method", method);
paramMap.put("servletPath", servletPath);
paramMap.put("ip", ipAddr);
paramMap.put("costTime", costTime + "ms");
log.info(JsonMapper.getInstance().toJsonString(paramMap));
} catch (Exception ignored) {
} finally {
COST_TIME.remove();
}
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("请求日志打印已开启");
}
}
2、新建HttpLogHandlerAutoConfig.java类,配置请求日志打印类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Author h-bingo
* @Date 2023-05-28 15:27
* @Version 1.0
*/
@ConditionalOnMissingBean(HttpLogHandlerAutoConfig.class)
@Import({HttpLogHandlerInterceptor.class})
public class HttpLogHandlerAutoConfig implements WebMvcConfigurer {
@Autowired
private HttpLogHandlerInterceptor httpLogHandlerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(httpLogHandlerInterceptor);
}
}
3、以上基本功能就都已经完成了,可以发现并没有使用常见的@Bean或者@Component等spring注解,在HttpLogHandlerInterceptor类上只有@ConditionalOnMissingBean(HttpLogHandlerInterceptor.class)注解,这个注解的意思是Spring容器中不存在HttpLogHandlerInterceptor类的bean才注入(并没有注入bean的功能,只是一个条件限制)。
4、那么如何使用这个功能呢?新建EnableHttpLogInterceptor.java注解,在这个注解上标注@Import({HttpLogHandlerAutoConfig.class})
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* @Author h-bingo
* @Date 2023-05-28 15:24
* @Version 1.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({HttpLogHandlerAutoConfig.class})
public @interface EnableHttpLogInterceptor {
}
5、在项目的启动类上加上@EnableHttpLogInterceptor注解,然后随便新建一个controller接口,启动项目访问接口看效果,如下
6、可以看到项目启动后,HttpLogHandlerInterceptor类中日志‘请求日志打印功能已开启’成功打印,并且访问接口后有相应的日志记录,说明配置成功
原理分析
在以上案例中,首先是在启动类上标注了@EnableHttpLogInterceptor注解,项目启动后会加载这个注解,然后通过这个注解找到@Import({HttpLogHandlerAutoConfig.class}),此时将HttpLogHandlerAutoConfig.class类注入到spring容器中,然后又通过@Import({HttpLogHandlerInterceptor.class})将HttpLogHandlerInterceptor.class注入到容器中,因此在启动的过程中可以看到HttpLogHandlerInterceptor.class类中的afterPropertiesSet方法打印的日志
@Import源码分析
找到ConfigurationClassParser.java类processImports()方法,先看ge tImports()
上图分别是收集import进来的类,StudySystemApplication(启动类)、HttpLogHandlerAutoConfig(配置类)、HttpLogHandlerInterceptor(功能实现类)
往下直接看箭头处,前面两个if是另外两种用法
结束语
好了,本次分享Spring中的@Import注解简单使用就结束了,欢迎大家指正。