Java中利用代理(Proxy)可以在运行时创建一个实现了一组给定接口的新类spring用的就是这个原理,如何没有实现特定接口可以通过CGLIB库来创建。
Proxy类的特性:
1.代理(Proxy)类只有一个实例变量,即调用处理器(InvocationHandler)
2.代理类需要的额外数据都必须存储在调用处理器中
3.代理类一定是public和final.
4.如果代理类实现的所有接口都是public,则代理类就不属于某个特定的包;否则所有的非公有接口都必须属于同一个包,代理类也属于这个包(此设定的目的是确定代理类的所属包)
public class ProxyTest {
public static void main(String[] args) throws Exception{
//创建代理类,通常用接口的那个类加载器
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());
System.out.println("----------begin constructors list----------");
/*$Proxy0()代理类的名字
$Proxy0(InvocationHandler,int)*/
//得到所有的构造方法
Constructor[] constructors = clazzProxy1.getConstructors();
//用for循环加强逐一取出构造方法
for(Constructor constructor : constructors){
String name = constructor.getName();
StringBuilder sBuilder = new StringBuilder(name);
//StringBuilder是线程不安全的,单线程下效率更高
sBuilder.append('(');
//得到构造方法的参数
Class[] clazzParams = constructor.getParameterTypes();
for(Class clazzParam : clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length != 0)
//只有在有参数时才去掉最后的逗号
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
System.out.println("----------begin methods list----------");
/*$Proxy0()
$Proxy0(InvocationHandler,int)*/
Method[] methods = clazzProxy1.getMethods();
for(Method method : methods){
String name = method.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = method.getParameterTypes();
for(Class clazzParam : clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
System.out.println("----------begin create instance object----------");
//Object obj = clazzProxy1.newInstance();会调用不带参数的构造参数,但它没有,所以不能用这种方式
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);//需要接收一个InvocationHandler参数
//一个空的实现
class MyInvocationHander1 implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
}
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1());
//toString返回值为null
System.out.println(proxy1);
proxy1.clear();
//不可以调用带有返回值的方法;因为invoke返回的是null而proxy1.size()要返回一个整数,将null转化成整数不成,所以报错
//proxy1.size();
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
//匿名内部类,android应用开发中经常用到,方便快捷
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
//演示如何通过自己写的获得动态代理的方法
//方法里的内部类要访问局部变量,局部变量前必须加final
final ArrayList target = new ArrayList();
Collection proxy3 = (Collection)getProxy(target,new MyAdvice());
proxy3.add("zxx");
proxy3.add("lhm");
proxy3.add("bxd");
System.out.println(proxy3.size());
//此处proxy从object继承的方法只有toString() hashCode() equals()才派发给handler 其它方法有自己的实现不交给handler
System.out.println(proxy3.getClass().getName());
}
//自己写的获得动态代理的方法
private static Object getProxy(final Object target,final Advice advice) {
//通过Proxy的加一个静态方法一步到位实现代理类
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
/*new Class[]{Collection.class},*/
target.getClass().getInterfaces(),
new InvocationHandler(){
//调用代理的三个要素:调用哪个代理,哪个方法及方法接收的参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/*long beginTime = System.currentTimeMillis();在方法调用前记下时间
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();方法调用完成时记下时间
System.out.println(method.getName() + " running time of " + (endTime - beginTime));
计算出方法运行了多长时间
return retVal;*/
//调用某个类的方法来实现通告
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
}
);
return proxy3;
}
}
Advice接口:
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}
MyAdvice类:
public class MyAdvice implements Advice {
long beginTime = 0;
public void afterMethod(Method method) {
System.out.println("从黑马毕业上班啦!");
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " + (endTime - beginTime));
}
public void beforeMethod(Method method) {
System.out.println("到黑马来学习啦!");
beginTime = System.currentTimeMillis();
}
}