Java有三个动态特性:
- 反射
- 注解
- 动态代理
本文主要介绍动态代理的知识。
代理背后一般至少有一个实际对象,代理的外部功能和实际对象一般是一样的,用户与代理打交道,不直接接触实际对象,虽然外部功能和实际对象一样,但代理有它存在的价值:
-节省成本比较高的实际对象的创建开销,按需延迟加载,创建代理时并不真正创建实际对象,而只是保存实际对象的地址,在需要时再加载或创建
- 执行权限检查,代理检查权限后,再调用实际对象
- 屏蔽网络差异和复杂性,代理在本地,而实际对象在其他服务器上,调用本地代理时,本地代理请求其他服务器
首先我们来看看动态代理的定义。
动态代理:
动态代理可以在运行时动态地创建一个类,这个类实现一个或多个接口,在不修改原有类的基础上动态地为该类获取的对象添加方法,修改行为。
为什么要介绍动态代理呢?因为动态代理是面向切面编程(AOP)的基础。是不是很耳熟,没错,相信你在学习Spring的时候已经接触到了Spring AOP的概念了。
什么是Spring AOP呢?
在Java编程中,有很多切面如日志,权限,数据库事务等在程序中都会用到,代码都差不多,但是与具体的业务代码关系不大。
如果在每个地方都写这些代码会显得很冗余,难以维护。
Spring AOP将切面与主体逻辑的业务代码分离。
Spring AOP就是动态代理的应用之一,实际上动态代理广泛应用于各种系统程序、框架和库中,如Spring,Hibernate,Mybatis,Guice等。
动态代理有两种实现方式,一种是Java SDK提供的,另外一种是第三方库如cglib提供的。
介绍这两种方式之前先介绍下静态代理。
静态代理代码结构比较简单,需要有三个要素:
- 共同的接口:定义方法不提供实现,供外部调用;
- 实现类:实现上述接口,提供上述方法的具体实现;
- 代理类:注入的是实现类,调用接口的方法实际是调用实现类的方法的实现。
下面看代码实现:
package com.wgs.静态代理;
/**
* 静态代理
* @author GenshenWang.nomico
* @time 2017/05/28
*
*/
public class StaticProxyDemo {
//公共接口,没有任何实现,必须.
static interface CommonService{
public void sayHello();
}
//公共接口的具体实现
static class RealService implements CommonService{
@Override
public void sayHello() {
System.out.println("hello,静态代理!");
}
}
//产生一个代理类,此类必须实现公共接口,作用:具有sayHello功能的类
static class StaticProxy implements CommonService{
private CommonService realService;
//通过构造器注入接口,实际注入接口的是RealService
public StaticProxy(CommonService service){
this.realService = service;
}
//此处模拟的即为AOP编程思想。sayHello()方法实现不变,只需在其上下加上一些诸如日志事务等代码,
//无需在sayHello()中加上与业务代码无关的代码
@Override
public void sayHello() {
System.out.println("模拟日志输出1.....");
realService.sayHello();
System.out.println("模拟日志输出2.....");
}
}
//测试类
public static void main(String[] args) {
CommonService service = new RealService();
CommonService proxy = new StaticProxy(service);
proxy.sayHello();
}
}
输出:
模拟日志输出1.....
hello,静态代理!
模拟日志输出2.....
在上述例子中我们可以看到,在不修改sayHello()方法源码的情况下即可在其上下加上调试语句,这个过程是通过代理类实现的。
为什么叫静态代理呢?
可以看到我们在程序中固定的为CommonServiceA接口和RealServiceA实现类造出了一个代理类StaticProxy。
缺点就是我们如果再想为CommonServiceB类,CommonServiceC类…实现代理类,必须得另建对应代理类,就会显得很麻烦,这时候就需要动态代理去实现它了。
在静态代理中,代理类Proxy是直接定义在代码中的,而在动态代理中,代理是动态生成的,代码实现如下:
1. 首先需要一个公共接口:
package com.wgs.动态代理;
public interface CommonService {
void sayHello();
}
2. 公共接口的实现类,是被代理的实际对象:
package com.wgs.动态代理;
public class CommonServiceImpl implements CommonService {
@Override
public void sayHello() {
System.out.println("hello 动态代理!");
}
}
3. 动态产生代理类:
package com.wgs.动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理类作用: 为一个真实对象产生一个类。
* 这个类具有真实对象的某个或者所有的功能
* 每一个动态代理类都要实现一个InvocationHandler
* @author GenshenWang.nomico
* @time 2017/05/28
*/
public class DynamicProxyHanlder implements InvocationHandler{
//要代理的真实对象
private Object obj;
//注入的是实际对象
public DynamicProxyHanlder(Object realObj){
this.obj = realObj;
}
/**
* @proxy: Object proxy没有太大作用
* @method: 想要调用的方法
* @args:传入的参数值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("模拟日志输出1.....");
//实际执行的业务代码,可能有返回值.注入实际接口与参数值
method.invoke(obj, args);
System.out.println("模拟日志输出2.....");
return null;
}
}
4. 产生动态代理对象并测试Proxy.newProxyInstance:
package com.wgs.动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* 测试类
* @author GenshenWang.nomico
* @time 2017/05/28
*
*/
public class Client {
public static void main(String[] args) {
//代理的真实对象
CommonService realService = new CommonServiceImpl();
//产生一个代理类
InvocationHandler proxyHandler = new DynamicProxyHanlder(realService);
//产生一个代理对象
CommonService proxyService =
(CommonService) Proxy.newProxyInstance(
CommonService.class.getClassLoader(),
new Class>[]{CommonService.class},
proxyHandler);
proxyService.sayHello();
}
}
这里有两个地方要详细解释下。
1. InvocationHandler
第一个就是生成动态代理类的时候类需要实现InvocationHandler,它的构造方法接受一个参数realObj表示被代理的对象,
invoke方法处理所有的接口调用,它有三个参数:
- proxy:表示代理对象本身,需要注意,它不是被代理的对象,这个参数一般用处不大
- method:表示正在被调用的方法
- args :表示方法的参数
2. Proxy.newProxyInstance
使用java.lang.reflect包中的Proxy类的静态方法newProxyInstance来创建代理对象,方法定义如下:
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
三个参数:
loader:表示类加载器
interfaces:表示代理类要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类,例子中只有一个IService
h:h的类型为InvocationHandler,它是一个接口,也定义在java.lang.reflect包中,它只定义了一个方法invoke,对代理接口所有方法的调用都会转给该方法
注:newProxyInstance的返回值类型为Object,可以强制转换为interfaces数组中的某个接口类型,这里我们强制转换为了IService类型,需要注意的是,它不能强制转换为某个类类型,比如RealService,即使它实际代理的对象类型为RealService。
Java SDK动态代理的局限在于:
它只能为接口创建代理,返回的代理对象也只能转换到某个接口类型,如果一个类没有接口,或者希望代理非接口中定义的方法,那就没有办法了。
有一个第三方的类库cglib(https://github.com/cglib/cglib)可以做到这一点,Spring,Hibernate等都使用该类库。
cglib:
cglib实现机制与Java SDK不同,它是通过继承实现的。
cglib动态地创建了一个类,但这个类的父类是被代理的类,代理类重写了父类所有public非final得方法,改为调用callback中的相关方法。
代码实现:
package com.wgs.动态代理;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 动态代理实现方式二---cglib
* @author GenshenWang.nomico
* @time 2017/5/30
*
*/
public class CGLibDemo {
/**
* 被代理的类,不需要实现接口
* @author GenshenWang.nomico
*
*/
static class RealService{
public void sayHello(){
System.out.println("hello,cglib");
}
}
static class SimpleInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object object, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("模拟日志输出1.。。。");
Object result = proxy.invokeSuper(object, args);
System.out.println("模拟日志输出2.。。。");
return result;
}
}
/**
* 为一个类生产代理对象
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
private static T getProxy(Class clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new SimpleInterceptor());
return (T)enhancer.create();
}
/**
* 测试
*/
public static void main(String[] args) throws Exception {
RealService proxyService = getProxy(RealService.class);
proxyService.sayHello();
}
}
RealService:被代理的类,没有接口。
getProxy() : 为一个类(RealService)生成代理对象;
cglib的Enhancer类:
setSuperclass设置被代理的类;
setCallback设置被代理类的public非final方法被调用时的处理类
Enhancer支持多种类型,这里使用的类实现了MethodInterceptor接口,它与Java SDK中的InvocationHandler有点类似,方法名称变成了intercept,多了一个MethodProxy类型的参数。
与前面的InvocationHandler不同,SimpleInterceptor中没有被代理的对象,它通过MethodProxy的invokeSuper方法调用被代理类的方法:
Object result = proxy.invokeSuper(object, args);
Java SDK代理:面向的是一组接口,
它为这些接口动态创建了一个实现类,接口的具体实现逻辑是通过自定义的InvocationHandler实现的,这个实现是自定义的,也就是说,其背后都不一定有真正被代理的对象,也可能多个实际对象,根据情况动态选择。
cglib代理:面向的是一个具体的类,它动态创建了一个新类,继承了该类,重写了其方法。
参考:http://www.cnblogs.com/swiftma/p/6869790.html