微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

三Spring-静态代理和动态代理

(三)Spring-静态代理和动态代理

一、代理模式

代理就是帮人做一些事情

为什么要学习代理模式?因为这就是SpringAOP的底层。SpringAOP是重点。

代理模式的分类:

  • 静态代理
  • 动态代理

二、静态代理

2.1 静态代理定义

角色分析:

  • 抽象角色:一般真实角色和代理角色会有个共同角色,会使用接口或抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色,我们一般会有一些附加操作
  • 客户:访问代理对象的人。

原来模式:

代理模式:

2.2 实现步骤

1 代理对象和被4代理对象共同实现1个接口

为什么要共同实现一个接口?经过实践,其实代理和被代理之间不需要强制实现一个接口,也能实现类似代理的功能,但是还是建议共同实现一个接口

我个人理解为:

  • 接口规范了需要共同实现的核心方法,强制实现类必须实现核心方法。比如租房,方便代理对象在核心方法调用被代理对象核心方法
  • 使得程序解耦,都可以用共同实现接口来做定义,如RentService。
  • 调用者看来都是一个类RentService。
package com.happy.service;

public interface RentService {

    void rent();
}

2 被代理对象(房东)

package com.happy.service.impl;

import com.happy.service.RentService;

public class Fangdong implements RentService{


    @Override
    public void rent() {
        System.out.println("房东:房东签订合同,收钱");
    }
}

3 代理对象(中介)

package com.happy.service.impl;

import com.happy.service.RentService;

public class Proxy implements RentService{
    private RentService fangdong;

    public Proxy(RentService fangdong) {
        this.fangdong = fangdong;
    }

    public Proxy() {
    }

    public RentService getFangdong() {
        return fangdong;
    }

    public void setFangdong(RentService fangdong) {
        this.fangdong = fangdong;
    }

    @Override
    public void rent() {
        System.out.println("中介:带租客看房");
        fangdong.rent();
        System.out.println("中介:缴税");
    }
}

4 调用访问者(租客)

package com.happy.service;

import com.happy.service.impl.Fangdong;
import com.happy.service.impl.Proxy;
import org.junit.Test;

public class Client {

    @Test
    public void rent(){
        RentService fangdong = new Fangdong();
        fangdong.rent();
        System.out.println("============================");
        //中介一般会在被代理对象以后,添加一些附加操作
        RentService proxy = new Proxy(fangdong);
        proxy.rent();
    }
}

2.3 实现核心总结

1 代理对象一定要持有被代理对象的引用

  • 一般能用组合的方式,不要用继承,组合也能实现在调用方法
  • 代理持有被代理对象引用后(组合),方便代理对象在自己方法调用被代理对象方法(一般同名,因为实现共同接口)。
  • 在代理方法内实现:

2 代理对象和被代理对象实现共同接口

2.4 代理模式的好处

  • 可以使真实觉得的操作更加纯粹!不用去关注一些公共的业务。
  • 公共业务就交给了代理角色。
  • 实现了业务的分工!
  • 公共业务发生了扩展的时候,方便接种管理!
  • 功能增强:当需要给原有代码增加非业务核心功能时,使用代理模式添加,可以避免修改原有代码
  • 控制访问:不让直接访问真实角色即被代理对象。
  • 不在原有的基础上纵向开发,使用静态代理就是横向开发,就是AOP底层的实现机制。

2.5 代理模式的坏处

  • 一个被代理(真实角色)就会产生和对应一个代理角色。代码量会翻倍,开发效率会变低。

2.6 真实运用场景

  • 要求可以利用代理模式给一个类的方法加日志记录
  • 并不改变原有类

实现的共同接口

package com.happy.service;

public interface UserService {

    void add();
    void delete();
    void update();
    void query();
}

被代理真实对象

package com.happy.service.impl;

import com.happy.service.UserService;

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("add操作");
    }

    @Override
    public void delete() {
        System.out.println("delete操作");
    }

    @Override
    public void update() {
        System.out.println("update操作");
    }

    @Override
    public void query() {
        System.out.println("query操作");
    }
}

代理对象

package com.happy.service.impl;

import com.happy.service.UserService;

public class UserServiceProxy implements UserService {
    UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log();
        userService.add();
    }

    @Override
    public void delete() {
        log();
        userService.add();
    }

    @Override
    public void update() {
        log();
        userService.add();
    }

    @Override
    public void query() {
        log();
        userService.add();
    }

    public void log() {
        System.out.println("记录日志!");
    }
}

调用

package com.happy.service;

import com.happy.service.impl.UserServiceImpl;
import com.happy.service.impl.UserServiceProxy;
import org.junit.Test;

public class UserServiceTest {

    @Test
    public void testUserService(){
        UserService userService = new UserServiceImpl();
//        new UserServiceProxy(userService);
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        userServiceProxy.setUserService(userService);
        userServiceProxy.delete();
        userServiceProxy.query();
    }
}

三、动态代理

3.1 动态代理定义

  • 动态代理和静态代理的角色一样。

  • 动态代理的代理类是动态生成的,不是我们直接写好的!不需要写对应的代理类的.java文件

3.2 动态代理分为3大类

1 基于接口的动态代理-JDK动态代理

  • JDK动态代理【我们在这里使用的】
  • JDK动态代理要求被代理对象必须要有接口,没有接口不能使用!
  • 使用java反射包中的类和接口实现动态代理的功能
  • 反射包为java.lang.reflect,里面有三个核心类:
    • InvocationHandler,Method,Proxy

2 基于类的动态代理-cglib动态代理

  • cglib
    • cglib是第三方的工具库,创建代理对象。

    • cglib的原理是继承cglib通过继承目标类,创建它的子类。

    • 在子类中重写父类同名的方法,实现动态的修改

    • 以为cglib是继承,需要重写方法所以要求目标类不能是final的,方法也不是final的

    • cglib的要求目标类比较宽松,只要能继承就可以了,cglib 在很多框架中都有使用,比如mybatis,spring AOP框架中都有。

3 基于字节码实现-JAVAsist

  • JAVAsist

3.3 JDK动态代理的三个核心相关类

1 Proxy

他是一个类,Proxy类,用于创建代理的对象(不需要创建代理类,直接代理对象)

  • 之前创建对象都是new一个类的构造方法(),即new 代理类的构造方法,现在我们是使用Proxy类的方法,代替new使用。

  • 如何使用:

    • 创建代理对象,使用静态方法 newProxyInstance()
    • 类似于静态代理的new Proxy() + 创建Proxy类的类文件
  • newProxyInstance()方法

    public static Object newProxyInstance(ClassLoader loader,
                                          类<?>[] interfaces,
                                          InvocationHandler h)
                                   throws IllegalArgumentException
    

    返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。

    Proxy.newProxyInstance因为与IllegalArgumentException相同的原因而Proxy.getProxyClass

    • 参数

      loader - 类加载器来定义代理类 ,负责向内存中加载对象的使用反射获取对象a的ClassLoader类,a.getClass().getClassLoader()

      interfaces - 代理类实现的接口列表 ,也是通过反射获取的。

      h - 调度方法调用调用处理函数 ,他是我们自己写的,见下面InvocationHandler

    • 结果

      具有由指定的类加载器定义并实现指定接口的代理类的指定调用处理程序的代理实例

2 InvocationHandler

他是一个接口,调用处理程序,接口中只有一个方法invoke(),他表示你的代理要干什么

package java.lang.reflect;

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

  • 一个参数:Object proxy,jdk创建的代理对象,无需赋值
  • 第二个参数:Method method,目标类中的方法,也不需要赋值,jdk提供。
  • 第三个参数, Object[] args,目标方法的参数,jdk提供。
  • 如何使用:
    • 创建类,实现接口InvocationHandler
    • 重写invoke()方法,把原来静态代理中代理类要完成的功能,写在这里

3 Method

他是一个类,反射Method类,表示方法,确切就是目标类的方法。通过method可以执行某个方法

如何使用:

  • 通过method可以执行某个目标类的方法,method.invoke()
  • 注意method.invoke()的invoke是Method类里面的方法,和前面InvocationHandler的invoke()不一样,只是同名。
  • method.invoke(目标对象,方法的参数),等同于静态代理的object.方法名()如proxy.rent()

4 小结:

通过上面三个结合使用,完成jdk动态代理的实现。

3.4 动态代理的好处

  • 静态代理的好处,动态代理的好处都有
  • 一个动态代理代理的是一个接口, 一般就是对应的一类业务
  • 一个动态代理可以代理多个类,只要实现一个接口就可以,不需要写代理类。而静态代理需要为每一个被代理类生成对应代理类(静态代理和被代理类需要实现一个共同接口)。
  • 所以动态代理是固定模式,对虽有被代理类都一样(只要实现了接口),可以把动态代理作为工具类

3.5 实现动态代理的步骤

1 实现接口,定义目标类要完成的功能

2 创建目标类,去实现接口

3 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能

package com.happy.service.impl;

import com.happy.service.RentService;

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

//一、创建InvocationHandler的实现类
public class MyInvocationHandler implements InvocationHandler {

    //    为了更加泛化,使得该类不再局限于UserService一个种类,使用Object
    Object target;
    //    Object target;

    public MyInvocationHandler() {
    }

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        //method传入,假设我们已经拿到目标类即被代理类的方法了
        vist();
//        target.rent();
//        动态代理用method方法反射调用方法,可以实现动态调用所有方法
//        而以前的静态方法,这里最多调用一类接口的方法,代理的对象有限,如果需要代理其他接口,则需再写一个静态代理
        res = method.invoke(target, args);
        return res;
    }

    public void vist() {
        System.out.println("租房前看房!");
    }
}

4 使用Proxy类的静态方法,创建代理对象,并把返回值转为接口类型。

package com.happy.service;

import com.happy.service.impl.Fangdong;
import com.happy.service.impl.MyInvocationHandler;
import com.happy.service.impl.UserServiceImpl;
import org.junit.Test;

import java.lang.reflect.Proxy;

public class TestJDKProxy {

    @Test
    public void test() {

        /* @CallerSensitive
        public static Object newProxyInstance
        (ClassLoader loader,
        Class<?>[] interfaces,
        InvocationHandler h)
                          */

//        二、创建代理对象,使用proxy
//        1.创建被代理目标对象(这个静态代理和动态代理都需要)
        RentService userService = new Fangdong();
//        2.创建InvocationHandler对象
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
//        3.动态通过构造器或者setter注入被代理对象
        myInvocationHandler.setTarget(userService);
//        4.创建动态代理对象
        RentService proxyInstance = (RentService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), myInvocationHandler);
//        5.执行动态代理方法
        proxyInstance.rent();
    }
}

四、优化动态代理作为工具类

4.1 使用普通Object

因为动态代理为固定范式,可以将他提取为公共工具类。

package com.happy.utils;

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

public class MyInvocationHandlerUtils implements InvocationHandler {
    Object target;

    public MyInvocationHandlerUtils() {
    }

    public MyInvocationHandlerUtils(Object target) {
        this.target = target;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        /*
        *    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        * 传入目标类的classLoader等,所以目标类对象应该动态传入工具类
        * */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    //    代理要实现的方法
//    1.被代理类的方法
//    2.增强
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        before();
        //method为jdk帮传入被代理类的method反射方法
        res = method.invoke(target, args);
        after();
        return res;
    }

    public void before() {
        System.out.println("before method!");
    }

    public void after() {
        System.out.println("after method!");
    }
}
package com.happy.service;

import com.happy.service.impl.Fangdong;
import com.happy.service.impl.MyInvocationHandler;
import com.happy.service.impl.UserServiceImpl;
import com.happy.utils.MyInvocationHandlerUtils;
import org.junit.Test;

import java.lang.reflect.Proxy;

public class TestJDKProxy {

    @Test
    public void test() {

        /* @CallerSensitive
        public static Object newProxyInstance
        (ClassLoader loader,
        Class<?>[] interfaces,
        InvocationHandler h)
                          */

//        二、创建代理对象,使用proxy
//        1.创建被代理目标对象(这个静态代理和动态代理都需要)
        RentService userService = new Fangdong();
//        2.创建InvocationHandler对象
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
//        3.动态通过构造器或者setter注入被代理对象
        myInvocationHandler.setTarget(userService);
//        4.创建动态代理对象
        RentService proxyInstance = (RentService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), myInvocationHandler);
//        5.执行动态代理方法
        proxyInstance.rent();
    }

    @Test
    public void testJDKProxyUtils() {
        Object fangdong = new Fangdong();
//        MyInvocationHandlerUtils myInvocationHandler = new MyInvocationHandlerUtils(fangdong);
//        或者使用无参构造+set
        MyInvocationHandlerUtils myInvocationHandler = new MyInvocationHandlerUtils();
        myInvocationHandler.setTarget(fangdong);
//        myInvocationHandler.getProxy 内部使用了Proxy类静态方法,传入target和myInvocationHandler,才能生成proxy
        RentService proxy = (RentService) myInvocationHandler.getProxy();
        proxy.rent();
    }
}

4.2 使用泛型

使用泛型后,在new MyInvocationHandler的时候用泛型传入类型RentService,可以不再强转。

package com.happy.utils;

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

//在实例化泛型类时,必须指定T的具体类型
public class MyInvocationHandlerUtils<T> implements InvocationHandler {
    T target;

    public MyInvocationHandlerUtils() {
    }

    public MyInvocationHandlerUtils(T target) {
        this.target = target;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(T target) {
        this.target = target;
    }

    public T getProxy() {
        /*
        *    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        * 传入目标类的classLoader等,所以目标类对象应该动态传入工具类
        * */
        T object=null;
        object= (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        return object;
    }

    //    代理要实现的方法
//    1.被代理类的方法
//    2.增强
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        System.out.println("执行方法:"+method.getName());
        before();
        //method为jdk帮传入被代理类的method反射方法
        res =  method.invoke(target, args);
        after();
        return res;
    }

    public void before() {
        System.out.println("before method!");
    }

    public void after() {
        System.out.println("after method!");
    }
}

package com.happy.service;

import com.happy.service.impl.Fangdong;
import com.happy.service.impl.MyInvocationHandler;
import com.happy.service.impl.UserServiceImpl;
import com.happy.utils.MyInvocationHandlerUtils;
import org.junit.Test;

import java.lang.reflect.Proxy;

public class TestJDKProxy {
    
    @Test
    public void testJDKProxyUtils() {
        RentService fangdong = new Fangdong();
//        MyInvocationHandlerUtils myInvocationHandler = new MyInvocationHandlerUtils(fangdong);
//        或者使用无参构造+set
        MyInvocationHandlerUtils<RentService> myInvocationHandler = new MyInvocationHandlerUtils<RentService>();
        myInvocationHandler.setTarget(fangdong);
//        myInvocationHandler.getProxy 内部使用了Proxy类静态方法,传入target和myInvocationHandler,才能生成proxy
        RentService proxy = myInvocationHandler.getProxy();

        proxy.rent();
    }
}

五、静态代理VS动态代理

静态代理 动态代理
1 代理类是自己手工实现的,自己创建一个java类 动态生成代理类(我们不需要手工写和生成
2 你所要代理的目标类是确定的 所要代理的目标类不确定,可以给不同的目标类随时创建代理
3 修改了被代理类和代理类的共同接口的时候,只需要修改被代理类 修改了被代理类和代理类的共同接口的时候,除了需要修改被代理类,还需要修改代理类

注意:

  • 静态代理和jdk动态代理生成的代理对象都是被代理对象的兄弟
  • 上面就要求了被代理对象必须具有接口,且被代理和代理都需要实现相同接口的核心方法

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐