相关版本
Spring Boot 2.1.5.RELEASE
Spring AOP 1.5.7.RELEASE
CGLIB原理:
动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
-
主要原因由于org.springframework.web.filter.GenericFilterBean的子类A的代理对象的logger属性为空引发的空指针异常 -
分析
- AOP 注解
@Aspect。对应切点配置的路径下类会由CGLIB(SpringBoot默认)动态代理生成.
@Pointcut("execution(public * com.example.handler..*.*(..))")-
包
com.example.handler下的类A继承了OncePerRequestFilter -
由于
CGLIB原理, 生成类A的代理对象时,final声明的属性将为空
public abstract class GenericFilterBean implements Filter, BeanNameAware, EnvironmentAware, EnvironmentCapable, ServletContextAware, InitializingBean, DisposableBean { /** Logger available to subclasses. */ # ======> logger 属性声明为 final <====== protected final Log logger = LogFactory.getLog(getClass()); @Nullable private String beanName; @Nullable private Environment environment; @Nullable private ServletContext servletContext; @Nullable private FilterConfig filterConfig; private final Set<String> requiredProperties = new HashSet<>(4); 略......GenericFilterBean执行init()方法到类A时,由于类A的代理对象的logger属性为空, 导致logger.isDebugEnabled()报空指针异常
// org.springframework.web.filter.GenericFilterBean /** * Standard way of initializing this filter. * Map config parameters onto bean properties of this filter, and * invoke subclass initialization. * @param filterConfig the configuration for this filter * @throws ServletException if bean properties are invalid (or required * properties are missing), or if subclass initialization fails. * @see #initFilterBean */ @Override public final void init(FilterConfig filterConfig) throws ServletException { Assert.notNull(filterConfig, "FilterConfig must not be null"); this.filterConfig = filterConfig; // Set bean properties from init parameters. PropertyValues pvs = new FilterConfigPropertyValues(filterConfig, this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(filterConfig.getServletContext()); Environment env = this.environment; if (env == null) { env = new StandardServletEnvironment(); } bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, env)); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { String msg = "Failed to set bean properties on filter '" + filterConfig.getFilterName() + "': " + ex.getMessage(); logger.error(msg, ex); throw new NestedServletException(msg, ex); } } // Let subclasses do whatever initialization they like. initFilterBean(); # ========> 由于 logger 为空, 此处出现空指针异常 <======== if (logger.isDebugEnabled()) { logger.debug("Filter '" + filterConfig.getFilterName() + "' configured for use"); } } - AOP 注解
-
解决方案
-
将继承
OncePerRequestFilter的类A移出对应包路径com.example.handler -
将对应 Filter 功能移到其他包下执行
-