代理模式

代理模式

代理这个词很好理解,从它的名字我们也能大概猜到什么意思,我们自己不想做的事委托给别人去做,在生活场景经常碰到。在编程中,提升为一种设计模式,提供了对目标对象另外的一种访问方式,即通过代理对象去访问目标对象,这样做的好处在于不影响目标对象,从而增强目标对象的功能,也可以说扩展了目标对象的功能,这种模式在各种框架中经常用到,建议大家好好研究下。接下来通过简单的代码体会这种模式的使用。

1.静态代理

静态代理使用时需要定义接口或者父类,目标对象和代理对象需要实现同一接口或继承相同父类

代码示例

定义一个接口IUserDao,save()方法模拟动作,目标对象UserDao实现这个接口,如果事静态模式需要代理对象也实现这个接口,调用的时候通过调用代理对象的方法来调用目标对象
接口:IUserDao.java

public interface IUserDao {
    void save();
}

目标对象:UserDao.java

public class UserDao implements IUserDao {
    @Override
    public void save() {
        System.out.println("-----已经保存数据------------");
    }
}

代理对象:UserDaoProxy.java

public class UserDaoProxy implements IUserDao {

    private IUserDao target;
    public UserDaoProxy(IUserDao target){
        this.target = target;
    }
    @Override
    public void save() {
        System.out.println("代理对象执行之前");
        target.save();
        System.out.println("代理对象执行之后");
    }
}

测试类:ProxyTest.java

public class ProxyTest {
    public static void main(String[] args) {
        IUserDao target = new UserDao();
        UserDaoProxy proxy = new UserDaoProxy(target);
        proxy.save();
    }
}

运行结果:

静态代理总结

优点:可以在不修改原有对象的功能上,对目标功能进行扩展;
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护;
如果要避免这个缺点就要使用动态代理。

2.动态代理

2.1 JDK动态代理

JDK动态代理不需要代理对象实现接口,但是目标对象必须实现接口,代理对象是通过JDK的API在内存中构建代理对象(在java.lang.reflect.Proxy包下)
接口类IUserDao.java以及接口实现类UserDao是一样的,没有做修改.在这个基础上,增加一个代理工厂类
ProxyFactory.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {
    private IUserDao target;
    public  ProxyFactory(IUserDao target){
        this.target = target;
    }
    public Object getPorxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开始目标方法之前");
                        //执行目标对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("执行目标方法之后");
                        return returnValue;
                    }
                }
        );
    }

}

测试类ProxyTest.java

public class ProxyTest {
    public static void main(String[] args) {
        IUserDao target = new UserDao();
        //UserDaoProxy proxy = new UserDaoProxy(target);
        //proxy.save();
        System.out.println(target.getClass());
        IUserDao userDao = (IUserDao) new ProxyFactory(target).getPorxyInstance();
        System.out.println(userDao.getClass());
        userDao.save();
    }
}

运行结果:

可以看到运行效果和静态代理是一样的,但是实现的方式不一样,JDK动态代理是在内存中动态生成一个对象,代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

2.2 Cglib动态代理

上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做Cglib代理
StudentDao.java

public class StudentDao {
    public void save(){
        System.out.println("-------保存学生数据-------");
    }
}

ProxyFactory2.java

public class ProxyFactory2 implements MethodInterceptor {
    private  Object target;
    public ProxyFactory2(Object target){
        this.target = target;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始执行保存前...");
        Object result = method.invoke(target,objects);
        System.out.println("保存后...");
        return result;
    }

    public Object getProxyInstance() {
        //工具类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);

        return enhancer.create();
    }
}

测试类:ProxyTest.java

public class ProxyTest {
    public static void main(String[] args) {
        StudentDao target = new StudentDao();
        StudentDao proxy = (StudentDao)new ProxyFactory2(target).getProxyInstance();
        proxy.save();
    }
}

运行结果:

动态代理总结:

如果目标对象有实现接口,用JDK代理.
如果目标对象没有实现接口,用Cglib代理.

posted @ 2019-05-17 14:18  相看两不厌  阅读(339)  评论(0)    收藏  举报