Spring07之AOP的代理模式


AOP的核心是动态代理模式。
要掌握AOP,就要掌握代理模式。
代理模式有两种,分别为静态代理和动态代理。
学习动态代理之前,先掌握静态代理。

一、静态代理

一共有四个角色:

1.抽象角色

一般使用接口或者抽象类来解决。例如租房这件事情。

抽象角色

public interface Rent {
    void rent();
}

2.真实角色

被代理的角色。例如房东。

public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("我是房东,我有房子");
    }
}

3.代理角色

代理真实角色,并且增加一些附加操作。例如中介。

public class Proxy {
    
    private Rent rent;  //多态!
    public Proxy(Rent rent) {
        this.rent = rent;
    }


    public void rent(){
        seeHouse();
        rent.rent();
        fare();
    }


    public void seeHouse(){
        System.out.println("看房子");
    }
    public void fare(){
        System.out.println("收取中介费");
    }
}

4.客户(测试)

访问代理角色的人。

public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

二、JDK原生动态代理

1、理解动态代理为何要这样写

我们的需求

动态代理,就是被代理类随机,根据加载到内存中的被代理类,来创建代理类对象。
由此需求,我们对于代理类的模板要求如下:

如何解决

第一、模板可根据反射获取被代理类中的方法名、方法参数、方法返回值。
第二、将代理类和被代理类绑定起来
第三、代理类对象执行方法,实质上是被代理类对象在执行。

假设代理类对象是A。被代理类对象是B。
A.method1(arg1,arg2)的内部其实是
A.method(){
B.method1(arg1,arg2);
}

因此A中需要这样一个方法处理程序,通过
A.method1(arg1,arg2) 获取到方法名,方法参数
然后在method里复造这个method1这个方法,并暗箱操作,让B来调用这个方法。
这个方法处理程序就是InvocationHandler
由于内部调用了B对象,因此在InvocationHandler中B应该作为属性出现,并通过构造器或方法赋值。

2、案例一:租房案例

四个角色加一个方法处理程序

1、抽象角色

public interface Rent {
    void rent();
}

2、真实角色

房东

public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("我是房东,我有房子");
    }
}

3.代理角色

public class ProxyRent {
    public static Object getProxyInstance(Object obj){
        MyInvocationHandler handler = new MyInvocationHandler(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}

4.方法处理程序

public class MyInvocationHandler implements InvocationHandler {
    private Object obj;


    public MyInvocationHandler(Object obj) {
        this.obj=obj;
    }




    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        Object returnValue = method.invoke(obj, args);
        fare();
        return returnValue;


    }


    public void seeHouse(){
        System.out.println("看房子");
    }
    public void fare(){
        System.out.println("收取中介费");
    }
}

5.客户(测试)

public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Rent proxyInstance = (Rent) ProxyRent.getProxyInstance(host);  //强制类型转换时,只能转换成父接口,不能转换成实现了这个接口的子类
        proxyInstance.rent();
    }
}

注意
强制类型转换时,只能转换成父接口不能转换成实现了这个接口的子类
ProxyRent.getProxyInstance(host)这个方法返回的是实现了Rent接口的代理类的对象。只能强制转换成Rent类型的对象。

2、案例二之超人案例

1、抽象角色

public interface Human {
    String getBelieve();
    void eat(String food);
}

2、真实角色

public class SuperMan implements Human {
    @Override
    public String getBelieve() {
        return "I believe I can fly!";
    }


    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃"+food);
    }
}

3、代理角色

public class ProxyFactory {
    //1.参数传入被代理类的对象,返回一个代理类的对象
    public static Object getProxyInstance(Object obj){
        MyInvocationHandler handler = new MyInvocationHandler(obj);

        //Proxy.newProxyInstance() 该方法专门用于创建并返回代理类的对象
        //参数1:被代理类对象的运行时类的类加载器;类加载器用于将字节码文件加载到内存中,动态创建代理类对象,自然需要一个类加载器。
        //参数2:被代理类对象的运行时类的实现的接口;指明代理类需要实现什么接口。
        //参数3:实现了InvocationHandler接口的类的对象;
        //      放一个对象在这里的目的是:
        //      该方法动态创建并返回的代理类对象,不管调用啥方法,都会自动调用handler里的invoke().解决了问题3.
        //      因此被代理类要想实现的功能就应该放在invoke()里面。
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}

4、方法处理程序

public class MyInvocationHandler implements InvocationHandler {

    //obj:被代理类的对象
    private Object obj;

    public MyInvocationHandler(Object obj){
            this.obj=obj;
        }

    
    //当我们通过代理类的对象调用方法a时,就会自动调用如下方法:invoke()
    //将被代理类要执行的方法a的功能声明在invoke()中
    /*
    * 参数1:代理类对象。先有代理类对象,再是调用方法。
    * 参数2:代理类对象调用的方法名
    * 参数3:方法的参数。例如"火锅",就作为参数传了进来
    *
    * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法。
        //obj:被代理类的对象。本质上还是被代理类在调用该方法。
        Object returnValue = method.invoke(obj, args);

        //method方法的返回值也是invoke()方法的返回值
        return returnValue;

    }
}

5、测试

public class ProxyTest {
    public static void main(String[] args) {
        //创建被代理类对象
        SuperMan superMan = new SuperMan();


        //获取代理类对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);


        /*
        代理类对象调用被代理类对象的方法:
        调用时,会自动进入handler里面的invoke()。
        invoke(Object proxy, Method method, Object[] args)
        proxy已经被绑定了,method就是 eat ,args就是"火锅"。
        然后invoke()里面还是被代理类对象在调用该方法。
        * */

        proxyInstance.eat("火锅");
        System.out.println(proxyInstance.getBelieve());
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值