代理模式

什么是代理模式?

代理模式提供了对目标对象的另外的访问模式,即通过代理对象可以访问到目标对象,代理模式的好处是,可以在不修改目标对象方法的基础上进行扩展,比如:打印日志、计算方法执行时间等。
代理模式中有两个关键点:
代理对象和目标对象,代理对象是对目标对象的扩展,并会调用目标对象。

举个简单的例子租房子的例子,目标对象就是房东,代理对象是中介,房东把房子交给中介帮忙出租,租房子的人可以直接通过中介进行租赁。

静态代理

静态代理在使用时,需要定义接口或者父类,代理对象与目标对象一起实现相同的接口或者是继承相同父类。举个简单的例子:
定义实现接口IUserDao:

interface IUserDao {

    void add();

    void delete();

    void update();

    void select();
}

目标对象UserDao:

public class UserDao implements IUserDao {

    @Override
    public void add() {
        System.out.println("目标对象执行add方法");
    }

    @Override
    public void delete() {
        System.out.println("目标对象执行delete方法");
    }

    @Override
    public void update() {
        System.out.println("目标对象执行update方法");
    }

    @Override
    public void select() {
        System.out.println("目标对象执行select方法");
    }
}

代理对象UserDaoProxy:

public class UserDaoProxy implements IUserDao {

    private IUserDao target;

    public UserDaoProxy(IUserDao target) {
        this.target = target;
    }

    @Override
    public void add() {
        long t1 = System.currentTimeMillis();
        System.out.println("方法开始执行时间:" + t1);
        target.add();   //调用目标方法
        System.out.println("方法执行共耗时:" + (System.currentTimeMillis() - t1)/1000);
    }

    @Override
    public void delete() {

    }

    @Override
    public void update() {

    }

    @Override
    public void select() {

    }
}

测试案例:

public class ProxyTest {

    public static void main(String[] args) {
        //目标对象
        IUserDao userDao = new UserDao();
        //代理对象,把目标对象传给代理对象,建立代理关系
        UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
        userDaoProxy.add();
    }
}

在实例化代理对象的时候,通过构造器把目标对象传给代理对象,从而建立起联系,代理对象就可以获取调用目标对象的方法,并且在此基础上进行扩展,如:记录日志等。

优点:可以做到在不修改目标对象的基础上,对目标对象进行扩展。
缺点:由于目标对象跟代理对象要实现相同的接口,如果接口新增方法,代理对象也要新增,目标对象跟代理对象需要同时进行维护,维护比较麻烦。

动态代理

动态代理的特点:
1. 动态代理代理对象不需要实现接口;
2. 代理对象是通过JDK的api在内存中动态生成的;
3. .动态代理也叫JDK代理或接口代理;

动态代理很好的弥补了静态代理的缺点。生成代理类的API方法java.lang.reflect.Proxy,调用newProxyInstance方法可以生成代理类,该方法有三个参数:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的;
interfaces:目标对象实现的接口的类型,使用泛型方式确认类型;
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入;

示例:
代理工厂类,利用getProxyInstance可以生成代理对象:

public class ProxyFactory {

    //目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 生成代理对象
     * @return
     */
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler(){

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long t1 = System.currentTimeMillis();
                System.out.println("方法"+ method.getName() +"开始执行时间:" + t1);
                //执行目标对象方法
                //设置成可以访问
                method.setAccessible(true);
                Object returnValue = method.invoke(target,args);
                System.out.println("方法" + method.getName() +"执行共耗时:" + (System.currentTimeMillis() - t1)/1000);
                return returnValue;
            }
        });
    }
}

测试:

public class ProxyTest {

    public static void main(String[] args) {
        //目标对象
        IUserDao userDao = new UserDao();
        System.out.println("目标对象类型:" + userDao.getClass());

        ProxyFactory proxyFactory = new ProxyFactory(userDao);
        IUserDao proxyObject = (IUserDao)proxyFactory.getProxyInstance();
        System.out.println("代理对象的类型:"+proxyObject.getClass());
        //执行方法
        proxyObject.add();
    }
}

代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。

CGLIB动态代理

无论是静态代理还是JDK动态代理,要求目标方法都必须实现接口,但是有时候目标对象只是一个独立的对象,没有实现任何接口,这个时候可以通过以目标对象子类的方式实现代理,这种方法就叫CGLIB代理,也叫子类代理,它在内存中创建一个子类对象,并且实现对目标对象的扩展。

示例:

public class CglibProxy implements MethodInterceptor{

    //目标对象
    private Object target;

    public CglibProxy(Object target) {
        this.target = target;
    }

    //给目标对象创建代理对象
    public Object getProxyInstance(){
        //工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建子类(代理对象)
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long t1 = System.currentTimeMillis();
        System.out.println("方法"+ method.getName() +"开始执行时间:" + t1);
        //执行目标对象方法
        Object returnValue = method.invoke(target,args);
        System.out.println("方法" + method.getName() +"执行共耗时:" + (System.currentTimeMillis() - t1)/1000);
        return returnValue;
    }
}

测试:

public class ProxyTest {

    public static void main(String[] args) {
//        //目标对象
        IUserDao userDao = new UserDao();

        CglibProxy cglibProxy = new CglibProxy(userDao);
        IUserDao proxyObject = (IUserDao)cglibProxy.getProxyInstance();
        //执行方法
        proxyObject.add();
    }
}

比较:

     JDK动态代理目标对象必须实现一个或多个接口,但是CGLIB代理可以直接通过创建目标对象的一个子类就可以实现动态代理,只需引入cglib的jar就可以了,Spring AOP就实现了代理模式,底层就是实现了cglib,实现了方法的拦截。
     在Spring AOP编程中,如果容器中目标对象有实现接口,可以用JDK代理,如果没有实现任何接口,可以使用CGLIB代理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值