设计模式之代理模式

Java设计模式(代理模式)

Java中的设计模式-代理模式

  • 静态代理(定义一个平级的实现类完成代理模式)

  • 动态代理

         — JDK动态代理(定义拦截器实现InvocationHandler接口,通过Proxy产生代理对象)
    
         —CGLIB动态代理(定义拦截器实现MethodInterceptor接口,通过Enhancer类生成代理对象)
    

静态代理

代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object.

静态代理就是通过创建平级的实现类的形式完成代理,能够实现代理的优势,但是同时缺点也很多:

以下就是基于静态代理进行的操作。静态代理存在如下几个缺点:

1、如果项目中存在N个接口,那么就会产生N个对应的实现类。我们的代理类也会随着实现类的增加而增加。

2、随着接口中的功能的增加,实现类中的功能也会同步增加。我们的代理类中的增强方法也会增加。

3、从严格意义上来讲:我们定义的增强的内容(事务处理)。其实是没有达到彻底复用。

4、优点:就是通过代理的方式,完成了目标对象的代理,并且在方法中置入了增强的处理,而且没有针对源代码进行修改。

  • 需要代理的接口
package com.sin.service;

/**
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public interface ProxySubject {
    public String inrtroduce(String name);
}
  • 实现类
package com.sin.service.impl;

import com.sin.service.ProxySubject;

/**
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public class ProxySubjectImpl implements ProxySubject {
    @Override
    public String inrtroduce(String name) {
        System.out.println("我的名字是:" + name);
        return name;
    }
}
  • 代理类
package com.sin.proxy;

import com.sin.service.ProxySubject;
import com.sin.service.impl.ProxySubjectImpl;

/**
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public class StaticProxy implements ProxySubject {

    private ProxySubjectImpl proxySubject;


    public StaticProxy(ProxySubjectImpl proxySubject){
        this.proxySubject = proxySubject;
    }

    @Override
    public void inrtroduce(String name) {
        System.out.println("调用前业务");
        proxySubject.inrtroduce(name);
        System.out.println("调用后的业务");
    }
}
  • 测试
package com.sin.test.proxy;

import com.sin.proxy.StaticProxy;
import com.sin.service.ProxySubject;
import com.sin.service.impl.ProxySubjectImpl;
import org.junit.Test;

/**
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public class StaticProxyTest {
    @Test
    public void test(){
        ProxySubjectImpl  proxySubject = new ProxySubjectImpl();

        StaticProxy staticProxy = new StaticProxy(proxySubject);
        staticProxy.inrtroduce("张三");
    }
}
  • 测试结果

在这里插入图片描述

JDK动态代理

动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的· 过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

Java1.3 以后, JAVA 实现了动态代理技术,允许开发者在运行期创建接口的代理实例 。

JDK 的动态代理主要使用的是 java.lang.reflect 包中的Proxy 和 InvocationHandler。

  • InvocationHandler 是接口,可以通过实现该接口来定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起 。
  • Proxy 利用 InvocationHandler 动态创建出一个实现某一接口的实例,来生成目标类的代理对象 。
//自定义定获取被代理接口实现类的实例对象
public <T> T getProxy() {
	        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                                              target.getClass().getInterfaces(), 
                                              this);
}

提示!

jdk动态代理必须要依赖于接口。

  • 需要动态代理的接口
package com.sin.service;

/**
 * 需要动态代理的接口
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public interface JDKAgentSubjectService {
    /**
     * 你好
     * @param name
     * @return
     */
    public String sayHello(String name);

    /**
     * 再见
     * @return
     */
    public String sayGoodBye();
}
  • 需要代理的实际对象
package com.sin.service.impl;

import com.sin.service.JDKAgentSubjectService;

/**
 * 实例接口对象
 *
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public class JDKAgentSubjectServiceImpl implements JDKAgentSubjectService {
    /**
     * 你好
     *
     * @param name
     * @return
     */
    @Override
    public String sayHello(String name) {
        return "你好" + name;
    }

    /**
     * 再见
     *
     * @return
     */
    @Override
    public String sayGoodBye() {
        return "再见";
    }
}
  • 调用处理器实现类
package com.sin.proxy;

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

/**
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public class InvocationHandlerImpl  implements InvocationHandler {

    //要代理的对象
    private Object subject;

    public InvocationHandlerImpl(Object subject){
        this.subject = subject;
    }

    /**
     * 负责集中处理动态代理上的所有方法调用
     * @param proxy 代理类实例
     * @param method 被调用的方法对象
     * @param args 调用参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("模拟的添加操作");

        System.out.println("Method" + method);

        Object value  = method.invoke(subject,args);

        System.out.println("接收后模拟的添加操作");

        return value;
    }
}
  • 测试
package com.sin.test.proxy;

import com.sin.proxy.InvocationHandlerImpl;
import com.sin.service.JDKAgentSubjectService;
import com.sin.service.impl.JDKAgentSubjectServiceImpl;
import org.junit.Test;

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

/**
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public class InvocationHandlerTest {

    @Test
    public void testReal(){
        //代理的对象
         Object  subjectService =new JDKAgentSubjectServiceImpl();

        /**
         * InvocationHandlerImpl 实现了InvocationHandler接口,实现方法调用从代理类到委托类的发派转发
         * 内部包含了指向委托类实例的引用,用于真正执行分派转发过来的方法调用
         * 要代理那个对象就将那个对象传进来,最后通过该对象来调用其方法
         */
        InvocationHandler handler=  new InvocationHandlerImpl(subjectService);

        ClassLoader loader = subjectService.getClass().getClassLoader();
        Class[] interfaces = subjectService.getClass().getInterfaces();

        JDKAgentSubjectService subject = (JDKAgentSubjectService) Proxy.newProxyInstance(loader,interfaces,handler);

        System.out.println("要动态代理对象的类型:"+subject.getClass().getName());

        String hello = subject.sayHello("张三");
        System.out.println(hello);

        String bye = subject.sayGoodBye();
        System.out.println(bye);
    }
}
  • 测试结果

在这里插入图片描述

CGLIB动态代理

采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。

实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。

  • 要代理对象接口
package com.sin.service;

/**
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public interface UserServiceCGlib {
    public void addUser();
}
  • 要代理对象接口实现类
package com.sin.service.impl;

import com.sin.service.UserServiceCGlib;

/**
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public class UserServiceCGlibImpl implements UserServiceCGlib {
    @Override
    public void addUser() {
        System.out.println("增加了一条用户");
    }
}
  • CGlib动态代理实现类
package com.sin.proxy;

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

import java.lang.reflect.Method;

/**
 * CGlib 动态代理实现类
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public class MethodInterceptorImpl implements MethodInterceptor {

    //CGlib需要代理的对象
    private Object object;

    public Object createProxyObject( Object o){
         this.object = o;

         //通过CGlib动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();

        //设置Enhancer对象的父类
        enhancer.setSuperclass(o.getClass());

        //设置Enhancer的回顾对象
        enhancer.setCallback(this);

        //设置Enhancer对象的父类
        Object o1 = enhancer.create();
        return o1;
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object obj ;
        System.out.println("调用前的业务");
        obj = method.invoke(object,objects);
        System.out.println("调用后的业务");
        return obj;
    }
}
  • 测试
package com.sin.test.proxy;

import com.sin.proxy.MethodInterceptorImpl;
import com.sin.service.impl.UserServiceCGlibImpl;
import org.junit.Test;

/**
 * @author sin
 * @date 2022/10/27
 * @apiNote
 */
public class MethodInterceptorTest {
    @Test
    public void test(){
        UserServiceCGlibImpl userServiceCGlib = new UserServiceCGlibImpl();

        MethodInterceptorImpl methodInterceptorimpl = new MethodInterceptorImpl();

        UserServiceCGlibImpl serviceCGlib  = (UserServiceCGlibImpl)methodInterceptorimpl.createProxyObject(userServiceCGlib);

        serviceCGlib.addUser();
    }
}
  • 测试结果

在这里插入图片描述

静态代理

  • 静态代理模式并没有做到事务的重用
  • 假设dao有100个类,100个proxy,接口中有多少方法,在proxy层就得实现多少方法,有多少方法就要开启和提交多少事务
  • 如果一个proxy实现了多个接口,如果其中的一个接口发生变化(添加了一个方法),那么proxy也要做相应改变

JDK动态代理总结

动态代理类:在程序运行时,运用反射机制动态创建而成。
JDK的动态代理必须具备四个条件:1、目标接口 2、目标类 3、拦截器 4、代理类

  • 因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
  • 生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
  • 利用JDKProxy方式必须有接口的存在。
  • invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
  • 缺点:
    • 在拦截器中除了能调用目标对象的目标方法以外,功能是比较单一的,在这个例子中只能处理事务
    • 使用JDK创建代理有一个限制,即JDK动态代理只能为接口创建代理。

CGLIB动态代理总结:

  • CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
  • 用CGlib生成代理类是目标类的子类。
  • 用CGlib生成 代理类不需要接口
  • 用CGLib生成的代理类重写了父类的各个方法。
  • 拦截器中的intercept方法内容正好就是代理类中的方法体

JDK代理和CGLib 代理区别

  • JDK动态代理只能针对接口实现类生成代理实例,而不能针对类;也就是说它是面向接口的
  • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,
  • JDK 代理是java 语言自带的,无需加载第三方类去实现,CGLIB是一个基于ASM实现的
  • 使用动态代理的对象必须实现一个或多个接口
  • 使用cglib代理的对象则无需实现接口,达到代理类无侵入。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陆卿之

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值