一个同事将公司的开发框架基于最新的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);
}
}
}

  理解了这个应该就理解了上边空指针问题的原因了吧,希望能帮到遇到此问题的人。

CGLib动态代理引起的空指针异常的更多相关文章

  1. CGLib动态代理原理及实现

    JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了.CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采 ...

  2. Spring中的cglib动态代理

    Spring中的cglib动态代理 cglib:Code Generation library, 基于ASM(java字节码操作码)的高性能代码生成包 被许多AOP框架使用 区别于JDK动态代理,cg ...

  3. 【Java EE 学习 51】【Spring学习第三天】【cglib动态代理】【AOP和动态代理】【切入点表达式】

    一.cglib动态代理 1.简介 (1)CGlib是一个强大的,高性能,高质量的Code生成类库.它可以在运行期扩展Java类与实现Java接口. (2) 用CGlib生成代理类是目标类的子类. (3 ...

  4. JDK动态代理与CGLib动态代理

    1.JDK动态代理 JDK1.3以后java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,动态代理是实现AOP的绝好底层技术. JDK的动态代理主要涉及到java.lang.reflect ...

  5. Java代理(jdk静态代理、动态代理和cglib动态代理)

    一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 /** * 业务接 ...

  6. [z]Java代理(jdk静态代理、动态代理和cglib动态代理)

    一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 1 2 3 4 5 ...

  7. Atitit 代理CGLIB 动态代理 AspectJ静态代理区别

    Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...

  8. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  9. 代理模式 & Java原生动态代理技术 & CGLib动态代理技术

    第一部分.代理模式  代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常 ...

随机推荐

  1. struts2框架实例

    一,Struts2框架介绍 它是一个View框架,对Servle进行了封装,使用核心过滤器对servlet进行了解耦,可以自动封装数据 核心是结果视图导航 二,程序实例 1.导入框架依赖包 2.注册框 ...

  2. cogs696 longest prefix

    cogs696 longest prefix 原题链接 IOI1996原题? 其实这题我不会. map+string+手动氧气大法好 //就是这么皮(滑稽 Code // It is made by ...

  3. log4net始终占用日志文件的问题

    在appender 下面加 <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />

  4. Vue 项目在其他电脑 npm run dev 运行报错的解决方法

    一个 Vue 项目从一台电脑上传到 github 上之后,再另外一台电脑上 git clone .并使用 npm run dev 或 npm run start 发生以下报错的解决方法.   报错原因 ...

  5. Unity学习笔记草稿篇(一)为unity配置添加VS智能感知

    1. 打开要编辑的配置文件: 2. 菜单栏 -> xml -> 架构(schema) -> 添加或使用xsd.如下图所示:

  6. AirSim的搭建和使用

    由于自己使用设备拍摄的数据质量太差,所以决定使用AirSim这个框架来生成数据.之所以使用这个框架,是因为之前同事用其生成了一些有效数据. 当然,我是不可能把我搭建的步骤一一写出来的,一来是因为太麻烦 ...

  7. 1.openldap介绍

    1.openldap介绍 OpenLDAP是轻型目录访问协议(Lightweight Directory Access Protocol,LDAP)的自由和开源的实现,在其OpenLDAP许可证下发行 ...

  8. 遗传算法中几种不同选择算子及Python实现

    前言 本文对遗传算法中的几种选择策略进行了总结, 其中包括: Proportionate Roulette Wheel Selection Linear Ranking Selection Expon ...

  9. 关于0x80000000为什么等于-2147483648和负数在内存上储存的问题

    转载自大佬的博客https://blog.csdn.net/youyou362/article/details/72667951/ 1·先说明负数怎么储存 (1)十进制负数是以其补码储存在内存上. 验 ...

  10. Python:字典操作总结

     字典是Python中唯一的映射类型 [注]:字典中数据是无序排放的 一.字典的创建方法 方法1:用大括号包裹键值对从而创建字典 addict={}#创建一个空字典 addict={key1:valu ...