设计模式——代理模式

来源:互联网 发布:java相关文献 编辑:程序博客网 时间:2024/06/17 20:53

代理模式

名词解释(百度百科):

代理模式为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
抽象角色通过接口或抽象类声明真实角色实现的业务方法。
真实角色实现抽象角色(jdk代理),定义真实角色所要实现的业务逻辑,供代理角色调用。
代理角色实现抽象角色(jdk代理),是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

对这些名词有一定概念之后继续阅读可以深入理解代理模式。

代理又分为静态代理和动态代理

我们先从静态代理开始了解代理模式的工作流程

静态代理

抽象角色
java代码:
/** * 抽象角色 *  * @author Javen * */public interface UserService {    /**     *      * @Description:测试方法     * @param     * @throws     */    public void getName();}


真实角色

java代码:

/** * jdk动态代理——被代理类 *  * @author Javen * */public class UserServiceImpl implements UserService {    @Override    public void getName() {        System.out.println("---------getName()----------");    }}


代理角色

java代码:

/** * 静态代理——代理角色 *  * @author Javen * */public class ProxyUserService implements UserService {    private UserServiceImpl userServiceImpl; // 以真实角色作为代理角色的属性    public ProxyUserService() {}    public void getName() { // 该方法封装了真实对象的getName方法        beforeGetName();// 真实对象方法执行之前的操作        if (userServiceImpl == null) {            userServiceImpl = new UserServiceImpl();        }        userServiceImpl.getName(); // 此处执行真实对象的getName方法        afterGetName();// 真实对象方法执行之后的操作    }    private void beforeGetName() {        // 插入真实对象执行前的操作
    System.out.println("---------beforeGetName()----------");    }    private void afterGetName() {        // 插入真实对象执行后的操作
    System.out.println("---------afterGetName()----------");    }}

客户端测试类

java代码:

/** * 静态代理测试类 *  * @author Javen * */public class StaticProxyTest {    public static void main(String[] args) {        UserService userService = new ProxyUserService();        userService.getName();    }}


输出结果:

---------beforeGetName()-------------------getName()-------------------afterGetName()----------

由以上代码可以看出,原本由真实角色UserService执行的getName()方法,现在由代理角色ProxyUserService执行,而且还封装了beforeGetName()和afterGetName()方法,在真实角色执行方法前后加入自定义功能。但是也能看出这种代理模式需要一个真实角色对应一个代理角色,在实际开发中,大量使用静态代理会导致类的急剧增加,所以这时候就需要使用动态代理了,在运行时动态创建代理角色。

动态代理

抽象角色和真实角色沿用上面的代码。


代理角色
java代码:
/** * 动态代理类 *  * @author Javen * */public class MyInvocationHandler implements InvocationHandler {    private Object target;    MyInvocationHandler(Object target) {        super();        this.target = target;    }    /**     *      * @Title: 调用方法之前的操作     * @Description:     * @param     * @throws     */    public void doBefore() {        System.out.println("---------doBefore()----------");    }    /**     *      * @Title: 调用方法之后的操作     * @Description:     * @param     * @throws     */    public void doAfter() {        System.out.println("---------doAfter()----------");    }    @Override    public Object invoke(Object obj, Method method, Object[] args) throws Throwable {        doBefore();        Object result = method.invoke(target, args);        doAfter();        return result;    }}


代理角色有真实角色属性,构造方法;实现InvocationHandler接口,实现invoke(Object obj, Method method, Object[] args)方法,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的getName(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。


客户端测试类
java代码:
/** * 动态代理测试类 *  * @author Javen * */public class ProxyMain {    public static void main(String[] args) {        UserService userService = new UserServiceImpl();        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(userService);        UserService userServiceProxy =                (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(),                        myInvocationHandler);        userServiceProxy.getName();    }}


Proxy代理类有静态方法:Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler invocationHandler):返回代理类的一个实例,返回后的代理类可以被当作真实角色使用,可使用真实角色中申明的方法。

其实动态代理就是在运行时,通过指定需要实现的接口,然后生成class文件,将该class文件的实例自然就能指向为任意一个已实现的接口,相当于生成了一个代理类实例,但是这个代理类的具体操作封装在InvocationHandler的invoke(Object obj, Method method, Object[] args)中实现,所以在生成代理类的时候还需提供一个handler,由它接管实际操作。
通过这种方式,抽象角色、真实角色都能在运行时动态改变,从而实现了非常灵活的动态代理关系。

补充:
动态代理又分为JDK动态代理和CGLIB动态代理,JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,CGLIB是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
Spring是依靠什么来判断采用哪种代理策略来生成AOP代理呢?以下代码就是Spring的判断逻辑  
    //org.springframework.aop.framework.DefaultAopProxyFactory   
    //参数AdvisedSupport 是Spring AOP配置相关类   
    public AopProxy createAopProxy(AdvisedSupport advisedSupport)   
            throws AopConfigException {   
        //在此判断使用JDK动态代理还是CGLIB代理   
        if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()   
                || hasNoUserSuppliedProxyInterfaces(advisedSupport)
) {   
            if (!cglibAvailable) {   
                throw new AopConfigException(   
                        "Cannot proxy target class because CGLIB2 is not available. "  
                                + "Add CGLIB to the class path or specify proxy interfaces.");   
              }   
            return CglibProxyFactory.createCglibProxy(advisedSupport);   
          } else {   
            return new JdkDynamicAopProxy(advisedSupport);   
          }   
      }  

advisedSupport.isOptimize()与advisedSupport.isProxyTargetClass()默认返回都是false,所以在默认情况下目标对象有没有实现接口决定着Spring采取的策略,如果目标对象没有实现接口,则默认会采用CGLIB代理;如果目标对象实现了接口,可以强制使用CGLIB实现代理(添加CGLIB库,并在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。


应用场景:

SpringAop,日志处理等。

原创粉丝点击