代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想在现实中的一个例子
用图表示如下:
4.1静态代理
定义一个接口,列出抽象方法
public interface CarInterf {
//造车
public abstract void makeCar();
//洗车
public abstract void washCar();
//修车
public abstract void fixCar();
//找车模
public abstract void findCarModel();
}
定义委托类,并且要继承接口
//委托方
public class CarFactory implements CarInterf {
@Override
public void makeCar() {
System.out.println("委托者造车方法");
}
@Override
public void washCar() {
System.out.println("委托者洗车方法");
}
@Override
public void fixCar() {
System.out.println("委托者修车方法");
}
@Override
public void findCarModel() {
System.out.println("委托者找车模方法");
}
}
定义一个通知类,用来扩充委托者的功能
public class MyAdvice {
public void beafore(){
System.out.println("前置通知");
}
public void after(){
System.out.println("后置通知");
}
//第一个参数为要增强的方法
//第二个参数为代理对象的class对象
public void around(String method,Class proxyCar) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
System.out.println("环绕增强开始");
//得到要执行的方法
Method meth = proxyCar.getMethod(method, null);
//执行该方法
meth.invoke(proxyCar.newInstance(), null);
System.out.println("环绕增强结束");
}
}
定义一个代理类,并且继承同一接口
/*
* 代理方:
* 为委托方的功能添加额外功能,并且还不影响委托方中的代码结构
*/
public class ProxyCar implements CarInterf{
//通知对象
private static MyAdvice advice = new MyAdvice();
//委托者对象
private CarInterf carFacory;
public ProxyCar(CarInterf carFacory) {
this.carFacory = carFacory;
}
public ProxyCar() {
}
@Override
public void makeCar() {
// System.out.println("造车之前的额外功能");
advice.beafore();
//基本功能
this.carFacory.makeCar();
// System.out.println("造车之后的额外功能");
}
@Override
public void washCar() {
this.carFacory.washCar();
advice.after();
}
@Override
public void fixCar() {
try {
advice.around("fixCar", carFacory.getClass());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void findCarModel() {
}
}
测试类
@Test
public void test() {
//使用静态代理
ProxyCar proxyCar = new ProxyCar(carFactory);
proxyCar.makeCar();
proxyCar.fixCar();
proxyCar.washCar();
proxyCar.findCarModel();
}
4.2动态代理
定义一个接口,列出抽象方法
public interface CarInterf {
//造车
public abstract void makeCar();
//洗车
public abstract void washCar();
//修车
public abstract void fixCar();
//找车模
public abstract void findCarModel();
}
定义委托类,并且要继承接口
//委托方
public class CarFactory implements CarInterf {
@Override
public void makeCar() {
System.out.println("委托者造车方法");
}
@Override
public void washCar() {
System.out.println("委托者洗车方法");
}
@Override
public void fixCar() {
System.out.println("委托者修车方法");
}
@Override
public void findCarModel() {
System.out.println("委托者找车模方法");
}
}
定义一个动态代理类,需要继承InvocationHandler接口,在程序运行前要先加载委托方
/*
* 委托方是在运行时确定
* 动态代理 需要实现JDK提供的接口
* AOP使用的动态代理是 CGlib
* JDK动态代理内部实现是基于接口的
* CGlib动态代理内部实现还是基于委托对象
*/
public class DynamicProxy implements InvocationHandler{
//目标对象
private Object targetObj;
public DynamicProxy() {
}
public DynamicProxy(Object targetObj) {
this.targetObj = targetObj;
}
//将委托方和代理方建立动态联系
//返回值为代理对象
public Object instance(){
/*
* 第一个参数:对象的加载器(ClassLoader)在JVM中将.java源文件进行编译就生成.class文件,通过.class文件 创建当前实例对象
* 第二个参数:目标对象的所有接口方法
* 第三个参数:代理对象
*/
return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces(), this);
}
/*
* 当代理对象调用委托对象的接口方法时,
* 并不是立即执行,而是执行该invoke方法。
* 第一个参数:代理对象
* 第二个方法:需要执行的方法
* 第三个参数:执行方法需要的实参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置通知");
//method方法的返回值
Object object = method.invoke(targetObj, args);
System.out.println("后置通知");
return object;
}
}
测试类
@Test
public void test() {
//动态代理
CarFactory carFactory = new CarFactory();
//代理CarFactory
DynamicProxy dynamicProxy = new DynamicProxy(carFactory);
//进行动态绑定,生成代理对象。
CarInterf object = (CarInterf)dynamicProxy.instance();
//当代理对象调用委托对象的接口方法时,先执行invoke()
object.washCar();
}
这里有一个坑!Spring AOP采用两种代理方法,一种是常规的JDK,一种是CGLIB。由于CarFactory实现了CarInterf接口,当代理对象实现了至少一个接口是,默认使用JDK动态代理。当代理对象没有实现任何接口是,就会使用CGLIB方法。在JDK动态代理中,代理对象必须强转为接口,不然会报错。