java 设计模式——结构模式——代理模式

最近在研究java 里面的代理模式,有一些自己的感悟,在此跟大家分享一下。
一、代理的本质:
自己不想做或不放方便做的事情,交给代理来做,这样自己就可以安心做好自己的本质工作了。
二、代理的意义:
1.代理可以实现代码的松耦合,使各个模块各司其职,方便以后扩展和维护。
2.代理还可以,还可以对原有的功能进行扩展和加强,
三、代理的分类:
代理分为静态代理,动态代理(jdk 动态代理、cglib 动态代理)

在这里插入图片描述
四、动态代理的应用场景
1.统计每个api 的请求耗时。
2.统一的日志输出。
3.校验被调用的api 是否已登录和权限鉴定。
4.日志、监控、事务
5.框架中的应用:spring 中AOP 的切面编程;RPC 框架中的远程调用。
五、简单demo
1.静态代理

(1)IHuman接口

package com.example.demo.service;

public interface IHuman {
    void eat();
    void speak();
}

(2)Student 实现IHuman接口

package com.example.demo.po;

import com.example.demo.service.IHuman;
import lombok.extern.slf4j.Slf4j;

/**
 * @description:
 * @author:houqd
 * @time: 2021/7/14 17:22
 */
@Slf4j
public class Student implements IHuman {
    @Override
    public void eat() {
        log.info("student eat...");
    }

    @Override
    public void speak() {
        log.info("student speak...");
    }
}

(3) Teacher 实现Ihuman 接口

package com.example.demo.po;

import com.example.demo.service.IHuman;
import lombok.extern.slf4j.Slf4j;

/**
 * @description:
 * @author:houqd
 * @time: 2021/7/14 17:23
 */
@Slf4j
public class Teacher implements IHuman {
    @Override
    public void eat() {
        log.info("teacher eat...");
    }

    @Override
    public void speak() {
        log.info("teacher speak...");
    }
}

(4)测试

package com.example.demo.danamicproxy.jdkproxy;

import com.example.demo.po.Student;
import com.example.demo.service.IHuman;
import lombok.extern.slf4j.Slf4j;

/**
 * @description:
 * @author:houqd
 * @time: 2021/7/14 17:25
 */
@Slf4j
public class ProxyStatic implements IHuman {
    private IHuman human;

    public ProxyStatic(IHuman human) {
        this.human = human;
    }

    @Override
    public void eat() {
        log.info("eat before...");
        this.human.eat();
        log.info("eat after...");
    }

    @Override
    public void speak() {

    }

    public static void main(String[] args) {
        ProxyStatic proxyStatic = new ProxyStatic(new Student());
        proxyStatic.eat();


    }
}

2.jdk 动态代理
JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是JDK中所有要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中有一定的局限性,而且使用反射的效率也不高

(1)实现InvocationHandler接口,及获取代理实例

package com.example.demo.danamicproxy.jdkproxy;

import com.example.demo.service.IHuman;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @description:
 * @author:houqd
 * @time: 2021/7/14 17:45
 */
@Slf4j
public class JdkDynamicInvocationhandler implements InvocationHandler {
    private Object target;
    public JdkDynamicInvocationhandler(Object target){
        this.target = target;
    }

    /**
     *
     * @param proxy 通过类加载器和接口生成的代理对象
     * @param method 被代理的方法
     * @param args 被代理的方法参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("Do something Before");
        Object invoke = method.invoke(target, args);// 通过java 反射实现对目标类的调用
        log.info("Do something after");
        return invoke;
    }

    /**
     * 获取代理实例:
     * 第一个参数为类加载器,
     * 第二个参数为目标类接口,
     * 第三个参数为InvocationHandler接口的自定义实现
     * @param <T>
     * @return
     */
    public <T> T getProxy(){
        return (T) Proxy.newProxyInstance(IHuman.class.getClassLoader(),target.getClass().getInterfaces(),this);
    }
}


(2)测试

package com.example.demo.danamicproxy.jdkproxy;

import com.example.demo.po.Student;
import com.example.demo.po.Teacher;
import com.example.demo.service.IHuman;

/**
 * @description:
 * @author:houqd
 * @time: 2021/7/14 17:52
 */

public class TestDynamic {
    public static void main(String[] args) {
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");


        IHuman proxy = new JdkDynamicInvocationhandler(new Student()).getProxy(); // 必须用接口来接收,用类不可以
        proxy.eat();

        IHuman proxy2 = new JdkDynamicInvocationhandler(new Teacher()).getProxy();// 必须用接口来接收,用类不可以
        proxy2.eat();
    }
}

3.cglib 动态代理
使用cglib是实现动态代理,不受代理类必须实现接口的限制,因为cglib底层是用ASM框架,使用字节码技术生成代理类,你使用Java反射的效率要高,cglib不能对声明final的方法进行代理,因为cglib原理是动态生成被代理类的子类

(1)实现MethodInterceptor 拦截器

package com.example.demo.danamicproxy.cglib;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @description: 通过实现方法拦截器,来拦截父类方法,同时从切入点织入其他方法来增强功能
 * @author:houqd
 * @time: 2021/7/15 11:03
 */

public class TargetInterceptor implements MethodInterceptor {
    /**
     *
     * @param obj 代理类对象
     * @param method  当前被代理拦截的方法
     * @param args  拦截方法的参数
     * @param methodProxy 代理类对应目标类的代理方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib 代理前。。。");
        Object result = methodProxy.invokeSuper(obj, args);//
        System.out.println("Cglib 代理后。。。");
        return result;
    }
}

(2)生成cglib代理子类

package com.example.demo.danamicproxy.cglib;

import org.springframework.cglib.proxy.Enhancer;

/**
 * @description: 生成Cglib 代理子类
 * @author:houqd
 * @time: 2021/7/15 11:02
 */

public class CglibProxy {
    public static Object getProxy(Class<?> clazz){
        // 通过Enhancer 来生成代理子类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new TargetInterceptor());
        return enhancer.create();
    }


}

(3)测试

package com.example.demo.danamicproxy.cglib;

import com.example.demo.po.Student;
import com.example.demo.po.Teacher;
import com.example.demo.service.IHuman;

/**
 * @description:
 * @author:houqd
 * @time: 2021/7/15 11:15
 */

public class TestCglibProxy {
    public static void main(String[] args) {
        // 可以代理接口
        IHuman human = (IHuman) CglibProxy.getProxy(Student.class);
        human.eat();

        // 也可以代理类(但是类必须是非fianl,被代理方法也是fianl)
        Teacher teacher = (Teacher) CglibProxy.getProxy(Teacher.class);
        teacher.eat();
    }
}

六、小结
1.jdk 动态代理:
(1)代理对象:只能对实现接口的类进行代理。
(2)执行效率:jdk1.8 之前 不如cglib 快,jdk1.8做了优化甚至比cglib 还快。
(3)Spring 框架默认使用jdk 动态代理,当该类没有实现接口时转为cglib 动态代理。
(4)实现原理:
①通过实现 InvocationHandler 接口创建自己的调用处理器;
通过classLoader 和接口 ,生成代理类class。
②通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
③通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
④通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

2.cglib 动态代理:
(1)代理对象:对类实没实现接口不在意,只要该类是非fianl 类型,方法是非fianl的即可。
(2) CGLIB的核心类:
net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是 Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;

第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。

(3)实现原理:
cglib是一个java字节码的生成工具,它动态生成一个被代理类的子类,子类重写被代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值