***cglib动态代理***和***jdk动态代理***都是在java运行过程中动态生成新的类,cglib是利用新生成的代理类来继承委托类,因为继承可以在代理类内拥有委托类的非private和非final方法,这样就达到了代理委托类去执行;
首先,要引进依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
1.创建一个委托类
package com.fekertest.cglib.bean;
public class BookServiceBean {
public void create(){
System.out.println("create() is running !");
}
public void query(){
System.out.println("query() is running !");
}
public void update(){
System.out.println("update() is running !");
}
public void delete(){
System.out.println("delete() is running !");
}
}
2.新增拦截器
package com.fekertest.cglib.interceptor;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.log4j.Logger;
public class MyCglibProxy implements MethodInterceptor{
private Logger log=Logger.getLogger(MyCglibProxy.class);
public Enhancer enhancer = new Enhancer();
private String name;
public MyCglibProxy(String name) {
this.name = name ;
}
/**
* 根据class对象创建该对象的代理对象
* 1、设置父类;2、设置回调
* 本质:动态创建了一个class对象的子类
*
* @param cls
* @return
*/
public Object getDaoBean(Class cls) {
enhancer.setSuperclass(cls);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
log.info("调用的方法是:" + method.getName());
//用户进行判断
if(!"boss".equals(name)){
System.out.println("你没有权限!");
return null;
}
Object result = methodProxy.invokeSuper(object, args);
return result;
}
}
拦截器在intercept中完成对目标方法的最终调用,这里边可以写自己的拦截逻辑,我们这里写了只允许boss来调用目标方法;
3.创建工厂,来生成委托类实例
package com.fekertest.cglib.bean;
import com.fekertest.cglib.filter.MyProxyFilter;
import com.fekertest.cglib.interceptor.MyCglibProxy;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
public class BookServiceFactory {
private static BookServiceBean service = new BookServiceBean();
private BookServiceFactory() {
}
public static BookServiceBean getProxyInstance(MyCglibProxy myProxy){
Enhancer en = new Enhancer();
//进行代理
en.setSuperclass(BookServiceBean.class);
en.setCallback(myProxy);
//生成代理实例
return (BookServiceBean)en.create();
}
public static BookServiceBean getProxyInstanceByFilter(MyCglibProxy myProxy){
Enhancer en = new Enhancer();
en.setSuperclass(BookServiceBean.class);
en.setCallbacks(new Callback[]{myProxy, NoOp.INSTANCE});
en.setCallbackFilter(new MyProxyFilter());
return (BookServiceBean)en.create();
}
public static void main(String[] args) {
BookServiceBean service = BookServiceFactory.getProxyInstance(new MyCglibProxy("boss"));
service.create();
BookServiceBean service2 = BookServiceFactory.getProxyInstance(new MyCglibProxy("john"));
service2.create();
BookServiceBean service3 = BookServiceFactory.getProxyInstanceByFilter(new MyCglibProxy("jhon"));
service.create();
BookServiceBean service4 = BookServiceFactory.getProxyInstanceByFilter(new MyCglibProxy("jhon"));
service2.query();
}
}
这里面有 getProxyInstance 和 getProxyInstanceByFilter 这两个方法,第一个方式是直接建立代理,第二个是含有过滤器的代理;
4.新增过滤器
package com.fekertest.cglib.filter;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
public class MyProxyFilter implements CallbackFilter {
@Override
public int accept(Method arg0) {
if(!"query".equalsIgnoreCase(arg0.getName()))
return 0;
return 1;
}
}
这样通过调用上面启动类的方法,就可以看到结果:
create() is running !
你没有权限!
你没有权限!
query() is running !
前两个结果是都调用拦截器直接得来的处理结果,后两个结果是添加了过滤器获得的结果,显然,过滤器过滤掉了对query方法的拦截;
接下来来看cglib的原理:
我们对动态生成的代理类进行打印可以看到对于create方法,在这个代理类中对应会有create 和CGLIB$create
0
这
两
个
方
法
;
其
中
前
者
则
是
我
们
使
用
代
理
类
时
候
调
用
的
方
法
,
后
者
是
在
方
法
拦
截
器
里
面
调
用
的
,
换
句
话
来
说
当
我
们
代
码
调
用
代
理
对
象
的
c
r
e
a
t
e
方
法
,
然
后
会
到
方
法
拦
截
器
中
调
用
i
n
t
e
r
c
e
p
t
方
法
,
该
方
法
内
则
通
过
p
r
o
x
y
.
i
n
v
o
k
e
S
u
p
e
r
调
用
C
G
L
I
B
0这两个方法;其中前者则是我们使用代理类时候调用的方法,后者是在方法拦截器里面调用的,换句话来说当我们代码调用代理对象的create方法,然后会到方法拦截器中调用intercept方法,该方法内则通过proxy.invokeSuper调用CGLIB
0这两个方法;其中前者则是我们使用代理类时候调用的方法,后者是在方法拦截器里面调用的,换句话来说当我们代码调用代理对象的create方法,然后会到方法拦截器中调用intercept方法,该方法内则通过proxy.invokeSuper调用CGLIBcreate$0这个方法,不要因为方法名字太长了就觉得难,其实原理很简单。。。
上面我们看了CGLib动态代理的用法、实际生成的代理类以及FastClass机制,下面我们就以最前面的那个例子中调用create()方法来看看主要的调用步骤;
第一步:是经过一系列操作实例化出了Enhancer对象,并设置了所需要的参数然后enhancer.create()成功创建出来了代理对象,这个就不多说了…
第二步:调用代理对象的create()方法,会进入到方法拦截器的intercept()方法,在这个方法中会调用methodProxy.invokeSuper(obj, args);方法
第三步:invokeSuper中,通过FastClass机制调用目标类的方法
Jdk动态代理的拦截对象是通过反射的机制来调用被拦截方法的,反射的效率比较低,所以cglib采用了FastClass的机制来实现对被拦截方法的调用。FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法,下面用一个小例子来说明一下,这样比较直观:
public class FastClass{
public static void main(String[] args){
Test tt = new Test();
Test2 fc = new Test2();
int index = fc.getIndex("f()V");
fc.invoke(index, tt, null);
}
}
class Test{
public void f(){
System.out.println("f method");
}
public void g(){
System.out.println("g method");
}
}
class Test2{
public Object invoke(int index, Object o, Object[] ol){
Test t = (Test) o;
switch(index){
case 1:
t.f();
return null;
case 2:
t.g();
return null;
}
return null;
}
public int getIndex(String signature){
switch(signature.hashCode()){
case 3078479:
return 1;
case 3108270:
return 2;
}
return -1;
}
}
上例中,Test2是Test的Fastclass,在Test2中有两个方法getIndex和invoke。在getIndex方法中对Test的每个方法建立索引,并根据入参(方法名+方法的描述符)来返回相应的索引。Invoke根据指定的索引,以ol为入参调用对象O的方法。这样就避免了反射调用,提高了效率。
方法拦截器中只有一个intercept()方法,这个方法有四个参数,obj表示代理对象,method表示目标类中的方法,args表示方法参数,proxy表示代理方法的MethodProxy对象
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy)
在这个方法内部会调用proxy.invokeSuper(obj, args)方法,我们进入.invokeSuper方法内部看看:
简单看看init()方法:
FastClassInfo内部如下图,由此可以看出prxy.invokeSuper()方法中fci.f2.invoke(fci.i2, obj, args),其实就是调用CGLIB
c
r
e
a
t
e
create
create这个方法
总结:
这里随便画一个简单的图看看整个过程,当我们去调用方法一的时候,在代理类中会先判断是否实现了方法拦截的接口,没实现的话直接调用目标类的方法一;如果实现了那就会被方法拦截器拦截,在方法拦截器中会对目标类中所有的方法建立索引,其实大概就是将每个方法的引用保存在数组中,我们就可以根据数组的下标直接调用方法,而不是用反射;索引建立完成之后,方法拦截器内部就会调用invoke方法(这个方法在生成的FastClass中实现),在invoke方法内就是调用CGLIB 方 法 一 方法一 方法一这种方法,也就是调用对应的目标类的方法一;
一般我们要添加自己的逻辑就是在方法拦截器那里。。。。
最后的最后,将jdk动态代理和cglib动态代理进行总结和比较
动态代理在Java中有着广泛的应用,如Spring AOP,Hibernate数据查询、测试框架的后端mock、RPC,Java注解对象获取等。动态代理的代理关系是在运行时期确定的。接下来主要阐述两种动态代理的区别。
JDK和CGLib动态代理分析
自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方。JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler。其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑(如:我们在方法执行前后打印的日志,本文只是为了演示,实际的应用一般不会只是简单的打印日志的),并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。
JDK动态代理的话,他有一个限制,就是它只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,如何创建动态代理实例哪?答案就是CGLib。
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。
JDK和CGLib动态代理区别
1、JDK动态代理具体实现原理:
通过实现InvocationHandler接口创建自己的调用处理器;
通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
2、CGLib动态代理:
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
3、两者对比:
JDK动态代理是面向接口的。
CGLib动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被final关键字所修饰,会失败。
4、使用注意:
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制);
如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
5、JDK和CGLib动态代理性能对比
关于两者之间的性能的话,网上有人对于不通版本的jdk进行测试,经过多次试验,测试结果大致是这样的,在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢,但是并没有教科书上的10倍差距,在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了,但是JDK动态代理和CGLIB动态代理的适用场景还是不一样的哈!
最后的最后的最后,再分享一个“万能”的jdk动态代理和cglib动态代理的类
package com.fekertest.cglib;
import com.fekertest.cglib.bean.BookServiceBean;
import com.fekertest.jdk.Huochepiao;
import com.fekertest.jdk.Tieluju;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import java.lang.reflect.Proxy;
public class ProxyService {
/**
* jdk动态代理
*
* @param object 被代理类对象
* @return 代理实例
*/
public static Object jdkProxyObject(Object object) {
//拦截器
SimpleInterceptor interceptor = new SimpleInterceptor();
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
(proxy, method, args) -> {
//拦截器 - 前置处理
interceptor.before();
Object result = method.invoke(object, args);
//拦截器 - 后置处理
interceptor.after();
return result;
});
}
/**
* cglib动态代理
*
* @param object 被代理类对象
* @return 代理实例
*/
public static Object cglibProxyObject(Object object) {
//模拟拦截器
SimpleInterceptor interceptor = new SimpleInterceptor();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(object.getClass());
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
//拦截器 - 前置处理
interceptor.before();
Object result = method.invoke(object, objects);
//拦截器 - 后置处理
interceptor.after();
return result;
});
return enhancer.create();
}
}
class SimpleInterceptor {
public void before() {
System.out.println("-----" + this.getClass().getSimpleName() + "do before" + "-----");
}
public void after() {
System.out.println("-----" + this.getClass().getSimpleName() + "do after" + "-----");
}
public static void main(String[] args) {
//这里是测试
ProxyService proxyService=new ProxyService();
//cglib动态代理
BookServiceBean bookServiceBean= (BookServiceBean) proxyService.cglibProxyObject(new BookServiceBean());
bookServiceBean.create();
//jdk动态代理
Huochepiao huochepiao= (Huochepiao) proxyService.jdkProxyObject(new Tieluju());
huochepiao.buyHuochepiao("feker");
}
}