大话 CGLIB 和 JDK 生成动态代理类的那点事

102 篇文章 2 订阅
11 篇文章 1 订阅
AOP 使用的设计模式就是代理模式,是对IOC设计的补充。为了扩展性,往往会加上反射,动态生成字节码,生成代理类。 
这里往往还会使用到DI,把代理的实现类通过依赖注入的方式,传给代理工厂。 
关于生成动态代理类的方式有两种:JDK和CGLIB。  CGLIB,是一个开源工具。spring 和hibernate已经广泛使用。 

关于AOP的概念解释网上有很多。 
附件的内容是关于JDK和CGLIB动态生成代理工厂的实例。 

JDK动态代理和CGLIB字节码生成的区别? 

JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。

CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final。

例子为

@Component("SalesEmployee")

public class SalesDepartEmployee implements employee {

......

}

ApplicationContext ctx = new ClassPathXmlApplicationContext("employee.xml");
employee p=(employee)ctx.getBean("SalesEmployee");


employee  是接口名字。

则程序可以通过proxy-target-class="true" 来指定CGLIB)或proxy-target-class="false" 来指定 JDK proxy。

但是如果代码是这样

SalesDepartEmployee p=(SalesDepartEmployee )ctx.getBean("SalesEmployee");

则只能用CGLIB来生成代理,否则如果JDK proxy,则会出现下面的运行错误:

  java.lang.ClassCastException: 
   com.sun.proxy.$Proxy13 cannot be cast to salesdepart.service.app.SalesDepartEmployee


CGLIB生成代码
Java代码   收藏代码
  1. public Object createProxyObject(Object obj) {  
  2.     this.targetObject = obj;  
  3.     Enhancer enhancer = new Enhancer();  
  4.     enhancer.setSuperclass(obj.getClass());  
  5.     enhancer.setCallback(this);  
  6.     Object proxyObj = enhancer.create();  
  7.     return proxyObj;// 返回代理对象,返回的对象其实就是一个封装了“实现类”的代理类,是实现类的实例。  
  8. }  


JDK生成代码

Java代码   收藏代码
  1. public Object newProxy(Object targetObject) {// 将目标对象传入进行代理  
  2.     this.targetObject = targetObject;  
  3.     return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),  
  4.             targetObject.getClass().getInterfaces(), this);// 返回代理对象  
  5. }  

看上面的代码。在动太生成代理类时,传递的是实现类所所实现的接口argetObject.getClass().getInterfaces()。JDK只能对于接口进行做代理。如果换成类的话,则会抛java.lang.ClassCastException异常。 

下面就来一段代码说明下:

(Spring's configuration file,这里强行使用JDK proxy,见红字配置项)


emploee.xml 

--------------------------------------------------------------

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 指定自动搜索Bean组件、自动搜索切面类 -->
<context:component-scan base-package="salesdepart.service.app">
<context:include-filter type="annotation"
expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
<!-- 启动@AspectJ支持 -->
<aop:aspectj-autoproxy proxy-target-class="false"/>
<context:annotation-config />
</beans>


employee .java

------------------------------------

package salesdepart.service.app;
public interface employee {
 public void displayInfo();
}


SalesDepartEmployee.java

----------------------------------

@Component("SalesEmployee")

public class SalesDepartEmployee implements employee {
 //@Autowired默认按类型装配,@Resource默认按名称装配
private String name;
//默认按类型装配
@Autowired(required=true) 
private DummyDAO mydao;
public SalesDepartEmployee() {
System.out.println(" SalesDepartEmployee() called");
name=" default";
mydao=null;
}
@Override
public void displayInfo() {
 System.out.println("displayInfo() called: this is Sales depart' employee "+name);
}

/**
* class specific method not provide in interface
*/
public void displayExtInfo() {
 System.out.println("displayExtInfo() called ");
}
public void setName(String name) {
this.name=name;
}
public String getName() {
return name;
}
}


BeanTest.java

---------------------------------------------

package salesdepart.service.app;
import org.springframework.context.*;
import org.springframework.context.support.*;
import java.util.*;
public class BeanTest { 
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("employee.xml");
SalesDepartEmployee p=(SalesDepartEmployee)ctx.getBean("SalesEmployee");

System.out.println("Hello: employee proxy class is "+p.getClass());
/**
 * this method can work well for JDK proxy and CGLIB proxy
 */
p.displayInfo();
 
/**
 * this method not provided in interface can work well for  CGLIB proxy ONLY.
 * If set  proxy-target-class="false", then see the below errors:
 *  java.lang.ClassCastException: 
 *  com.sun.proxy.$Proxy13 cannot be cast to salesdepart.service.app.SalesDepartEmployee

 */
p.displayExtInfo();
}
}


运行结果就是这样的

-----------------------

SalesDepartEmployee() called
Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy13 cannot be cast to salesdepart.service.app.SalesDepartEmployee
at salesdepart.service.app.BeanTest.main(BeanTest.java:10)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值