笔者对于Java的认识也是比较浅显的。所以所记录的也仅仅是心得而已,如果文章有幸被别人看到,文章中的错误还请指出(本文原创,请勿转载),小弟将悉听教导。
首先,说一下静态代理,Java的代理思想与OC相比,还是很大不同的。不过,核心思想就是某一个类寻找一个代理,在一个合适的时机,去触发代理类工作。关于静态代理,笔者有一下代码片段。
静态代理
首先创建一个IPerson接口:
package com.xiaonan.staticProxy;
public interface IPerson {
public void run();
}
创建接口的实现类:
package com.xiaonan.staticProxy;
public class PersonImpl implements IPerson{
@Override
public void run() {
System.out.println("人正在跑步");
}
}
package com.xiaonan.staticProxy;
public class ProxyStaticPerson implements IPerson{
private IPerson person;//这个成员变量的设置由为重要,因为它将保存被代理的对象
public ProxyStaticPerson(){
super();
}
public ProxyStaticPerson(IPerson person){// 重写构造函数,传入被代理对象
super();
this.person = person;//将传进来的被代理对象保存到成员变量中
}
public IPerson getPerson() {
return person;
}
public void setPerson(IPerson person) {
this.person = person;
}
/**
* 代理实现IPerson接口方法
*/
@Override
public void run() {
// 实际是由成员变量进行调用run方法
person.run();
}
}
测试类:
package com.xiaonan.test;
import com.xiaonan.staticProxy.IPerson;
import com.xiaonan.staticProxy.PersonImpl;
import com.xiaonan.staticProxy.ProxyStaticPerson;
public class ProxyTest {
public static void main(String[] args) {
// 实例化一个代理对象,将其设置为IPerson接口类型
IPerson person = new ProxyStaticPerson(new PersonImpl());
person.run();// 代理对象调用run方法,但是实际上,是由代理对象内部的PersonImpl进行调用的。这里体现了静态代理的核心。
}
}
输出结果为:
人正在跑步
总结:首先要设置一个实现了IPerson接口的代理对象 ,只有实现这个接口,代理对象才具有run方法可供外界去调用,紧接着设置一个成员变量,让其保存IPersonImpl对象(也就是被代理对象),因为代理这种模式,最终还是要靠IPersonImpl去调用自己的run方法的。只是方法的调用时机改变了,不再是IPersonImpl在外界去主动调用,而是通过先调用代理对象的run方法,然后代理对象的内部再去调用IPersonImpl的run方法。
静态代理的好处是代码比较明了,但是只能把成员变量固定化,不具有通用性。而且,也只能代理一个对象。所以JAVA后来引入了动态代理这个概念。
动态代理
JAVA中的动态代理应用非常广泛,动态代理基于JAVA反射机制,这也弥补的JAVA并非是一门运行时语言的不足。基于JAVA动态代理,spring为我们带来了AOP—面向切面编程这个重要思想。
接下来,上代码
创建Teacher接口
package com.proxy.main;
public interface Teacher {
public void teachEnglish();
}
创建Teacher接口实现类
package com.proxy.main;
public class TeacherImpl implements Teacher{
@Override
public void teachEnglish() {
System.out.println("英语老师正在上英语课");
}
}
以上两步骤和静态代理没有区别,重点在于下面这代理类的创建。首先,代理类要实现InvocationHandler接口,实现这个接口,则必须重写它的invoke方法。
package com.proxy.main;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TeacherProxy implements InvocationHandler{
private Object targetObject;
/**
*
* @param targetObject
* @return 返回代理对象
*/
// 方法可以返回一个绑定了TeacherImpl的代理类
public Object createProxy(Object target){// 进来的参数为Teacher接口实现类,也就是需要被代理的对象。
targetObject = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 此方法为虚拟机调用,调用时机为代理对象调用Teacher接口方法的时候。
berforeDo();
return method.invoke(this.targetObject, args);
}
public void berforeDo(){
System.out.println("上课前的准备工作");
}
}
测试类:
package com.proxy.test;
import com.proxy.main.Teacher;
import com.proxy.main.TeacherImpl;
import com.proxy.main.TeacherProxy;
public class ProxyTest {
public static void main(String[] args) {
TeacherProxy proxy = new TeacherProxy();
Teacher teacher_proxy = (Teacher)proxy.createProxy(new TeacherImpl());
teacher_proxy.teachEnglish();
}
}
输出结果为:
上课前的准备工作
英语老师正在上英语课
通过上述代码我们可以发现,TeacherProxy类的成员变量为Object类型,这也避免了静态代理的只能为一个对象做代理的局限性。而代理类能够调用接口方法关键在于 method.invoke(this.targetObject,args)的这句反射代码。因为我们在为代理类TeacherProxy 和TeacherImpl进行绑定的时候,调用的是createProxy这个方法,这个方法将TeacherImpl作为参数,传递给了TeacherProxy的成员变量targetObject。这样子,当虚拟机调用重写过的invoke的方法的时候,我们就可以用method.invoke(this.targetObject, args)对TeacherImpl进行反射。接下来说一下反射的时机,当测试中new出了代理类后,一旦执行了代理类的teachEnglish方法,那么代理类内部重写的invoke方法将会被虚拟机调用,从而invoke内部method.invoke(this.targetObject,args)也会被调用,这句反射代码将会导致TeacherImpl(也就是this.targetObject)去调用它自己的teachEnglish方法,所以这一时刻静态代理和动态代理殊途同归了,他俩最终都要靠一个真正的接口实现类去调用teachEnglish方法,而代理只是个桥梁,或者说是扩展而已。这个扩展 ,我可以理解为为面向切面做铺垫。
从上面的代码,我们音乐感觉到AOP的实现原理,所以我加了一个beforeDo方法,听起来像不像AOP的前置通知,而TeacherImpl的teachEnglish方法,则是AOP的切点,即pointcut,这个代理类TeacherProxy 则是切面。若将AOP比喻成一个小说的时间,地点,事件的话,那么pointcut即地点,切面就是事件,现在就差事件了,这个事件,就是advice,上述代码的beforeDo 则是为这个小说注入了时间要素(靠,比喻的太牵强了)。
OK,今天先说到这里,文章中错漏百出的地方请大家海涵,并指出其中的错误。顺便说一下,文章原创,请勿转载(其实一点转载的价值都没有)。