昨天介绍了下httpclient,其中涉及到aop的使用,其实aop的底层是用java动态代理实现的。动态代理其实就是用java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy进行操做,
这个使用也是比较简单的,记得一年前我刚开始学Java的时候,这个是我最难理解的。其实动态代理是在运行时创建代理对象,从而在实际调用方法(通过反射)的各个时机进行各种操作。
首先要实现InvocationHandler这个接口,具体代码如下,解析放到注释中:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理首先要实现InvocationHandler这个接口
*
*/
public class MyInvokeHandler implements InvocationHandler {
//实际被代理的对象
private Object object;
public MyInvokeHandler(Object object){
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 这里可以在调用方法的各个时机,进行各种操作,
* 其实对应也对应aop的各个通知。其实这里还可以
* 有异常处理,在完善代码时给出。
*/
System.out.println("调用方法之前");
Object obj = method.invoke(object,args);
System.out.println("调用方法之后");
//返回相应的结果,这里可以偷梁换柱,把结果换掉。
return obj;
}
}
实际对象对应的类:
//注意这两个类不是在同一个文件的,我这里是为了方便
//因为java中一个文件不能有两个public类
public interface JayChouConcert {
String singing(String musiceName);
void dancing();
}
public class OpenJayChouConcert implements JayChouConcert {
public String singing(String musiceName) {
System.out.println(String.format("周杰伦唱%s。。。",musiceName));
return musiceName;
}
public void dancing() {
System.out.println("伴舞。。。");
}
}
测试上述的代码:
import java.lang.reflect.Proxy;
public class Test {
public final static void main(String[] args){
//实际要代理的对象
JayChouConcert jayChouConcert = new OpenJayChouConcert();
MyInvokeHandler myInvokeHandler = new MyInvokeHandler(jayChouConcert);
//使用Proxy的newProxyInstance方法创建代理对象
JayChouConcert jayChouConcert1 = (JayChouConcert)Proxy.newProxyInstance(jayChouConcert.getClass().getClassLoader()
,jayChouConcert.getClass().getInterfaces(),myInvokeHandler);
jayChouConcert1.singing("蒲公英的约定");
}
}
运行输出的结果为:
调用方法之前
周杰伦唱蒲公英的约定。。。
调用方法之后
上述的代码便是动态代理的helloworld,接下来讲讲动态代理有什么用和springboot的aop中各种通知的对应关系。
动态代理因为在使用反射调用实际的方法前,可以进行相关的处理。
比如说:你有100个方法都要打印一条相同的信息,你不可能在100个方法前都加那个信息,因为某一天你要修改这条信息了,
你就要修改100边,而用动态代理的话你可以怎么做呢?
假设就上述的两个方法,我都要加一句话:最美的不是下雨天,而是与你躲过雨的屋檐。
你可以选择每次调用方法的时候都加,不过可能你某天要变成:而我已经分不清,你是友情还是错过的爱情。
那么你就要改两遍,这还好,如果是100方法的话,就麻烦了,而且还很容易出错,指不定你哪句话改错了。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 这里可以在调用方法的各个时机,进行各种操作,
* 其实对应也对应aop的各个通知。其实这里还可以
* 有异常处理,在完善代码时给出。
*/
System.out.println("最美的不过下雨天,而是与你躲过雨的屋檐。");
Object obj = method.invoke(object,args);
System.out.println("而我已经分不清,你是友情还是错过的爱情。");
return obj;
}
}
调用结果为:
最美的不过下雨天,而是与你躲过雨的屋檐。
周杰伦唱蒲公英的约定。。。
而我已经分不清,你是友情还是错过的爱情。
这只是其中一种运用,还有很多,你可能要对方法返回的结果,或者这个方法调用出现异常的时候进行相关的处理。这些处理
的时机就对应aop中的各种通知了。代码如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理首先要实现InvocationHandler这个接口
*
*/
public class MyInvokeHandler implements InvocationHandler {
//实际被代理的对象
private Object object;
public MyInvokeHandler(Object object){
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 这里可以在调用方法的各个时机,进行各种操作,
* 其实对应也对应aop的各个通知。其实这里还可以
* 有异常处理,在完善代码时给出。
*/
try{
//下面的System.out.println其实用方法表示更好,
//就是在调用实际方法的各个时机,调用其它方法
System.out.println("前置通知。。。@Before");
Object obj = method.invoke(object,args);
return obj;
}catch (Exception e){
System.out.println("后置异常通知。。。 @AfterThrowing");
}finally {
System.out.println("后置返回通知。。。 @AfterRuturning,从这个位置可以拿到返回结果obj");
}
return null;
}
//System.out.println("后置最终通知。。。@After,finally退出后执行,这个不管是否异常都会会
//执行");
}
进行测试:
方法正常运行的时候:
前置通知。。。@Before
周杰伦唱蒲公英的约定。。。
后置返回通知。。。 @AfterRuturning,从这个位置可以拿到返回结果obj
//这里还有After的结果,暂时无法演示
这这里可以看到异常的通知没有执行。
在方法中加入异常代码:
public class OpenJayChouConcert implements JayChouConcert {
public String singing(String musiceName) {
System.out.println(String.format("周杰伦唱%s。。。",musiceName));
int i = 10;
i = i/0;
return musiceName;
}
public void dancing() {
System.out.println("伴舞。。。");
}
}
此时运行:
前置通知。。。@Before
周杰伦唱蒲公英的约定。。。
后置异常通知。。。 @AfterThrowing
后置返回通知。。。 @AfterRuturning,从这个位置可以拿到返回结果obj
//这里还有After的结果,暂时无法演示
下次在Springboot环境实际操作下的aop。