代理分为静态代理和动态代理,今天笔者就和大家聊一聊动态代理,期间我们加入了切面,笔者并且进行了两个单元测试,一个没有加切面,一个加入切面,我们来看看结果有什么不同,大家就知道笔者加入切面的用意。
首先笔者用一个通俗易懂的例子来概述代理,由浅入深,大家就能理解深入理解代理,希望读者能认真看完,一定会对你们在这一块有所帮助的。
代理:
代理就好比租房子,这个过程中有三方人。第一个是租客,第二个是中介,第三个是房东。那么,代理是什么?这里的代理就是中介,租客只需要找中介租房子,而找房子的事就交个中介就行了,中介再去找合适的房子再联系房东,而房东要出租房子,只需要找中介,找租客的事交给中介就行了。这一件事中中介就是代理,这三个人就相当于三个类,每个类只需要实现自己的方法,就相当于这三个人只需要做自己的事。那么,我们将他们要做的事剔出来。
租客:找中介,租房子中介:帮租客找房子,帮房东找租客房东:找中介,找租
那么我们理解了代理,那什么是静态代理?什么是动态代理?
静态代理:静态代理就是手动的生成中介,在实现上述一些列操作。
动态代理:动态代理就是不需要手动的生成了,程序帮你完成了。
上述就算是由浅入深的带大家简单了解一下这两个代理,接下来笔者就重点讲讲动态代理,事不宜迟,上代码。
租客:
package com.kaifamiao.aop.mydynamic2;
public class Renter implements Rent{
String name="周星驰";
@Override
public void rent() {
System.out.println("我是"+name+"我要租房子");
}
}
房东:
package com.kaifamiao.aop.mydynamic2;
public class Host implements Rent{
String name="包租婆";
@Override
public void rent() {
System.out.println("我是"+name+",我要出租我的房子");
}
}
接口:
package com.kaifamiao.aop.mydynamic2;
public interface Rent {
void rent();
}
关于切面的类(稍后动态代理讲完再来看):
package com.kaifamiao.aop.mydynamic2;
import java.lang.reflect.Method;
import java.util.logging.Logger;
public class myAdvice {
private Logger logger=Logger.getAnonymousLogger();
public void before(Method m){
logger.info("方法:"+m.getName()+"即将执行");
}
public void afterReturn(Method m,Object result){
logger.info("方法:"+m.getName()+"执行后,返回结果:"+result);
}
public void afterThrow(Method m,Throwable cause){
logger.info("方法:"+m.getName()+"执行后,抛出:"+cause);
}
public void after(Method m){
logger.info("方法:"+m.getName()+"执行结束");
}
}
(中介)测试类:(相当于程序为我们生成的中介,也就是动态代理)
package com.kaifamiao.aop.mydynamic2;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.testng.annotations.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class dynamicTest {
public @BeforeClass static void middleHost(){
System.out.println("中介联系房东");
}
public @Test void HostTest(){
Rent target=new Host();
//Host h=new Host();
//Rent target =h;
Class> c = target.getClass();
ClassLoader loader = c.getClassLoader();
Class>[] interfaces = c.getInterfaces();
myAdvice advice = new myAdvice();
//InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,
// 每一个proxy代理实例都有一个关联的调用处理程序
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法
// invoke在代理实例上处理方法调用并返回结果。
String name = method.getName();
Object result=null;
try {
//before
if (name.equals("rent")){
advice.before(method);
}
result = method.invoke(target, args);
if (name.equals("rent")){
advice.afterReturn(method ,result);
}
}catch (Throwable cause){
if (name.equals("rent")){
advice.afterThrow(method,cause);
}
return cause;
}finally {
if (name.equals("rent")){
advice.after(method);
}
}
return result;
}
};
//Proxy类就是用来创建一个代理对象的类
Object proxy = Proxy.newProxyInstance(loader,interfaces,handler);
if (proxy instanceof Rent){
Rent r = (Rent) proxy;
r.rent();
r.hashCode();
r.toString();
r.equals( null );
}
middleHost();
}
public @Test void RenterTest(){
Rent target=new Renter();
Class> c = target.getClass();
ClassLoader loader = c.getClassLoader();
Class>[] interfaces = c.getInterfaces();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
return result;
}
};
Object proxy = Proxy.newProxyInstance(loader, interfaces, handler);
if (proxy instanceof Rent){
Rent r = (Rent) proxy;
r.rent();
}
middleRenter();
}
public @AfterClass static void middleRenter(){
System.out.println("中介向租客要中介费");
}
}
这里HostTest这个测试类是加入切面的,我们先看RenterTest这个单元测试。我们先要了解其中的一些东西。
getClassLoader() | 每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调 |
getInterfaces() | 获得类所直接实现的所有接口组成的数组 |
InvocationHandler() | 对象的类装载器 |
invoke() | 用来执行某个的对象的目标方法,invoke在代理实例上处理方法调用并返回结果 |
Proxy | Proxy类就是用来创建一个代理对象的类 |
newProxyInstance | 为目标接口生成代理类及代理对象 |
loader | 用哪个类加载器去加载代理对象 |
interfaces | 动态代理类需要实现的接口 |
handler | 动态代理方法在执行时,会调用handler里面的invoke方法去执行 |
这里读者解释一下RenterTest中第一行代码,Rent target=new Renter();其实这里笔者可以将它拆分写:
重点,重点,重点!!!
Renter r=new Renter(); //先new出一个租客的对象
Rent target=r;//因为租客是代理对象,且租客实现了其中的接口,因此,可以给得到一个代理目标
Rent target=new Renter(); Class> c = target.getClass(); ClassLoader loader = c.getClassLoader(); Class>[] interfaces = c.getInterfaces();
第一段代码是获得代理目标,后面三段代码分别是获得代理目标所对应的对象的类,类加载器,类中实现的接口。
Object proxy = Proxy.newProxyInstance(loader, interfaces, handler);
if (proxy instanceof Rent){
Rent r = (Rent) proxy;
r.rent();
}
1:proxy是代理对象,就相当于中介,这里就是相当于租客去租房子,换成了中介去租房子,newProxyInstance为目标接口生成代理类及代理对象,所以得到的proxy就是代理对象,现在,代理对象要得到租客(代理目标)的类加载器,接口,方法,这个方法,用匿名类实现InvocationHandler接口并完成调用操作,因为这个匿名类中有代理目标所实现的方法,之后得到类的装载器。
2:ify语句中instanceof的作用是判断proxy这个代理对象是否是Rent这个接口的实例,换句话说就是,租客实现了这个Rent接口的方法,我通过instanceof关键字判断一下proxy这个代理对象是不是和租客一样也实现了这个Rent接口的方法,白话就是,人家租客租房子,现在租客找了一个人过来,就是替租客租房子,而不是替租客吃饭来的。先if语句中的判断为true,那么,代理对象proxy,也就是中介,就可以从房东哪里租房子了。
Rent r = (Rent) proxy; r.rent();
上述代码就是中介在代替租客租房子。笔者带你们将代码分析完了,接下俩我们来看看运行结果吧。
这就是运行结果,除了知道运行结果成了什么也不知道,光秃秃的。那么接下来笔者再给大家看看另一个截图,然后我再告诉你,这是加入切面的结果,我想,以后大家写动态代理也一定会用切面得吧,当然因人而异。
从上述图中,我们就知道代理目标租房子用的方法了,是不是一目了然。
接下来笔者给大家简单聊一聊切面。
HostTest中匿名类中有一段代码,result = method.invoke(target, args);它就是执行点,再执行点上加上方位就是切点,再切点上加上advice就是切面,由此可知,匿名类中有5个切面。
这里笔者就不唠叨了,大家好好看看上述的动态代理吧,如果笔者有说得不对的地方,希望大家留言指出来,笔者好即使纠正,蟹蟹大家!!!



