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);
}
}
}
理解了这个应该就理解了上边空指针问题的原因了吧,希望能帮到遇到此问题的人。
CGLib动态代理引起的空指针异常的更多相关文章
- CGLib动态代理原理及实现
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了.CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采 ...
- Spring中的cglib动态代理
Spring中的cglib动态代理 cglib:Code Generation library, 基于ASM(java字节码操作码)的高性能代码生成包 被许多AOP框架使用 区别于JDK动态代理,cg ...
- 【Java EE 学习 51】【Spring学习第三天】【cglib动态代理】【AOP和动态代理】【切入点表达式】
一.cglib动态代理 1.简介 (1)CGlib是一个强大的,高性能,高质量的Code生成类库.它可以在运行期扩展Java类与实现Java接口. (2) 用CGlib生成代理类是目标类的子类. (3 ...
- JDK动态代理与CGLib动态代理
1.JDK动态代理 JDK1.3以后java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,动态代理是实现AOP的绝好底层技术. JDK的动态代理主要涉及到java.lang.reflect ...
- Java代理(jdk静态代理、动态代理和cglib动态代理)
一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 /** * 业务接 ...
- [z]Java代理(jdk静态代理、动态代理和cglib动态代理)
一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 1 2 3 4 5 ...
- Atitit 代理CGLIB 动态代理 AspectJ静态代理区别
Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...
- JDK动态代理和CGLib动态代理简单演示
JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...
- 代理模式 & Java原生动态代理技术 & CGLib动态代理技术
第一部分.代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常 ...
随机推荐
- 成都Uber优步司机奖励政策(4月17日)
滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...
- Mysql:查询当天、今天、本周、上周、本月、上月、本季度、本年的数据
1. 今天 select * from 表名 WHERE TO_DAYS(时间字段名) = TO_DAYS(NOW()); 2. 昨天 3. 本周 SELECT * FROM 表名 WHERE YEA ...
- 180726-InfluxDB基本概念小结
InfluxDB基本概念小结 InfluxDB作为时序数据库,与传统的关系型数据库相比而言,还是有一些区别的,下面尽量以简单明了的方式介绍下相关的术语概念 I. 基本概念 mysql influxdb ...
- PHP版本的讲解
原文地址:http://dev.meettea.com/show-90-1.html 最近发现很多PHP程序员对PHP版本知识了解不是很清楚,其中不乏PHP产品主力开发人员. PHP版本主要分三支:P ...
- WebGL射线拾取模型——八叉树优化
经过前面2篇WebGL射线拾取模型的文章,相信大家对射线和模型面片相交的原理已经有所了解,那么今天我们再深入探究关于射线拾取的一个问题,那就是遍历场景中的所有与射线相交的模型的优化问题.首先我们来复习 ...
- 用Python深入理解跳跃表原理及实现
最近看 Redis 的实现原理,其中讲到 Redis 中的有序数据结构是通过跳跃表来进行实现的.第一次听说跳跃表的概念,感到比较新奇,所以查了不少资料.其中,网上有部分文章是按照如下方式描述跳跃表的: ...
- Python字符串/元祖/列表/字典互转
#-*- coding:UTF-8 -*- #author:RXS002 #1.字典 dict = {'name':'Zara','age':7,'class':'First'} #字典转换为字符串, ...
- Javascript深入__proto__和prototype的区别和联系
有一个一个装逼的同事,写了一段代码 function a(){} a.__proto__.__proto__.__proto__ 然后问我,下面这个玩意a.__proto__.__proto__.__ ...
- Throwable、Error、Exception、RuntimeException的区别与联系
Throwable类是Java语言中所有错误和异常的超类.只有作为此类(或其子类之一)的实例的对象才被Java虚拟机抛出,或者可以被Java throw语句抛出.类似地,只有这个类或其子类之一可以是c ...
- Open vSwitch for CentOS
原文发表于cu:2016-06-02 本文属于重发,ovs当前的安装方式可能略有不同. 参考文档: 官方文档: http://openvswitch.org/support/dist-docs-2.5 ...