在前一篇文章中提到静态代理和JDK的动态代理,但是这两种代理都只能对接口实现代理
静态代理的不足
1、需要为真实主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦
2、如果接口有变动,则真实主题和代理类都要修改,不利于系统维护
JDK动态代理的美中不足
由于java不支持多继承,而JDK动态代理的动态生成的代理类必定继承一个共同的父类Proxy,这也就注定了JDK动态代理无法实现对class的动态代理。
动态代理实际上是程序在运行中,根据被代理的接口来动态生成代理类的class文件,并加载class文件运行的过程,通过反编译被生成的$Proxy0.class文件发现:
利用CGLIB实现对Class动态代理
CGLIB动态代理介绍
CGLIB是一个强大的高性能的代码生成包。
可以通过使用CGLIB来为那些没有接口的类创建模仿(mock)对象。
关于CGLIB
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类。除了CGLIB包,脚本语言例如 Groovy和BeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。所以cglib包要依赖于asm包,需要一起导入。
利用CGLIB实现动态代理
下面的例子主要演示通过代理实现简单的 写日志和权限控制
1.定义被代理类
public class BookServiceBean {
public BookServiceBean() {
//Thread.sleep(3000);
System.out.println("实例化BookServiceBean对象");
}
public BookServiceBean(Object o){
System.out.println("模拟初始化资源,等耗时连接");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
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.配置拦截器
public class MyCglibInterceptor implements MethodInterceptor {
private String name;
public MyCglibInterceptor(String name) {
this.name = name ;
}
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
System.out.println("这里可以分别打印日志或者其他操作调用的方法是:" + arg1.getName());
// 用户进行判断
if (!"shli".equals(name)) {
System.out.println("你没有权限!");
return null;
}
Object result = arg3.invokeSuper(arg0, arg2);
return result;
}
}
3.动态生成代理类
//简单的使用cglib
private static void withoutFilter(){
Enhancer en = new Enhancer();
//进行代理
en.setSuperclass(BookServiceBean.class);
//只有用户是 shli的时候才有权限
MyCglibInterceptor interceptor = new MyCglibInterceptor("aclu");
en.setCallback(interceptor);
BookServiceBean service = (BookServiceBean) en.create();
//使用代理 需要执行耗时连接
service.query();
}
CGLIB实现动态代理时的其他常用功能
- CallbackFilter(回调过滤器)
/**
* 方法过滤器(CallbackFilter),CallbackFilte可以明确表明,被代理的类中不同的方法,被哪个拦截器所拦截
*/
public class MyProxyFilter implements CallbackFilter {
public int accept(Method method) {
if(!"query".equalsIgnoreCase(method.getName()))
return 0;
return 1;
}
}
//使用cglib的filter功能
private static void withFilter(){
Enhancer en = new Enhancer();
//进行代理
en.setSuperclass(BookServiceBean.class);
MyCglibInterceptor interceptor = new MyCglibInterceptor("aclu");
//setCallbacks中定义了所使用的拦截器,其中NoOp.INSTANCE是CGlib所提供的实际是一个没有任何操作的拦截器
//他们是有序的,一定要和CallbackFilter里面的顺序一致。上面return返回(0/1)的就是返回的顺序。
//也就是说如果调用query方法就使用NoOp.INSTANCE进行拦截
en.setCallbacks(new Callback[]{interceptor,NoOp.INSTANCE});
en.setCallbackFilter(new MyProxyFilter());
BookServiceBean service = (BookServiceBean) en.create();
//使用代理
service.query();
}
- 延迟加载对象
示例1:
经测试,被懒加载的对象必须保留无参的构造方法,而无参的构造方法会在被懒加载对象生成的时候调用。
所以建议把需要懒加载的逻辑(耗时的操作),放入有参数的构造方法中,并且在loadObject()方法中使用有参数的构造方法初始化对象。
public class ServiceLazyLoader implements LazyLoader{
public Object loadObject() throws Exception {
System.out.println("开始加载服务信息 ...");
BookServiceBean service = new BookServiceBean(new Object());
System.out.println("加载完成....");
return service;
}
}
//简单的使用cglib 包括懒加载
private static void withoutFilter(){
Enhancer en = new Enhancer();
//进行代理
en.setSuperclass(BookServiceBean.class);
//只有用户是 shli的时候才有权限
MyCglibInterceptor interceptor = new MyCglibInterceptor("aclu");
en.setCallback(interceptor);
//延时加载
en.setCallback(new ServiceLazyLoader());
BookServiceBean service = (BookServiceBean) en.create();
//使用代理 需要执行耗时连接
service.query();
}
示例3:
使用Dispatcher和LazyLoader接口,模拟实体信息的懒加载功能
LazyLoader只在第一次访问延迟加载属性时触发代理类回调方法,
而Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法
/**
*其中包含的Info信息 需要懒加载
*infoFromLazyLoader 通过实现LazyLoader接口实现功能
*infoFromDispatcher 通过实现Dispatcher接口实现功能
*/
public class Person {
private String name;
private int id;
private Info infoFromLazyLoader;
private Info infoFromDispatcher;
public Person(){
this.name = "陶醉";
this.id = 1;
this.infoFromLazyLoader = createLazyLoaderInfoBean();
this.infoFromDispatcher = createDispatcherInfoBean();
}
private Info createDispatcherInfoBean() {
Info info = (Info)Enhancer.create(Info.class,new ConcreteClassDispatcher());
return info;
}
protected Info createLazyLoaderInfoBean(){
Info info = (Info)Enhancer.create(Info.class,new ConcreteClassLazyLoader());
return info;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Info getInfoFromLazyLoader() {
return infoFromLazyLoader;
}
public void setInfoFromLazyLoader(Info infoFromLazyLoader) {
this.infoFromLazyLoader = infoFromLazyLoader;
}
public Info getInfoFromDispatcher() {
return infoFromDispatcher;
}
public void setInfoFromDispatcher(Info infoFromDispatcher) {
this.infoFromDispatcher = infoFromDispatcher;
}
}
public class Info {
public Info() {
}
public Info(String ss){
System.out.println("info 构造方法");
}
private String address;
private boolean married;
private int age;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public boolean isMarried() {
return married;
}
public void setMarried(boolean married) {
this.married = married;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class ConcreteClassDispatcher implements Dispatcher{
public Object loadObject() throws Exception {
System.out.println("使用“Dispatcher”开始加载人员的附加信息 ...");
Info info = new Info("ss");
info.setAddress("胡建省");
info.setAge(12);
System.out.println("使用“Dispatcher”加载完成....");
return info;
}
}
public class ConcreteClassLazyLoader implements LazyLoader{
public Object loadObject() throws Exception {
System.out.println("开始加载人员的附加信息 ...");
Info info = new Info("ss");
info.setAddress("胡建省");
info.setAge(12);
System.out.println("加载完成....");
return info;
}
}
//模拟cglib懒加载 的两种情况 分别为:实现LazyLoader 和 实现Dispatcher
//LazyLoader只在第一次访问延迟加载属性时触发代理类回调方法,
//而Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法
private static void testLazy() {
Person person = new Person();
System.out.println(person.getId()+"------"+person.getName());
//获取info属性 LazyLoader
Info info = person.getInfoFromLazyLoader();
//调用info属性
System.out.println("调用info的方法");
System.out.println(info.getAge());
Info info1 = person.getInfoFromLazyLoader();
info1.getAge();
//获取info Dispatcher
Info info2 = person.getInfoFromDispatcher();
info2.getAddress();
Info info3 = person.getInfoFromDispatcher();
info3.getAddress();
}
控制台信息
1------陶醉
调用info的方法
开始加载人员的附加信息 ...
info 构造方法
加载完成....
12
使用“Dispatcher”开始加载人员的附加信息 ...
info 构造方法
使用“Dispatcher”加载完成....
使用“Dispatcher”开始加载人员的附加信息 ...
info 构造方法
使用“Dispatcher”加载完成....
通过控制台可以看出
在调用info的方法时,info才真正的被加载。
LazyLoader只在第一次访问延迟加载属性时触发代理类回调方法,而Dispatcher在每次访问延迟加载属性时都会触发代理类回调方法