个人理解的代理模式,动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。即不知道真实对象是谁,此时代理对象可以在客户端和真实对象之间起到中介的作用.
- 代理对象完全包含真实对象, 客户端使用的都是代理对象的方法,
- 代理模式的职责: 把不属于真实对象的操作从其身上分开 — 责任分离
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
1.1 JDK 的动态代理 要求真实对象必须有接口.
static InvocationHandler getInvocationHandler(Object proxy)
//返回指定代理实例的调用处理程序。
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
//返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
static boolean isProxyClass(Class<?> cl)
//当且仅当指定的类通过 getProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。
static Object newProxyInstance( ClassLoader loader , Class<?>[] interfaces , InvocationHandler h )
//参数1: 类加载器,一般跟上真实对象的类加载器
//参数2: 真实对象所实现的接口; .getClass().getInterfaces();
//参数3: 如何做增强的对象, 即动态代理类的处理器; 此类需要实现InvocationHandler接口;
一个对象数组包含的值情况代理实例上在方法调用中传递,或null如果接口方法不包含任何参数。原始类型的参数被包装在适当的原始包装类的实例中,例如java.lang。整数或java.lang.Boolean。
直接上代码:
/**
* 创建一个代理对象
* @author LzM
*/
public class SomeClassProxy implements InvocationHandler {
private Object target; //要被代理的真实目标类
public SomeClassProxy( Object target ){
this.target = target;
}
/**
* 创建一个代理对象
* 注意返回的时候要用接口变量. 否则可能会转换异常
* @param <T>
* @return ProxyObject
*/
public <T> T getProxyObject(){
return (T) Proxy.newProxyInstance( target.getClass().getClassLoader(), //真实对象的类加载器
target.getClass().getInterfaces(), //真实对象所实现的接口
this ); //动态代理类的处理器,需实现InvocationHandler接口
}
/**
* 为真实对象做增强的具体操作.这里自定义,可以写多个类的处理器也可以写一个,这里为了演示只写一个
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy类 "+proxy.getClass());
System.out.println("方法名 "+method.getName());
System.out.println("参数列表 "+Arrays.toString(args));
System.out.println("-------------------------------");
System.out.println("开启增强: balabalaba");
try {
//调用 真实对象的方法
method.invoke(target, args);
} catch (Exception e) {
System.err.println("[增强] 出错了!回滚..");
return 0 ;
}finally{
System.out.println("结束..");
}
return null;
}
}
基于 CGLIB动态代理 (可以没有接口)
CGLIB的原理,简单说就是直接继承了需要被增强(代理)的类,并重写其中的关键方法。
/**
* CGLIB - 创建一个代理对象
* @author LzM
*/
public class SomeClassProxy_cg implements org.springframework.cglib.proxy.InvocationHandler {
private Object target; //要被代理的真实目标类
public SomeClassProxy_cg( Object target ){
this.target = target;
}
/**
* 创建一个代理对象
* @param <T>
* @return ProxyObject
*/
public Object getProxyObject(){
Enhancer enhancer = new Enhancer();
//继承那个类去做增强(代理)
enhancer.setSuperclass(target.getClass());
//设置增强处理对象(这里是本身,其他的话就用别的类)
enhancer.setCallback(this);
//返回创建的代理对象
return enhancer.create();
}
/**
* 为真实对象做增强的具体操作
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy类 "+proxy.getClass());
System.out.println("方法名 "+method.getName());
System.out.println("参数列表 "+Arrays.toString(args));
System.out.println("-------------------------------");
System.out.println("开启增强: 拿纸笔");
try {
//调用 真实对象的方法
method.invoke(target, args);
} catch (Exception e) {
System.err.println("[增强] 出错了!回滚..");
return 0 ;
}finally{
System.out.println("结束..");
}
return null;
}
}
---------- 测试 -------------
public static void main(String[] args) {
ZooIMPL_cg zooIMPL = new ZooIMPL_cg();
SomeClassProxy_cg proxy = new SomeClassProxy_cg(zooIMPL);
Zoo_cg proxyObject = (Zoo_cg) proxy.getProxyObject();
proxyObject.getAllannimo(11);
}
举个栗子:这是一个可以唱歌和跳舞的接口。
/**
* 定义了唱歌和跳舞的接口
* @author LzM
*/
public interface Iperson {
/**
* 唱歌
* @param name 歌名
* @return 唱歌的人
*/
String sing(String name);
/**
* 跳舞
* @param name 舞名
* @return 跳舞者
*/
String dance( String name );
//...
}
这是一个具体人,实现了上面的接口:
/**
* 实现person接口的具体类
* @author LzM
*/
public class ZhangSan implements Iperson{
@Override
public String sing(String name) {
System.out.println("张三,开始唱" + name);
return "唱完了。谢谢大家";
}
@Override
public String dance(String name) {
System.out.println("张三跳舞了。舞蹈名:" + name);
return "跳累了。。歇会";
}
}
下面开始给张三一个代理人
/**
* 这个类用来生成张三的代理人。 即代理类
* @author LzM
*/
public class ZhangSanProxy {
//要代理的目标对象 :张三
private ZhangSan zs = new ZhangSan();
public Iperson getProxy(){
Iperson p = (Iperson) Proxy.newProxyInstance (
ZhangSanProxy.class.getClassLoader(),
zs.getClass().getInterfaces(),
new personHandle( zs ));
return p ;
}
}
然后给代理人一个处理方法,即不能直接找张三,得先给代理人付钱。才能找张三
/**
* InvocationHandler接口的实现类
* 用来指明代理对象(即newProxyInstance的第一个参数)要做什么事情。
* 细分的话,在调用前做什么,调用后做什么。
* @author LzM
*/
public class personHandle implements InvocationHandler{
private Object obj = null;
public personHandle( Object o ){
this.obj = o;
}
/**
* 在代理实例上处理方法调用并返回结果。
* 在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//下面是测试打印:
System.out.println("[代理类]"+proxy.getClass().getName());
System.out.println("[方法名]"+method.getName());
System.out.println("[参数]"+Arrays.toString(args)+"\n\n");
//-------------------------------------------------------
Object result = null;//被代理的类型为Object基类
/**
* 这里就可以进行所谓的AOP编程了
* 在调用被代理类的方法之前前,先执行自定义功能处理下
*/
//下面是自定义功能,经纪人要加钱。。
if( method.getName().equals("sing") ){
System.out.println("我是张三经纪人,你要他唱歌先给我10万... ");
System.out.println("给钱中...");
//下面才调用张三的唱歌
return method.invoke( obj, args );
}
if( method.getName().equals("dance") ){
System.out.println("我是张三经纪人,你要他跳舞啊,先给我50万... ");
System.out.println("给钱中...");
//下面才调用张三跳舞
return method.invoke( obj , args );
}
return result;
}
}
测试一下:
/**
* 入口函数
* @author LzM
*/
public class TsetMain {
public static void main(String[] args) {
ZhangSanProxy proxy = new ZhangSanProxy();
Iperson p = proxy.getProxy();
p.dance("小天鹅");
p.sing("不将就");
/*
输出:
[代理类]com.sun.proxy.$Proxy0
[方法名]dance
[参数][小天鹅]
我是张三经纪人,你要他跳舞啊,先给我50万...
给钱中...
张三跳舞了。舞蹈名:小天鹅
[代理类]com.sun.proxy.$Proxy0
[方法名]sing
[参数][不将就]
我是张三经纪人,你要他唱歌先给我10万...
给钱中...
张三,开始唱不将就
*/
}
}