• 代理模式


    代理模式

    1.基础知识

    定义∶

    为其他对象提供一种代理,以控制对这个对象的访问

    代理对象在客户端和目标对象之间起到中介的作用


    适用场景

    保护目标对象

    增强目标对象


    优点

    代理模式能将代理对象与真实被调用的目标对象分离

    一定程度上降低了系统的耦合度,扩展性好

    保护目标对象

    增强目标对象


    缺点

    代理模式会造成系统设计中类的数目增加

    在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢

    增加系统的复杂度


    扩展

    静态代理

    动态代理

    CGLib代理


    Spring代理选择

    当Bean有实现接口时,Spring就会用JDK的动态代理

    当Bean没有实现接口时,Spring使用CGlib

    可以强制使用Cglib

    在spring配置中加入<aop∶aspectj-autoproxy proxy-target-class="true"/>)


    2.实战

    在常用的保存用户信息的接口上,统计接口耗时,分别用静态代理和动态代理实现

    静态代理方式:

    /**
     * 用户类
     * @Author LYS
     * @Date 2022/1/16 15:21
     * @Version 1.0
     */
    @Data
    public class User {
        private String name;
        private String sex;
    }
    
    /**
     * @Author LYS
     * @Date 2022/1/16 15:21
     * @Version 1.0
     */
    public interface UserService {
        int saveUserInfo(User user) throws InterruptedException;
    }
    
    /**
     * @Author LYS
     * @Date 2022/1/16 15:22
     * @Version 1.0
     */
    public class UserServiceImpl implements UserService{
        @Override
        public int saveUserInfo(User user) throws InterruptedException {
            System.out.println("保存成功!");
            Thread.sleep(100);
            return 1;
        }
    }
    
    /**
     * 静态代理
     *
     * @Author LYS
     * @Date 2022/1/16 15:23
     * @Version 1.0
     */
    public class UserServiceStaticProxy {
        private UserService userService;
        long start;
    
        public int saveUser(User user) throws InterruptedException {
            userService = new UserServiceImpl();
            beforeMethod();
            int result = userService.saveUserInfo(user);
            afterMethod();
            return result;
        }
    
        public void print() {
            System.out.println("打印数据:");
        }
    
        private void beforeMethod() {
            System.out.println("静态代理 before code");
            start = System.currentTimeMillis();
    
        }
    
        private void afterMethod() {
            System.out.println("静态代理 after code");
            long time = System.currentTimeMillis() - start;
            System.out.println("调用此接口花费了【" + time + "】毫秒");
        }
    }
    
    /**
     * @Author LYS
     * @Date 2022/1/16 15:28
     * @Version 1.0
     */
    public class StaticTest {
        public static void main(String[] args) throws InterruptedException {
            User user = new User();
            UserServiceStaticProxy userServiceStaticProxy = new UserServiceStaticProxy();
            userServiceStaticProxy.saveUser(user);
        }
    }
    

    控制台输出:

    image-20220116154031025


    动态代理方式:使用反射invoke调用真实方法

    /**
     * @Author LYS
     * @Date 2022/1/16 15:41
     * @Version 1.0
     */
    public class UserServiceDynamicProxy implements InvocationHandler {
        private Object target;
        long start;
    
        public UserServiceDynamicProxy(Object target) {
            this.target = target;
        }
    
        /**
         * 返回动态代理类
         *
         * @return
         */
        public Object bind() {
            Class cls = target.getClass();
            return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), this);
        }
    
    
        /**
         * 动态代理类实际执行的方法
         *
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object argObject = args[0];
            beforeMethod(argObject);
            Object object = method.invoke(target, args);
            afterMethod();
            return object;
        }
    
        private void beforeMethod(Object obj) {
            System.out.println("动态代理 before code");
            start = System.currentTimeMillis();
    
        }
    
        private void afterMethod() {
            System.out.println("动态代理 after code");
            long time = System.currentTimeMillis() - start;
            System.out.println("调用此接口花费了【" + time + "】毫秒");
        }
    }
    
    public class DynamicTest {
        public static void main(String[] args) throws InterruptedException {
            User user = new User();
            user.setName("小李");
            UserService userServiceStaticProxy = (UserService) new UserServiceDynamicProxy(new UserServiceImpl()).bind();
            userServiceStaticProxy.saveUserInfo(user);
        }
    }
    

    控制台输出

    image-20220116154614098


    3.spring源码分析

    spring 动态代理实现AOP

    我们在使用 Spring AOP 时,需要先配置好 ProxyFactoryBean,然后通过 ac.getBean(bean id) 来获取 ProxyFactoryBean 代理的对象。而 ProxyFactoryBean 类使用 ProxyFactoryBean.getObject() 方法获取返回的对象,即代理对象。

    下面先看 ProxyFactoryBean 类中的核心方法 getObject(),源码如下:

    public Object getObject() throws BeansException {
       initializeAdvisorChain();
       if (isSingleton()) {
          return getSingletonInstance();
       }
       else {
          if (this.targetName == null) {
             logger.info("Using non-singleton proxies with singleton targets is often undesirable. " +
                   "Enable prototype proxies by setting the 'targetName' property.");
          }
          return newPrototypeInstance();
       }
    }
    

    在 getObject() 方法中,主要调用 getSingletonInstance() 和 newPrototypeInstance() 方法。

    在 Spring 的配置中,如果不做任何设置,则 Spring 代理生成的 Bean 都是单例对象。如果修改 scope,则每次都创建一个新的原型对象。newPrototypeInstance() 里的逻辑比较复杂,教程后面会详细讲解,这里简单了解即可。

    Spring 使用动态代理实现 AOP 时有两个非常重要的类,即 JdkDynamicAopProxy 类和 CglibAopProxy 类,其类图如下:

    img

    Spring 中的代理选择如下:

    • 当 Bean 有实现接口时,Spring 会用 JDK 动态代理方式
    • 当 Bean 没有实现接口时,Spring 会选择 CGLib 动态代理方式

    Spring 可以通过配置强制使用 CGLib 动态代理,只需在 Spring 的配置文件中加入如下代码即可。

    <aop:aspectj-autoproxy proxy-target-class="true"/>

  • 相关阅读:
    005.SQLServer AlwaysOn可用性组高可用简介
    004.Windows Server 故障转移群集 (WSFC)简介
    003.SQLServer数据库镜像高可用部署
    附008.Kubernetes TLS证书介绍及创建
    附007.Kubernetes ABAC授权
    附006.Kubernetes RBAC授权
    附005.Kubernetes身份认证
    附004.Kubernetes Dashboard简介及使用
    附003.Kubeadm部署Kubernetes
    附002.Minikube介绍及使用
  • 原文地址:https://www.cnblogs.com/yslu/p/15810346.html
Copyright © 2020-2023  润新知