/***************************************************************************************/
此博客主要是在观看张孝祥老师的教学视频的过程中,自己所做的学习笔记,在此以博客的形式分享出来,方便大家学习,建议大家观看视频,以此笔记作为回顾资料。
参考资料
张孝祥2010年贺岁视频:Java高新技术 视频下载
疯狂Java讲义
/**************************************
49节视频***************************************/
分析代理的原理与作用及AOP的概念
代理
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 (参看下页的原理图)
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在代码中引用代理类跟目标类都实现的接口,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易
这个不是很正规,正规的方式是,定义一个接口,接口里面有sayHello方法,然后让X类跟XProxy都实现这个接口,在XProxy的实现的sayHello方法
里面,调用X的sayHello方法
在编写程序的时候不是直接引用目标对象,也不是直接引用代理对象,而是直接引用目标跟代理都实现的接口。
学会画类图,AOP
类图 实现接口------虚线 继承类-----实线
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
如果目标类没有实现接口,那么可以使用第三方的CGLIB库,CGLIB可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
/**************************************
50节视频
***************************************/
创建动态类及查看其方法列表信息
分析java虚拟机动态生成的类
//第一个参数指定的是生成的类的类加载器,因为这个类是JVM动态生成的没有类加载器,所以我们需要为其指一
个类加载器,这个参数通常设置为这个代理类实现的接口的类加载器,及第二个参数的类加载器。
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(Constructor constructor : constructors){
String name = constructor.getName();
StringBuilder sBuilder = new StringBuilder(name);//StringBuilder是线程不安全的,StringBuffer是线程安全的,单线程的时候选择StringBuilder效率比较高,多线程的时候选择StringBuffer
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());
}
/*$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());
}
/**************************************51
节视频
***************************************/
创建动态类的实例对象及调用其方法
System.out.println("----------begin create instance object----------");
//Object obj = clazzProxy1.newInstance();
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);//动态类的构造方法没有无参数的
class MyInvocationHander1 implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
//创建动态类的实例 方法一
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1());
System.out.println(proxy1);
proxy1.clear();
//proxy1.size();//只能调用获得的动态类的实例的没有返回值的方法,调用有返回值的方法会报错。
//System.out.println("111111111111111");
/**************************************52
节视频
***************************************/
完成InvocationHandler对象的内部功能
//创建动态类的实例 方法二 利用了匿名内部类
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
//创建动态类的实例 方法三 将动态类的获取及其实例的创建一起完成
Collection proxy3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[]{Collection.class},new InvocationHandler() {
ArrayList target = new ArrayList();
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
long beginTime = System.currentTimeMillis();
Object retVal = arg1.invoke(target,arg2);
long endTime = System.currentTimeMillis();
System.out.println(arg1.getName()+"running time of "+(endTime-beginTime));
return retVal;
}
});
proxy3.add("zxx");
proxy3.add("lhm");
proxy3.add("bxd");
System.out.println(proxy3.size());
/**************************************53节视频***************************************/
分析InvocationHandler对象的运行原理
动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。
构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢? 实现Collection接口的动态类中的各个方法的代码又是怎样的呢?
在生成的动态类的内部有一个InvocationHandler类的实例,构造方法接收的InvocationHandler对象是为了初始化这个实例,具体代码见下
$Proxy0 implements Collection
{
InvocationHandler handler;
public $Proxy0(InvocationHandler handler)
{
this.handler = handler;
}
//生成的Collection接口中的方法的运行原理
int size()
{
return handler.invoke(this,this.getClass().getMethod("size"),null);
}
void clear(){
handler.invoke(this,this.getClass().getMethod("clear"),null);
}
boolean add(Object obj){
return handler.invoke(this,this.getClass().getMethod("add"),obj);
}
}
调用生成的动态代理类的实例对象的方法,实际上调用的是构造方法传递进去的InvocationHandler对象的invoke方法,每调用一次动态代理类实例对象的方法,就调用传递进去的InvocationHandler对象的invoke方法一次。
InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下:
Client程序调用objProxy.add(“abc”)方法时,涉及三要素:objProxy对象、add方法、“abc”参数
Class Proxy$ {
boolean add(Object object)
{
return handler.invoke(Object proxy, Method method, Object[] args);
}
}
Collection proxy3 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[]{Collection.class},new InvocationHandler() {
ArrayList target = new ArrayList();
@Override
public
Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
long beginTime = System.currentTimeMillis();
//可以对接收的参数arg2进行修改,例如写个过滤器,检查是否含有相关内容
Object retVal = arg1.invoke(target,arg2);//如果将第一个参数换成 arg0,会形成死循环
long endTime = System.currentTimeMillis();
System.out.println(arg1.getName()+"running time of "+(endTime-beginTime));
//可以对接收的参数arg2进行修改,替换掉某些内容
return retVal;
}
});
boolean
object1 = proxy3.add("zxx");
//InvocationHandler对象的invoke方法的返回值,传递给了Proxy对象的相关方法
boolean object2 =proxy3.add("lhm");
boolean object3 =proxy3.add("bxd");
System.out.println(proxy3.size());
System.out.println(proxy3.getClass().getName());//打印结果是
com.sun.proxy.$Proxy0
分析为什么动态类的实例对象的getClass()方法返回了正确结果呢?
调用代理对象的从Object类继承的hashCode, equals, 或toString这几个方法时,代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。
/**************************************54
节视频
***************************************/
总结分析动态代理类的设计原理与结构
********************Advice************************************
package cn.itcast.day3;
import java.lang.reflect.Method;
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}
********************MyAdvice************************************
package cn.itcast.day3;
import java.lang.reflect.Method;
public class MyAdvice implements Advice {
long beginTime = 0;
public void afterMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("从传智播客毕业上班啦!");
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " + (endTime - beginTime));
}
public void beforeMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("到传智播客来学习啦!");
beginTime = System.currentTimeMillis();
}
}
********************Demo************************************
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());
System.out.println(proxy3.getClass().getName());
private static Object getProxy(final Object target,final Advice advice) {
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;
}
/**************************************55
节视频
***************************************/
实现类似Spring的可配置的AOP框架