动态代理指在程序运行期间JVM根据需要通过反射等机制动态地创建代理类及其代理对象,这种代理有如下特点:代理类是在程序运行期间创建,可以在classpath目录(在Eclipse中,Java工程中的bin目录;Web工程中的build目录)中看到该文件;代理类和委托类的关系是在程序运行时确定的。
场景:
企业请明星唱歌或者导演请明星拍摄电影或电视剧,一般先通过其经纪人进行协商,然后由经纪人告知明星,明星同意后进行演出,最后经纪人处理善后事宜。
JDK动态代理
程序执行时使用java.lang.reflect包中Proxy类与InvocationHandler接口动态地生成一个实现代理接口的匿名代理类及其对象,无论调用代理对象哪个方法,最终都会执行invoke方法,示例如下:
/**
* 实现类(也称目标类或委托类)和代理类须实现的接口
*
* @author GaoHuanjie
*/
public interface IWork {
/**
* 唱歌方面
*
* @author GaoHuanjie
*/
void sing();
/**
* 演出(比如拍摄电影,演电视剧)方面
*
* @author GaoHuanjie
*/
void play(int option);
}
/**
* 实现类(也称目标类或委托类),该类实现IWork接口
*
* @author GaoHuanjie
*/
public class Star implements IWork{
private String name;//名字
public void setName(String name) {
this.name = name;
}
/**
* 唱歌方面,很有天赋,声音动听
*
* @author GaoHuanjie
*/
@Override
public void sing() {
try {
System.out.println(name+"正在唱歌......");
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 演出(比如拍摄电影,演电视剧)方面,惟妙惟肖,形象逼真
*
* @author GaoHuanjie
*/
@Override
public void play(int option) {
try {
switch (option) {
case 1:
System.out.println(name+"正在拍摄电影......");
break;
case 2:
System.out.println(name+"正在演电视剧......");
}
Thread.sleep(30000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工厂
*
* @author GaoHuanjie
*/
public class AgentFactory{
private static Object target;//实现类(也称目标类或委托类)对象,即代理谁
private static InvocationHandler handler = new InvocationHandler() {
/**
* 代理对象要做什么事情
*
* proxy:动态代理对象
* method:接口中定义的抽象方法
* args:抽象方法需要传入的参数列表
*
* @author GaoHuanjie
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if("sing".equals(method.getName())){
System.out.println("和客户协商,确定演出时间!");
System.out.println("和客户协商,确定演出地点!");
System.out.println("和客户协商,确定安保措施!");
System.out.println("和客户协商,确定出场费金额!");
result = method.invoke(target, args);//唱歌,调用真正的业务方法
System.out.println("收取客户出场费!");
} else if("play".equals(method.getName())) {
System.out.println("和导演协商,确定扮演角色!");
System.out.println("和导演协商,确定拍摄时段!");
System.out.println("和导演协商,确定拍摄地点!");
System.out.println("和导演协商,确定安保措施!");
System.out.println("和导演协商,确定片酬!");
result = method.invoke(target, args);//演出,调用真正的业务方法
System.out.println("收取导演片酬!");
}
return result;
}
};
/**
* 动态产生代理对象
*
* @author GaoHuanjie
*/
public static Object getAgent(Object target) {
AgentFactory.target = target;// 绑定业务实现类对象
ClassLoader classLoader = target.getClass().getClassLoader();// 使用哪个类装载器生成代理对象
Class>[] interfaces = target.getClass().getInterfaces();// 代理对象所实现的接口,代理对象与目标对象都要实现的接口
return Proxy.newProxyInstance(classLoader, interfaces, handler);// 创建代理对象
}
}
public class Test {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");//生成代理类的class文件
Star atar = new Star();
atar.setName("刘德华");
IWork agent = (IWork)AgentFactory.getAgent(atar);// 创建代理对象
agent.sing();
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~");
agent.play(1);
}
}
CGLib动态代理
程序执行时通过ASM(开源的Java字节码编辑库,操作字节码)jar包动态地为被代理类生成一个代理子类,通过该代理子类创建代理对象,由于存在继承关系,所以父类不能使用final修饰,示例如下:
/**
* 目标类(也称委托类)
*
* @author GaoHuanjie
*/
public class Star{
private String name;//名字
public void setName(String name) {
this.name = name;
}
/**
* 唱歌方面,很有天赋,声音动听
*
* @author GaoHuanjie
*/
public void sing() {
try {
System.out.println(name+"正在唱歌......");
Thread.sleep(10000);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 演出(比如拍摄电影,演电视剧)方面,惟妙惟肖,形象逼真
*
* @author GaoHuanjie
*/
public void play(int option) {
try {
switch (option) {
case 1:
System.out.println(name+"正在拍摄电影......");
break;
case 2:
System.out.println(name+"正在演电视剧......");
}
Thread.sleep(30000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 代理工厂
*
* @author GaoHuanjie
*/
public class AgentFactory {
private static Callback callback = new MethodInterceptor() {
/**
* 代理逻辑方法
*
* @param proxy 代理对象
* @param method 方法
* @param args 方法参数
* @param methodProxy 方法代理
*/
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
if("sing".equals(method.getName())){
System.out.println("和客户协商,确定演出时间!");
System.out.println("和客户协商,确定演出地点!");
System.out.println("和客户协商,确定安保措施!");
System.out.println("和客户协商,确定出场费金额!");
result = methodProxy.invokeSuper(proxy, args);// 调用业务类(父类)方法
System.out.println("收取客户出场费!");
} else if("play".equals(method.getName())) {
System.out.println("和导演协商,确定扮演角色!");
System.out.println("和导演协商,确定拍摄时段!");
System.out.println("和导演协商,确定拍摄地点!");
System.out.println("和导演协商,确定安保措施!");
System.out.println("和导演协商,确定片酬!");
result = methodProxy.invokeSuper(proxy, args);// 调用业务类(父类)方法
System.out.println("收取导演片酬!");
}else if("setName".equals(method.getName())) {
result = methodProxy.invokeSuper(proxy, args);
}
return result;
}
};
/**
* 获取CGLib代理对象
*
* @author GaoHuanjie
*/
public static Object getAgent(Class> clazz) {
Enhancer enhancer = new Enhancer(); // 创建字节码增强器,用来创建动态代理类
enhancer.setSuperclass(clazz); // 指定代理类对应父类
// 设置回调:此时代理类上所有方法的调用都会调用MethodInterceptor接口中intercept()抽象方法
enhancer.setCallback(callback);
// 创建动态代理类对象并返回
return enhancer.create();
}
}
public class Test {
public static void main(String[] args) {
Star agent = (Star)AgentFactory.getAgent(Star.class);
agent.setName("刘德华");
agent.sing();
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~");
agent.play(1);
}
}
JDK动态代理与CGLib动态代理区别:
1、JDK动态代理基于接口实现,所以实现JDK动态代理,必须先定义接口;CGLib动态代理基于类实现;
2、JDK动态代理机制是委托机制,委托hanlder调用原始实现类方法;CGLib则使用继承机制,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。
动态代理优点:
1、静态代理在程序执行前需手动创建代理类,如果需要很多代理类,每一个都手动创建不仅浪费时间,而且可能产生大量重复性代码,此时我们就可以采用动态代理。
2、动态代理通过InvocationHandler接口invoke方法或MethodInterceptor接口intercept方法为被代理对象中的方法增加额外功能,这种方式比静态代理中通过代理类逐一为被代理对象中的方法增加额外功能,更加的灵活。