什么是代理
代理通俗来说就是代替某人去做一件事情,可以举个例子,假设有个老师今天生病了不能来上课,那么是不是可以请一个代理老师来帮上课,实际意义上来说这两个老师完成的东西是一样的,都是给学生传道受业解惑,但是,代理老师是不是能在原来上课的基础上拓展更多“功能”,比如说代理老师去上课之前,先到隔壁班看了一眼中意的女老师有没有来,上完课之后又去吃了自己最爱吃的螺蛳粉~看到这儿有没有感觉到跟一个东西很像,所以SpringAOP面向切面变成的原理就是这样,当然实际实现方面会更加复杂。
代理分为三个角色
- 接口
- 委托类
- 代理类
静态代理
我们先说一下静态代理,就用我们前面举的例子,直接上代码。
接口:
package com.staticproxy;
//我和代理老师都是老师,共同实现了老师接口
public interface Teacher {
void teach();
}
委托类:
package com.staticproxy;
//这个就是生病的老师
public class MathTeacher implements Teacher {
@Override
public void teach() {
System.out.println("传道受业解惑40分钟");
}
}
代理类:
package com.staticproxy;
//这个是代理老师
public class ProxyTeacher implements Teacher {
//通过构造器注入被代理对象(注入一个生病老师的对象)
private Teacher targetTeacher;
public ProxyTeacher(Teacher teacher){
this.targetTeacher=teacher;
}
//代理老师实现了接口也有teach方法
@Override
public void teach() {
System.out.println("上课之前先去隔壁班看美女老师");
//调用生病老师的teach方法
targetTeacher.teach();
System.out.println("上完课了,去吃个螺蛳粉压压惊!");
}
}
Client的main方法:
package com.staticproxy;
public class Client {
public static void main(String[] args) {
Teacher teacher=new MathTeacher();
Teacher proxyteacher=new ProxyTeacher(teacher);
proxyteacher.teach();
}
}
结果:
静态代理跟着走一遍应该很快就能理清,非常简单。
下面我们继续动态代理,动态代理需要用到反射机制。
JDK动态代理
接口:
package com.jdkproxy;
public interface Teacher {
void teach();
}
没什么好说的跟前面一样的接口
委托类:
package com.jdkproxy;
public class MathTeacher implements Teacher {
@Override
public void teach() {
System.out.println("传道受业解惑40分钟");
}
}
重点来了
package com.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 注意动态代理这里不需要再实现教师接口了
* 这样才能体现动态的概念,往往在实际应用之中
* 我们是不会提前知道我们需要代理什么对象的
*/
public class ProxyTeacher {
private Object targetteacher;
public ProxyTeacher(Teacher teacher){
this.targetteacher=teacher;
}
/**
* 这里调用了Proxy的newProxyInstance方法获得代理对象
* 参数1表示被代理对象的类加载器
* 参数2是被代理对象实现的接口,可以是数组(表示实现多个接口)
* 参数3是用匿名内部类实例化一个实现了InvocationHandler接口的对象
*/
public Object getProxyInstance(){
return Proxy.newProxyInstance(targetteacher.getClass().getClassLoader(), targetteacher.getClass().getInterfaces(),
new InvocationHandler() {
/**
* @param proxy 调用这个方法的代理实例
* @param method 要调用的方法
* @param args 方法调用时所需要的参数
* @return 方法调用的结果
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("上课之前先去隔壁班看美女老师");
method.invoke(targetteacher,args);
System.out.println("上完课了,去吃个螺蛳粉压压惊");
return null;
}
});
}
}
Client:
package com.jdkproxy;
public class Client {
public static void main(String[] args) {
Teacher teacher=new MathTeacher();
Teacher proxyTeacher=(Teacher)new ProxyTeacher(teacher).getProxyInstance();
proxyTeacher.teach();
}
}
结果:
总结:
当然SpringAop用的动态代理更为复杂,还有cglib动态代理等其他技术这里不做延伸,我们只需要知道,动态代理可以在完成原来对象实现的功能的基础之上,继续延伸和拓展,这也是SpringAop面向切面编程的一个重要思想,广泛应用在比如日志,异常处理,监控,事务管理等功能上。这就是面向切面的大致原理,可以这么想,上课本来就是老师必做的流程,在该流程上横向切入了看美女和吃东西两个功能,而丝毫不影响功能的实现。