java本身提供了一种代理机制,何为代理?通俗来讲就是把 一个类 用 另外一个类 替代了。
初始代理机制
让我们通过一个例子来初步了解代理机制吧
首先提供一个接口 ISomeClass.java
public interface ISomeClass
{
String doDealString(String mess);
int doDealInt(int num);
}
提供一个 SomeClass.java 实现上面的接口
public class SomeClass implements ISomeClass
{
public SomeClass()
{
}
@Override
public String doDealString(String mess)
{
return "[" + mess + "]";
}
@Override
public int doDealInt(int num)
{
return num + 1;
}
}
给出一个 DirectJDKProxy.java 来品味一下 代理机制
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DirectJDKProxy
{
public static Object getProxy(Object object)
{
Class<?> klass=object.getClass();
// 获取类加载器
ClassLoader classLoader=klass.getClassLoader();
// 获取类所有的接口
Class<?>[] interfaces=klass.getInterfaces();
return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.println("要执行[" + method.getName() + "]方法了");
// 以下三行证明DoDealString方法 被 invoke方法干预了
String arg0=(String) args[0];
arg0="(" + arg0 + ")";
args[0]=arg0;
Object result=method.invoke(object, args);
System.out.println("执行完[" + method.getName() + "]方法了");
return result;
}
});
}
public static void main(String[] args)
{
SomeClass someClass=new SomeClass();
// 获取someClass(接口类型的)的代理对象
ISomeClass someClassProxy=(ISomeClass) getProxy(someClass);
// 这里的DoDealString方法 被 invoke方法干预了
String str=someClassProxy.doDealString("这是原始参数");
System.out.println("执行结果:" + str);
}
}
*invoke更改了函数执行的手段。我们也可以对它进行分析,如果它是合理的,继续往下走;如果不合理,拦截下来不再执行。
*发现用户执行了一个 不该执行的(权限不允许的)操作,但居然还执行了,就可以通过一套检查,检测到非法,就可以拦截操作,而且,拦截操作是不用修改 SomeClass.java 的代码的,这叫“非侵入式的更改” !!!不会影响SomeClass的逻辑,但是可以获取返回值,对返回值进行操作。
使用场合:权限管理 日志管理
有关日志(log4j.properties)
所谓的日志是指程序中专门写部分代码,将某些信息写到外存。比如一个用户登陆后查看敏感信息,就可以将某时某分某人信息写到外存里,保存到一个文件里面。如果对非敏感信息进行了敏感操作(比如删除),也要记录下来,将某时某分某人信息写到外存里,保存到一个文件里面。这样的信息有助于破案。如果对删除操作进行了日志,还可以对日志进行反过程,恢复数据。
异常如果被截获下来,就可以知道那个类的那个方法发生了异常,以及异常如果一层一层抛上去的,对调试很有意义。尤其是尚未被发现的错误。
一旦出了问题,相关的开发商可以要求把相关的 日志文件 发过去,开发商可以根据日志里面记载的东西精确地找到错误所在,还原错误场景。
执行结果如下:
JDK代理机制
特点:必须实现接口,而且只能拦截接口方法
基于上面的例子进行修改SomeClass.java
public class SomeClass implements ISomeClass
{
public SomeClass()
{}
@Override
public String doDealString(String mess)
{
System.out.println("执行方法中……");
return "[" + mess + "]";
}
@Override
public int doDealInt(int num)
{
return num + 1;
}
public void doSomething()
{
System.out.println("这个方法在JDK代理机制中不会被拦截!");
}
}
再提供一个SJDKProxy.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SJDKProxy
{
private Object object;
public SJDKProxy()
{}
public Object getObject()
{
return object;
}
public void setObject(Object object)
{
this.object=object;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> klass)
{
try
{
Object thisObject=klass.newInstance();
return (T) innerProxy(thisObject);
}
catch(InstantiationException e)
{
e.printStackTrace();
}
catch(IllegalAccessException e)
{
e.printStackTrace();
}
return null;
}
private Object innerProxy(Object object)
{
Class<?> klass=object.getClass();
return Proxy.newProxyInstance(klass.getClassLoader(), klass.getInterfaces(), new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.println("前置拦截");
Object result=method.invoke(object, args);
System.out.println("后置拦截");
return result;
}
});
}
@SuppressWarnings("unchecked")
public <T> T getProxy()
{
if(object == null)
{
return null;
}
return (T) innerProxy(object);
}
}
DirectJDKProxy.java 改动部分如下
public static void main(String[] args)
{
SomeClass someClass=new SomeClass();
SJDKProxy sjdk=new SJDKProxy();
ISomeClass some=sjdk.getProxy(SomeClass.class);
System.out.println(some.doDealString("abcd"));
someClass.doSomething();
}
cglib 代理机制
特点:
相关的方法不能是final,不能把 Complex.java 里的方法改成final。
通过生成子类
首先给出Complex.java
然后给出SCglibProxy.java
import java.lang.reflect.Method;
import about_proxy.jdk.core.Complex;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class SCglibProxy
{
private Object object;
public SCglibProxy()
{}
public Object getObject()
{
return object;
}
public void setObject(Object object)
{
this.object=object;
}
private Object innerProxy(Object object)
{
Class<?> parentClass=object.getClass();
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(parentClass);
// MethodInterceptor方法拦截器
enhancer.setCallback(new MethodInterceptor()
{
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable
{
System.out.println("proxy的类型:" + proxy.getClass());
System.out.println("methodProxy的类型:" + methodProxy.getClass());
System.out.println("proxy instanceof parentClass:" + (proxy instanceof Complex));
Object result=null;
System.out.println("前置拦截!");
result=method.invoke(object, args);
System.out.println("后置拦截!");
return result;
}
});
return enhancer.create();
}
@SuppressWarnings("unchecked")
public <T> T getPorxy()
{
return object == null ? null : ((T) innerProxy(object));
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> klass)
{
try
{
Object object=klass.newInstance();
return (T) innerProxy(object);
}
catch(InstantiationException e)
{
e.printStackTrace();
}
catch(IllegalAccessException e)
{
e.printStackTrace();
}
return null;
}
}
DirectJDKProxy.java 改动部分如下
public static void main(String[] args)
{
SCglibProxy sCglibProxy=new SCglibProxy();
Complex complex1=sCglibProxy.getProxy(Complex.class);
System.out.println(complex1.toString());
SCglibProxy cglibProxy=new SCglibProxy();
Complex complex2=cglibProxy.getProxy(Complex.class);
System.out.println(complex2.toString());
}
运行结果如下:
以上代码还有一些不足:
增加多个拦截 —— 拦截器链
还可以针对方法,进行各种拦截
前置拦截和后置拦截应该写成接口,让用户实现,
考虑增加异常拦截
怎样结合上面两个方法