(2)代理模式

来源:互联网 发布:动态海报制作软件 编辑:程序博客网 时间:2024/06/06 18:10

代理模式分为两种,一种静态代理,一种动态代理
一、静态代理
静态代理在我们生活中其实非常常见,比如买票,当火车站太远而且需要排队,这个时候就需要一个手机来在网上买票了,这种情况下手机就相当于火车站的代理对象,还有比如说追女孩,我想送一束花,因为跟女孩不太熟,直接送会显得很突兀,这时就需要委托一个人来帮忙送了,这个人此时就相当于我的代理对象。
代码说明
将人抽象成一个类

public abstract Boy{    void sendFlowers();}

我要追的女孩需要实现这个接口

public class Me extends Boy {    @Override    public void sendFlowers() {        System.out.println("送给女孩一朵花");      }}

直接送会很尴尬,所以委托别人帮我送

public class MeProxy extends Boy {    //需要穿入被代理对象的引用    private Boy boy;    public MeProxy (Boy boy) {        this.boy = boy;    }    @Override    public void sendFlowers() {        boy.sendFlowers();        System.out.println("委托者说你先把花给我然后我转交她");    }}

测试类

public class Test {    public static void main(String[] args) {        Me me = new Me();        MeProxy meproxy = new Meproxy(me);        meproxy.sendFlowers();    }}

二、动态代理
动态代理又分为两种,一种jdk动态代理,一种是cglib动态代理
jdk动态代理
首先定义一个接口

public interface Car {    int run();    void drive();}

被代理类实现接口

public class MyCar implements Car {    @Override    public int run() {        System.out.println("我的车速度很快");        return 80;    }    @Override    public void drive() {        System.out.println("安全第一,严禁酒驾");    }}

代理类继承InvocationHandler接口来给方法做增强

public class MyInvocationHandler implements InvocationHandler {    //传入被代理类的引用    private MyCar myCar;    public MyInvocationHandler(MyCar myCar) {        this.myCar = myCar;    }    @Override    public Object invoke(Object object, Method method, Object[] args) throws Throwable {        if (method.getName() == "drive") {            System.out.println("上车请系好安全带");            Object invoke = method.invoke(myCar, args);            System.out.println("下车请锁好车");            return invoke;        }        return method.invoke(myCar, args);    }}

测试类

public static void main(String[] args) {        MyCar myCar = new MyCar();         /**          * myInvocationHandler 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发          * 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.          * 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法          */          MyInvocationHandler myInvocationHandler = new MyInvocationHandler(myCar);        Object object = Proxy.newProxyInstance(myCar.getClass().getClassLoader(), myCar.getClass().getInterfaces(),myInvocationHandler );        Car car = (Car) object;        car.drive();        car.run();    }

运行结果如下
这里写图片描述

JDK动态代理和CGLIB的区别

Aspect默认情况下不用实现接口,但对于目标对象,在默认情况下必须实现接口
如果没有实现接口必须引入CGLIB库

我们可以通过Advice中添加一个JoinPoint参数,这个值会由spring自动传入,从JoinPoint中可以取得
参数值、方法名等等

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

如何强制使用CGLIB实现AOP?
* 添加CGLIB库,SPRING_HOME/cglib/*.jar
* 在spring配置文件中加入

JDK动态代理和CGLIB字节码生成的区别?
* JDK动态代理只能对实现了接口的类生成代理,而不能针对类
* CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final