- 浏览: 77646 次
- 性别:
- 来自: 上海
文章分类
最新评论
java动态代理——JDK和CGLIB原理解析与使用
CGLIB的动态代理
原理
代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,当它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用CGLIB包。
二、什么是cglib
CGLIB是一个强大的高性能的代码生成包。
1>它广泛的被许多AOP的框架使用,例如:springAOP和dynaop,为他们提供方法的interception(拦截);
2>hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的);
3>EasyMock和jMock是通过使用模仿(moke)对象来测试Java代码的包。
它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
三、底层
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类。除了CGLIB包,脚本语言例如 Groovy和BeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。所以cglib包要依赖于asm包,需要一起导入。下图为cglib与一些框架和语言的关系(CGLIB Library and ASM Bytecode Framework)
Spring AOP和Hibernate同时使用JDK的动态代理和CGLIB包。Spring AOP,如果不强制使用CGLIB包,默认情况是使用JDK的动态代理来代理接口。
四、实例场景模拟
1. 我们创建一个对Table操作的DAO类,提供了CRUD方法。
BookServiceBean.java
- packagecom.tech.cglibx;
- publicclassBookServiceBean{
- publicvoidcreate(){
- System.out.println("create()isrunning!");
- }
- publicvoidquery(){
- System.out.println("query()isrunning!");
- }
- publicvoidupdate(){
- System.out.println("update()isrunning!");
- }
- publicvoiddelete(){
- System.out.println("delete()isrunning!");
- }
- }
OK,它就是一个javaBean,提供了CRUD方法的javaBean。
下面我们创建一个DAO工厂,用来生成DAO实例。
- packagecom.tech.cglibx;
- publicclassBookServiceFactory{
- privatestaticBookServiceBeanservice=newBookServiceBean();
- privateBookServiceFactory(){
- }
- publicstaticBookServiceBeangetInstance(){
- returnservice;
- }
- }
接下来我们创建客户端,用来调用CRUD方法。
- publicclassClient{
- publicstaticvoidmain(String[]args){
- BookServiceBeanservice=BookServiceFactory.getInstance();
- doMethod(service);
- }
- publicstaticvoiddoMethod(BookServiceBeanservice){
- service.create();
- service.update();
- service.query();
- service.delete();
- }
- }
OK,完成了,CRUD方法完全被调用了。
当然这里并没有CGlib的任何内容。问题不会这么简单的就结束,新的需求来临了。
2. one day,Boss告诉我们这些方法不能开放给用户,只有“boss”才有权使用。怎么办,难道我们要在每个方法上面进行判断吗?好像这么做也太那啥了吧?对了,Proxy可能是最好的解决办法。jdk的代理就可以解决了。 好了我们来动手改造吧。等等jdk的代理需要实现接口,这样, 我们的dao类需要改变了。既然不想改动dao又要使用代理,我们这就请出CGlib。
我们只需新增一个权限验证的方法拦截器。
- packagecom.tech.cglibx;
- importjava.lang.reflect.Method;
- importnet.sf.cglib.proxy.Enhancer;
- importnet.sf.cglib.proxy.MethodInterceptor;
- importnet.sf.cglib.proxy.MethodProxy;
- importorg.apache.log4j.Logger;
- publicclassMyCglibProxyimplementsMethodInterceptor{
- privateLoggerlog=Logger.getLogger(MyCglibProxy.class);
- publicEnhancerenhancer=newEnhancer();
- privateStringname;
- publicMyCglibProxy(Stringname){
- this.name=name;
- }
- /**
- *根据class对象创建该对象的代理对象
- *1、设置父类;2、设置回调
- *本质:动态创建了一个class对象的子类
- *
- *@paramcls
- *@return
- */
- publicObjectgetDaoBean(Classcls){
- enhancer.setSuperclass(cls);
- enhancer.setCallback(this);
- returnenhancer.create();
- }
- @Override
- publicObjectintercept(Objectobject,Methodmethod,Object[]args,
- MethodProxymethodProxy)throwsThrowable{
- log.info("调用的方法是:"+method.getName());
- //用户进行判断
- if(!"张三".equals(name)){
- System.out.println("你没有权限!");
- returnnull;
- }
- Objectresult=methodProxy.invokeSuper(object,args);
- returnresult;
- }
- }
当然不能忘了对我们的dao工厂进行修改,我们提供一个使用代理的实例生成方法。上面的类中已经提供了一个通用的获取代理实例的方法,没有特殊需求(如下3)的方式可以使用上面的方式获取代理对象。
- publicstaticBookServiceBeangetProxyInstance(MyCglibProxymyProxy){
- Enhanceren=newEnhancer();
- //进行代理
- en.setSuperclass(BookServiceBean.class);
- en.setCallback(myProxy);
- //生成代理实例
- return(BookServiceBean)en.create();
- }<spanstyle="font-family:Arial,Verdana,sans-serif;white-space:normal;"></span>
我们这就可以看看客户端的实现了。添加了两个方法用来验证不同用户的权限
- BookServiceBeanservice=BookServiceFactory.getProxyInstance(newMyCglibProxy("boss"));
- service.create();
- BookServiceBeanservice2=BookServiceFactory.getProxyInstance(newMyCglibProxy("john"));
- service2.create();
OK,"boss"的正常执行,"john"的没有执行。
看到了吗?简单的aop就这样实现了
难道就这样结束了么?
3.grd Boss又来训话了,不行不行,现在除了"boss"其他人都用不了了,现在不可以这样。必须使用开放查询功能。
哈哈,现在可难不倒我们了,因为我们使用了CGlib。当然最简单的方式是去修改我们的方法拦截器,不过这样会使逻辑变得复杂,且不利于维护。
还好CGlib给我们提供了方法过滤器(CallbackFilter),CallbackFilte可以明确表明,被代理的类中不同的方法,被哪个拦截器所拦截。
下面我们就来做个过滤器用来过滤query方法。
- packagecom.tech.cglibx;
- importjava.lang.reflect.Method;
- importnet.sf.cglib.proxy.CallbackFilter;
- publicclassMyProxyFilterimplementsCallbackFilter{
- @Override
- publicintaccept(Methodarg0){
- if(!"query".equalsIgnoreCase(arg0.getName()))
- return0;
- return1;
- }
- }
我们在工场中新增一个使用了过滤器的实例生成方法。
- publicstaticBookServiceBeangetAuthInstanceByFilter(MyCglibProxymyProxy){
- Enhanceren=newEnhancer();
- en.setSuperclass(BookServiceBean.class);
- en.setCallbacks(newCallback[]{myProxy,NoOp.INSTANCE});
- en.setCallbackFilter(newMyProxyFilter());
- return(BookServiceBean)en.create();
- }
setCallbacks中定义了所使用的拦截器,其中NoOp.INSTANCE是CGlib所提供的实际是一个没有任何操作的拦截器,
他们是有序的,一定要和CallbackFilter里面的顺序一致。上面return返回(0/1)的就是返回的顺序。也就是说如果调用query方法就使用NoOp.INSTANCE进行拦截。
现在看一下客户端代码。
- BookServiceBeanservice=BookServiceFactory.getProxyInstanceByFilter(newMyCglibProxy("jhon"));
- service.create();
- BookServiceBeanservice2=BookServiceFactory.getProxyInstanceByFilter(newMyCglibProxy("jhon"));
- service2.query();
ok,现在"李四"也可以使用query方法了,其他方法仍然没有权限。
当然这个代理的实现没有任何侵入性,无需强制让dao去实现接口。
二、CGLib动态代理使用
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
简单的实现举例:
这是一个需要被代理的类,也就是父类,通过字节码技术创建这个类的子类,实现动态代理。
- publicclassSayHello{
- publicvoidsay(){
- System.out.println("helloeveryone");
- }
- }
- publicclassCglibProxyimplementsMethodInterceptor{
- privateEnhancerenhancer=newEnhancer();
- publicObjectgetProxy(Classclazz){
- //设置需要创建子类的类
- enhancer.setSuperclass(clazz);
- enhancer.setCallback(this);
- //通过字节码技术动态创建子类实例
- returnenhancer.create();
- }
- //实现MethodInterceptor接口方法
- publicObjectintercept(Objectobj,Methodmethod,Object[]args,
- MethodProxyproxy)throwsThrowable{
- System.out.println("前置代理");
- //通过代理类调用父类中的方法
- Objectresult=proxy.invokeSuper(obj,args);
- System.out.println("后置代理");
- returnresult;
- }
- }
具体实现类:
- publicclassDoCGLib{
- publicstaticvoidmain(String[]args){
- CglibProxyproxy=newCglibProxy();
- //通过生成子类的方式创建代理类
- SayHelloproxyImp=(SayHello)proxy.getProxy(SayHello.class);
- proxyImp.say();
- }
- }
输出结果:
- 前置代理
- helloeveryone
- 后置代理
三、JAVA的代理模式
代理模式
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
首先看一下静态代理:
1、Count.java
- packagenet.battier.dao;
- /**
- *定义一个账户接口
- *
- *@authorAdministrator
- *
- */
- publicinterfaceCount{
- //查看账户方法
- publicvoidqueryCount();
- //修改账户方法
- publicvoidupdateCount();
- }
2、CountImpl.java
- packagenet.battier.dao.impl;
- importnet.battier.dao.Count;
- /**
- *委托类(包含业务逻辑)
- *
- *@authorAdministrator
- *
- */
- publicclassCountImplimplementsCount{
- @Override
- publicvoidqueryCount(){
- System.out.println("查看账户方法...");
- }
- @Override
- publicvoidupdateCount(){
- System.out.println("修改账户方法...");
- }
- }
- 、CountProxy.java
- packagenet.battier.dao.impl;
- importnet.battier.dao.Count;
- /**
- *这是一个代理类(增强CountImpl实现类)
- *
- *@authorAdministrator
- *
- */
- publicclassCountProxyimplementsCount{
- privateCountImplcountImpl;
- /**
- *覆盖默认构造器
- *
- *@paramcountImpl
- */
- publicCountProxy(CountImplcountImpl){
- this.countImpl=countImpl;
- }
- @Override
- publicvoidqueryCount(){
- System.out.println("事务处理之前");
- //调用委托类的方法;
- countImpl.queryCount();
- System.out.println("事务处理之后");
- }
- @Override
- publicvoidupdateCount(){
- System.out.println("事务处理之前");
- //调用委托类的方法;
- countImpl.updateCount();
- System.out.println("事务处理之后");
- }
- }
3、TestCount.java
- packagenet.battier.test;
- importnet.battier.dao.impl.CountImpl;
- importnet.battier.dao.impl.CountProxy;
- /**
- *测试Count类
- *
- *@authorAdministrator
- *
- */
- publicclassTestCount{
- publicstaticvoidmain(String[]args){
- CountImplcountImpl=newCountImpl();
- CountProxycountProxy=newCountProxy(countImpl);
- countProxy.updateCount();
- countProxy.queryCount();
- }
- }
观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。
再来看一下动态代理:
JDK动态代理中包含一个类和一个接口:
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数
可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
Ps:类加载器
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器;
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。
动态代理
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
动态代理示例:
1、BookFacade.java
- packagenet.battier.dao;
- publicinterfaceBookFacade{
- publicvoidaddBook();
- }
2、BookFacadeImpl.java
- packagenet.battier.dao.impl;
- importnet.battier.dao.BookFacade;
- publicclassBookFacadeImplimplementsBookFacade{
- @Override
- publicvoidaddBook(){
- System.out.println("增加图书方法。。。");
- }
- }
- 、BookFacadeProxy.java
- packagenet.battier.proxy;
- importjava.lang.reflect.InvocationHandler;
- importjava.lang.reflect.Method;
- importjava.lang.reflect.Proxy;
- /**
- *JDK动态代理代理类
- *
- *@authorstudent
- *
- */
- publicclassBookFacadeProxyimplementsInvocationHandler{
- privateObjecttarget;
- /**
- *绑定委托对象并返回一个代理类
- *@paramtarget
- *@return
- */
- publicObjectbind(Objecttarget){
- this.target=target;
- //取得代理对象
- returnProxy.newProxyInstance(target.getClass().getClassLoader(),
- target.getClass().getInterfaces(),this);//要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
- }
- @Override
- /**
- *调用方法
- */
- publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)
- throwsThrowable{
- Objectresult=null;
- System.out.println("事物开始");
- //执行方法
- result=method.invoke(target,args);
- System.out.println("事物结束");
- returnresult;
- }
- }
3、TestProxy.java
- packagenet.battier.test;
- importnet.battier.dao.BookFacade;
- importnet.battier.dao.impl.BookFacadeImpl;
- importnet.battier.proxy.BookFacadeProxy;
- publicclassTestProxy{
- publicstaticvoidmain(String[]args){
- BookFacadeProxyproxy=newBookFacadeProxy();
- BookFacadebookProxy=(BookFacade)proxy.bind(newBookFacadeImpl());
- bookProxy.addBook();
- }
- }
但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。
Cglib动态代理
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
示例
1、BookFacadeCglib.java
- packagenet.battier.dao;
- publicinterfaceBookFacade{
- publicvoidaddBook();
- }
2、BookCadeImpl1.java
- packagenet.battier.dao.impl;
- /**
- *这个是没有实现接口的实现类
- *
- *@authorstudent
- *
- */
- publicclassBookFacadeImpl1{
- publicvoidaddBook(){
- System.out.println("增加图书的普通方法...");
- }
- }
3、BookFacadeProxy.java
- packagenet.battier.proxy;
- importjava.lang.reflect.Method;
- importnet.sf.cglib.proxy.Enhancer;
- importnet.sf.cglib.proxy.MethodInterceptor;
- importnet.sf.cglib.proxy.MethodProxy;
- /**
- *使用cglib动态代理
- *
- *@authorstudent
- *
- */
- publicclassBookFacadeCglibimplementsMethodInterceptor{
- privateObjecttarget;
- /**
- *创建代理对象
- *
- *@paramtarget
- *@return
- */
- publicObjectgetInstance(Objecttarget){
- this.target=target;
- Enhancerenhancer=newEnhancer();
- enhancer.setSuperclass(this.target.getClass());
- //回调方法
- enhancer.setCallback(this);
- //创建代理对象
- returnenhancer.create();
- }
- @Override
- //回调方法
- publicObjectintercept(Objectobj,Methodmethod,Object[]args,
- MethodProxyproxy)throwsThrowable{
- System.out.println("事物开始");
- proxy.invokeSuper(obj,args);
- System.out.println("事物结束");
- returnnull;
- }
- }
4、TestCglib.java
- packagenet.battier.test;
- importnet.battier.dao.impl.BookFacadeImpl1;
- importnet.battier.proxy.BookFacadeCglib;
- publicclassTestCglib{
- publicstaticvoidmain(String[]args){
- BookFacadeCglibcglib=newBookFacadeCglib();
- BookFacadeImpl1bookCglib=(BookFacadeImpl1)cglib.getInstance(newBookFacadeImpl1());
- bookCglib.addBook();
- }
- }
相关推荐
【标题】:“cglib——Java字节码生成库”是一个强大的工具,用于在运行时动态创建和修改类的字节码。它是一个广泛应用于Java开发中的库,特别是那些需要底层控制对象实例化、方法调用等场景的项目。 【描述】:...
Spring AOP主要通过动态代理机制实现,根据目标对象是否实现了接口,它会使用JDK动态代理或CGLIB代理。当目标对象是接口类型时,Spring会使用JDK的Proxy类生成一个实现了该接口的代理对象;若目标对象没有接口,...
它可以用于远程代理、虚拟代理、保护代理等,如Java的JDK动态代理和CGLIB代理。 7. **适配器模式(Adapter)** 适配器模式使两个不兼容的接口能够协同工作。它通过包装另一个对象,使得原本不兼容的接口得以转换,...
《Spring技术内幕——深入解析Spring架构与设计原理》是一本深度剖析Spring框架核心机制与设计理念的专业书籍。本书旨在帮助读者全面理解Spring的内部工作原理,从而更好地应用和优化Spring框架在实际开发中的使用。...
5. **Spring AOP的实现机制**: Spring支持两种代理模式——JDK动态代理和CGLIB代理。当目标对象实现了接口时,Spring使用JDK动态代理;否则,使用CGLIB生成一个子类来实现AOP。了解这两种代理模式的原理对于优化性能...
Spring支持两种类型的代理:JDK动态代理和CGLIB动态代理。前者适用于接口实现类,后者则可以处理没有接口的类。AOP的实现主要包括定义切面、通知类型(前置、后置、异常、最终、环绕通知)、连接点、切点和织入等...
Spring使用CGLIB库或JDK的动态代理来创建AOP代理。当目标对象实现了一个或多个接口时,JDK动态代理会被使用;若目标对象没有实现任何接口,则使用CGLIB生成子类进行代理。动态代理的优势在于无需显式创建代理类,...
根据目标对象是否实现了接口,Spring提供了两种代理方式:JDK动态代理和CGLIB动态代理。前者适用于目标对象实现了接口的情况,通过反射创建一个代理类实现接口并覆盖所有方法,加入切面逻辑;后者则用于未实现接口的...
5. **AOP代理**:Spring提供两种类型的AOP代理——JDK动态代理和CGLIB代理。通过代理,Spring能在方法调用前后插入拦截器,实现AOP功能。 6. **Spring MVC**:Spring的Web MVC框架提供了一个模型-视图-控制器架构,...
8. **AOP 代理**:Spring 使用两种代理模式——JDK 动态代理和 CGLIB 代理,根据目标类是否实现了接口来选择。源码中,`AbstractAutoProxyCreator` 负责创建代理对象。 9. **事件驱动架构**:Spring 支持事件发布和...
5. **AOP代理**:Spring提供了两种AOP代理——JDK动态代理和CGLIB代理。前者用于接口实现类,后者用于无接口类。代理允许在方法调用前后插入额外的行为。 6. **数据访问集成**:Spring提供了对各种数据库访问技术的...
在Spring中,AOP主要通过代理模式实现,分为JDK动态代理和CGLIB代理两种。`org.springframework.aop`和`org.springframework.aop.framework`包下的类,如`Advised`、`Advisor`、`Pointcut`和`ProxyFactoryBean`,...
CGLib与动态代理的区别 - CGLib可以代理任何类,即使该类没有实现接口。 #### 16. CGlib实现接口代理的可能性 - 由于CGLib本质上是对类进行字节码增强的技术,因此它可以直接代理类而不是接口。 #### 17. final...
在Spring中,AOP通过代理模式来实现,有JDK动态代理和CGLIB两种方式。JDK动态代理适用于实现了接口的类,而CGLIB则用于没有实现接口的类。`spring_aop_proxy`目录下的源码展示了Spring如何创建和管理这些代理,以及...
AOP通过动态代理实现,Spring提供了两种代理方式:JDK动态代理和CGLIB代理。JDK代理适用于实现了接口的类,而CGLIB代理则用于未实现接口的类。 Spring还提供了丰富的模块支持,如数据访问/集成(包括JDBC、ORM、OXM...
- **代理模式**:Spring AOP主要采用了JDK动态代理和CGLIB代理两种方式来实现AOP。 - **切面(Aspect)**:定义了横切关注点的模块,例如日志记录、性能统计、安全控制等。 - **连接点(Joinpoint)**:程序执行过程...
Spring支持JDK动态代理和CGLIB代理,用于在方法调用前后插入自定义代码,如日志记录、性能监控等。 2. `pom.xml`:这是Maven项目对象模型的配置文件,很可能包含了一个Spring项目的依赖管理,让我们能了解项目所需...
Spring AOP通过代理模式实现,可以是JDK动态代理或CGLIB代理,具体使用哪种取决于目标对象是否实现了接口。理解Spring AOP的实现原理对于优化代码和编写更高效的切面至关重要。 接着,Spring MVC是构建Web应用的...
AOP通过代理模式实现,Spring支持JDK动态代理和CGLIB字节码生成两种方式。 再来,Spring MVC是Spring用于构建Web应用的模块,它提供了模型-视图-控制器的架构。请求被路由到控制器,控制器处理请求并调用业务逻辑,...
1. **AOP代理**:Spring AOP支持两种代理方式,即JDK动态代理和CGLIB代理。JDK代理用于实现接口的类,而CGLIB代理则用于未实现接口的类。spring-aop.jar包含了这些代理机制的相关类,如`org.springframework.aop....