代理模式及案例

Posted hnzkljq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理模式及案例相关的知识,希望对你有一定的参考价值。

 *_* 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.

好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.

代理模型的三种方式
  1. 静态代理
  2. 动态代理  
   JDK动态代理
   CGLIB动态代理

一、静态代理

  在不修改目标对象方法的基础上,对目标对象方法进行扩展。

  

//Service接口
public interface UserService {

    void save();

    void delete();

    void update();

}

 

//Service实现
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("新增用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }
}

 

/**
 * 静态代理类
 * 实现日志扩展功能
 *要求:
 *1)和目标(类)实现同样的接口
 *2)在静态代理类中传入目标对象实例,以便调用目标对象的方法
 *3)可以在静态代理类的方法中添加代理逻辑代码
 */
public class LogProxy implements UserService{

    //接收目标对象实例
    private UserService userService;

    //使用构造方法传入目标对象实例
    public LogProxy(UserService userService){
        this.userService = userService;
    }

    @Override
    public void save() {
        System.out.println("before=====save");
        //调用目标对象的方法
        userService.save();
        System.out.println("after=====save");
    }

    @Override
    public void delete() {
        System.out.println("before=====save");
        //调用目标对象的方法
        userService.delete();
        System.out.println("after=====save");
    }

    @Override
    public void update() {
        System.out.println("before=====save");
        //调用目标对象的方法
        userService.update();
        System.out.println("after=====save");
    }
}
 
package com.lemon.a_staticproxy;

import com.lemon.service.UserService;
import com.lemon.service.impl.UserServiceImpl;

//测试 
public class Test {
    public static void main(String[] args) {
        //使用静态代理模式
        //1.创建目标对象
        UserService userService = new UserServiceImpl();
        //2.创建静态代理类对象
        UserService proxy = new LogProxy(userService);
        //3.调用代理类的方法
        proxy.save();
        proxy.update();
        proxy.delete();
    }
}

 

静态代理的缺点:

 1)一个静态代理类只能代理一个目标类

    2)静态代理类的每个方法都需要编写重复的代理逻辑,代码比较冗余

二、JDK动态代理

  前提:目标对象有接口的情况

/**
 * 用于生成JDK动态代理对象的工具类
 */
public class LogProxy {

    /**
     * 生成JDK动态代理对象的方法
     *
     * 返回值:生成的JDK动态代理对象
     * 参数:target, 传入目标对象
     */
    public static Object getProxy(Object target){
        /**
         * 参数一:类加载器,JDK动态代理的底层使用类加载器来生成的一个动态类的。通常传入当前类的类加载器即可!!!(LogProxy.class.getClassLoader())
         * 参数二:目标对象的接口列表(所有接口),通常使用目标对象获取接口列表(target.getClass().getInterfaces())
         * 参数三:接口。 该用于编写  代理类的代理逻辑代码。通常我们要提供InvocationHandler接口的实现类(匿名内部类的方式提供)
         */
        return Proxy.newProxyInstance(
                LogProxy.class.getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {

                    /**
                     * invoke方法:用于编写 代理类的代理逻辑代码。
                     *      invoke方法在什么时候会被调用?
                     *            该方法会在调用JDK代理对象的每个方法的时候被执行!!!!!
                     *
                     * @param proxy: 生成JDK动态代理对象
                     * @param method: 目标对象的执行方法的对象
                     * @param args: 目标对象的方法参数列表
                     * @return 返回值:目标对象方法执行后的返回结果
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //获取目标对象的方法名称
                        String methodName = method.getName();

                        System.out.println("before======"+methodName);

                        //获取方法的参数列表
                       /* if(args!=null)
                        System.out.println(Arrays.asList(args));*/

                        //调用目标对象的方法
                        /**
                         * 参数一:执行的对象(必须传入目标对象,不能传入代理对象,否则会死循环)
                         * 参数二:方法的参数列表
                         */
                        Object result = method.invoke(target,args);

                        System.out.println("after======"+methodName);

                        return result;
                    }
                }
        );
    }
}

 

 

三、CGLIB动态代理

  目标对象可有可无

<!-- 导入spring-core(包含cglib依赖) -->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
    </dependencies>

 

/**
 * 演示JDK动态代理
 */
public class Demo {

    public static void main(String[] args) {
        //1.创建目标对象
        UserService userService = new UserServiceImpl();
        //2.创建静态代理类对象
        UserService proxy = (UserService) LogProxy.getProxy(userService);
        //3.调用代理类的方法
        proxy.save();
        proxy.delete();
        proxy.update();
    }
}

 

/**
 * 用于生成Cglib代理对象的工具类
 */
public class LogProxy {


    /**
     * 生成Cglib代理对象
     * 返回值:生成的Cglib子类代理对象
     * 参数:目标对象(目标对象没有接口)
     */
    public static Object getProxy(Object target){

        /**
         * 方法返回值:生成的Cglib子类代理对象
         * 参数一:目标对象的类型(target.getClass()) (其实目标对象的类型就是Cglib代理对象 的  父类)
         * 参数二:MethodInterceptor接口,用于编写 代理对象的代理逻辑代码。通常提供MethodInterceptor接口的匿名内部即可
         */
        return Enhancer.create(
                target.getClass(),
                new MethodInterceptor() {
                    /**
                     * intercept方法:在调用代理对象的每个方法的时候会执行
                     * @param proxy: 生成的代理对象
                     * @param method: 目标对象的方法对象
                     * @param args: 目标对象的方法参数列表
                     * @param methodProxy: 代理对象的方法对象
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

                        //获取目标对象的方法名称
                        String methodName = method.getName();

                        System.out.println("before======"+methodName);

                        /**
                         * 调用目标对象的方法
                         */
                        //方式一:直接使用目标对象 调用 目标对象的方法
                        Object result = method.invoke(target,args);
                        //方式二:使用代理类(子类)调用 目标对象(父类)的方法
                        //invokeSuper: 调用父类的方法
                        //Object result = methodProxy.invokeSuper(proxy,args);

                        System.out.println("after====="+methodName);

                        return result;
                    }
                }
        );
    }

}

 

/**
 * 演示CJLIB动态代理
 */
public class Demo2 {

    public static void main(String[] args) {
        //1.创建目标对象
        UserService userService = new UserServiceImpl();
        //2.创建静态代理类对象
        UserService proxy = (UserService) LogProxy.getProxy(userService);
        //3.调用代理类的方法
        proxy.save();
        proxy.delete();
        proxy.update();
    }
}

 

 *_* Spring aop 案例

1 、创建service接口和实现

 

public interface LemonService {

    void save();
    void delete();
    void update();

}

 

public class LemonServiceImpl implements LemonService {
    @Override
    public void save() {
        System.out.println("新增");
    }

    @Override
    public void delete() {
        System.out.println("删除");
    }

    @Override
    public void update() {
        System.out.println("修改");
    }
}

2、创建切面类

/**
 * 日志切面类
 */
public class LogAspect {

    /**
     * 通知方法(插入到目标方法的前面)
     */
    public void writeLog(){
        System.out.println("before=======");
    }

}

3、配置切面类

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 1.创建目标对象 -->
    <bean id="lemonService" class="com.lemon.service.impl.LemonServiceImpl"/>

    <!-- 2.创建切面对象 -->
    <bean id="logAspect" class="com.lemon.log.LogAspect"/>

    <!-- 3.切面配置 -->
    <aop:config>
        <!-- 切面配置 = 通知(advice)+切入点(pointcut)-->
        <!--
            ref: 引用切面类对象
         -->
        <aop:aspect ref="logAspect">
            <!-- 定义切入点 -->
            <!--
                id: 定义切入点的别名
                expression: 切入点表达式(用于定义需要切入的方法)
             -->
            <aop:pointcut id="pt" expression="execution(* com.lemon.service.impl.LemonServiceImpl.*(..))"/>

            <!-- 定义通知 -->
            <!--
               method: 使用切面类的哪个方法作为通知方法
               pointcut-ref: 关联切入点
             -->
            <!-- 前置通知 -->
            <aop:before method="writeLog" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>
</beans>

 

 

 

 

 

以上是关于代理模式及案例的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之代理模式(Proxy)详解及代码示例

设计模式-代理模式

代理模式(静态代理)

代理模式(动态)

java设计模式---代理模式(案例解析)

代理模式(静态代理jdk动态代理CGLib动态代理)