CGLib署理引起的空指针异常问题
一个同事将公司的开拓框架基于最新的Spring、Tomcat、Java版本作了部门修改,拿来开拓运行之后,发明一个奇怪的空指针异常。
还原一下其时的场景,代码或许如下,所有的Servlet担任自BaseServlet。以DefaultServlet为例,当有DefaultServlet请求达到时,会映射到一个ServletProxy的servlet,然后再转发至DefaultServlet。在转发之前已经挪用了DefaultServlet的setLogger要领,假设转发到doGet要领,doGet要领先挪用了response(req,res)又转到了execute要领,execute要领打印一行log,至此都没有问题。接下来doGet要领也打印一行log"@@@@@@doGet",在这里就报了空指针异常。logger为null,这就奇怪了,怎么会为null呢。
public abstract class BaseServlet extends HttpServlet {
protected Log logger;
@Override
public final void doGet( HttpServletRequest req, HttpServletResponse res ) throws IOException {
response( req, res );
logger.debug( "@@@@@@doGet");
}
@Override
public final void doPost( HttpServletRequest req, HttpServletResponse res ) throws IOException {
response( req, res );
logger.debug( "@@@@@@doPost");
}
private void response( HttpServletRequest req, HttpServletResponse res ) throws IOException {
String result = "";
try {
result = execute( req, res );
}
finally {
//返回功效
}
}
public void setLogger( Log logger ) {
this.logger = logger;
}
public abstract String execute( HttpServletRequest req, HttpServletResponse res ) throws IOException;
}
public class DefaultServlet extends BaseServlet {
@Override
public String execute(HttpServletRequest req, HttpServletResponse res)
throws IOException {
logger.info("@@@@@@@DefaultServlet");
}
}
倒腾了半天也找不出个原因,最后与本来的框架较量了一下,发明doGet与doPost被加上了final修饰符。我去,这样一想就有颔首绪了,因为在Spring设置文件中设置了动态署理做切面。动态署理又是用的CGLib。
下面顺便说一下CGLib的或许道理(有部门揣摩的身分,错误之处请指正),假设有个类A,假如用CGLib做动态署理,将会在字节码的层面上动态生成一个类B并加载。模仿代码如下,B担任自A同时又有对A的引用,B类所有可重写的要领都要重写并挪用A范例target的同名要领,虽然在挪用target要领之前可以做挪用前操纵和挪用后操纵,这才是署理的用途,这里就省略掉了。
在main函数里new了一个B类的实例,并挪用了setName要领,实际上执行的是target的setName要领,配置的是target的字段name,B实例的字段name仍然为空。挪用notFinalMethod要领也是挪用target的要领并能把target的字段name打印出来。可是finalMethod要领由于有final修饰符,所以不能在B中重写,当挪用finalMethod要领时,就只能乖乖地挪用B自己的finalMethod要领而不能挪用target的finalMethod要领,这时由于B实例的name为空,所以打印出来的值也就为空了。
public class CGLibSimulate {
public static void main(String[] args) {
A a=new B();
a.setName("aa");
a.notFinalMethod();
a.finalMethod();
}
public static class A {
protected String name = null;
public final void finalMethod() {
System.out.println(name);
}
public void notFinalMethod() {
System.out.println(name);
}
public void setName(String name){
this.name=name;
}
}
public static class B extends A {
private A target=new A();
@Override
public void notFinalMethod() {
target.notFinalMethod();
}
@Override
public void setName(String name){
target.setName(name);
}
}
}
领略了这个应该就领略了上边空指针问题的原因了吧,但愿能帮到碰着此问题的人。