zoukankan      html  css  js  c++  java
  • 设计模式--->动态代理模式

    1.Spring动态代理的概念

    概念:通过代理类为原始类(目标类)增加额外功能

    好处:利于原始类(目标类)的维护
    从这点看和静态代理一样一样的

    2.Spring动态代理相关依赖的引入

    <!--Spring aop支持-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>5.1.14.RELEASE</version>
            </dependency>
    
            <!--aspectj框架包-->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.8.8</version>
            </dependency>
    
            <!--编制切面包-->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.3</version>
            </dependency>
    View Code

    3.Spring动态代理实战

    1. 目标类
      package proxy.service.impl;
      
      import proxy.service.UserService;
      
      public class UserServiceImpl implements UserService {
      
          @Override
          public void login(String username, String password) {
              System.out.println("UserServiceImpl.login 我是【service核心】");
          }
      }

      2. 方法前置增强代码,需要实现MethodBeforeAdvice接口

    package proxy.service.aop;import org.springframework.aop.BeforeAdvice;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;/**
     * @Classname MyAdvice
     * @Description 实现spring aop 包下MethodBeforeAdvice接口添加前置通知 这样只要在切面上的方法在执行前
     * 均要增强: MyBefore.before 【service外围】
     */

    publicclass MyBefore implements MethodBeforeAdvice{/** * * @param method 目标方法 login() * @param args login()的参数usernamepassword * @param target 目标对象userServiceImpl * @throws Throwable */@Override

    public void before(Method method, Object[] args, Object target)throws Throwable { System.out.println("MyBefore.before 【service外围】");
    }
    }

      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:p="http://www.springframework.org/schema/p"
           xmlns:context="http://www.springframework.org/schema/context"
           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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--目标类 此时和代理类无关-->
        <bean id = "userService" class="proxy.service.impl.UserServiceImpl"/>
        <bean id = "orderService" class="proxy.service.impl.OrderServiceImpl"/>
    
        <!--通知类-->
        <bean id= "myBefore" class="proxy.service.aop.MyBefore"/>
    
        <!--aop配置标签,会自动添加工作空间-->
        <aop:config>
            <!--定义接入点,即那些方法需要被加强-->
            <aop:pointcut id="pointcut" expression="execution(* *(..))"/>
            <!--切入点和通知的结合-->
            <aop:advisor advice-ref="myBefore" pointcut-ref="pointcut"/>
        </aop:config>
    
    </beans>

    4. 测试

     /**
         * spring动态代理
         */
        @Test
        public void test3() {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext6.xml");
            OrderService userService = (OrderService)ctx.getBean("orderService");
            userService.order();
        }

    5.debug查看获得的确实是代理类

    1.Spring工厂通过原始对象的id值获取的是代理对象

    2.获取代理对象后,可以通过声明接口类型,进行对象的存储。

    在这里插入图片描述

     

     

     

     

     

     

     

     

     

     

     

     

    4.Spring动态代理类在哪里?

    动态代理和之前的静态代理不同,他不需要java文件然后通过类加载子系统,加载进运行时数据区,这里是直接使用字节码相关技术,在JVM内存中直接生成当前类的代理类。也就没有那么多的java类让我们去管理,也就解决了这个痛点。另外他实用配置的方式对所有需要增强的类进行切入点的统一配置,这样就没有了代码冗余。

    5.Spring MethodBeforeAdvice 小总结及不使用他的原因

    那么肯定会有人提出问题,这个只能对方法的前置进行增强太鸡肋了。有没有更好的办法,可以在之前和之后均能增强呢?

    您能想到的问题spring都想到了。接着往下看。使用MethodInterceptor这个就可以实现。 特别注意是这个报下的:import org.aopalliance.intercept.MethodInterceptor;spring使用了aop联盟的相关接口来处理这个问题,并不是原生的spring的解决方案。

    6.MethodInterceptor使用

    1. MyArround.java
      package proxy.service.aop;
      
      import org.aopalliance.intercept.MethodInterceptor;
      import org.aopalliance.intercept.MethodInvocation;
      
      
      public class MyArround implements MethodInterceptor {
          /**
           *
           * @param invocation 和MethodBeforeAdvice.before()方法中的Method参数一样,只是更为强大的封装
           * @return Object 原始方法的返回值
           * @throws Throwable
           */
          @Override
          public Object invoke(MethodInvocation invocation) throws Throwable {
              System.out.println("MyArround.invoke 【service外围】前面的");
              Object res = null;
              try {//统一对异常进行抛出
                  res = invocation.proceed();
              } catch (Throwable throwable) {
                  throwable.printStackTrace();
              }
              System.out.println("MyArround.invoke 【service外围】后面的");
              return res;
              //return false; //影响原始方法的返回值。
          }
      }
    2. 配置文件
    <?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:p="http://www.springframework.org/schema/p"
           xmlns:context="http://www.springframework.org/schema/context"
           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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--目标类 此时和代理类无关-->
        <bean id = "userService" class="proxy.service.impl.UserServiceImpl"/>
        <bean id = "orderService" class="proxy.service.impl.OrderServiceImpl"/>
    
        <!--通知类-->
        <bean id= "myBefore" class="proxy.service.aop.MyBefore"/>
    
        <!--aop配置标签,会自动添加工作空间-->
        <aop:config>
            <!--定义接入点,即那些方法需要被加强-->
            <aop:pointcut id="pointcut" expression="execution(* *(..))"/>
            <!--切入点和通知的结合-->
            <aop:advisor advice-ref="myBefore" pointcut-ref="pointcut"/>
        </aop:config>
    
    </beans>

      <aop:pointcut id=“pointcut” expression=“execution(* *(…))”/>表示对所以方法进行增强。

    7.切入点表达式

    1. 方法切入点表达式
      execution(* *(…)) :所以方法进行增强
      * login(…) :login方法进行增强
      * login(String,String):login 方法且两个参数为String的方法增强
      * register(proxy.service.User) register方法且参数为User增强

    2. 类切入点
      * proxy.service.impl.UserServiceImpl.*(…) 类中的所有方法加入了增强功能
      .UserServiceImpl.(…) 类只在一级包,对类所有方法增强
      …UserServiceImpl.(…) 类存在多级包,UserServiceImpl类下的所有方法增强

    3. 包切入点表达式
      * proxy.service.impl..(…) 切入点包中的所有类,必须在impl中,不能在impl包的子包中
      * proxy.service.impl….(…)

    8.切入点函数

    1. execution
      可以满足你的所有想象,可以做所有的事情:方法切入、类切入、包切入

    2. args
      execution(* *(String,String)) 等价于:args(String,String)

    3. within 和args互补
      主要用于进行类、包切入点表达式的匹配
      execution(* …UserServiceImpl.(…))等价于within(…UserServiceImpl)
      execution(
       com.baizhiedu.proxy….(…))等价于ithin(com.baizhiedu.proxy…*)

    4. @annotation

      package proxy;
      
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
      
      /**
       * @Classname Log
       * @Description 用于切面
       */
      @Target(ElementType.METHOD) //使用在方法上
      @Retention(RetentionPolicy.RUNTIME) //使用在运行时环境中
      public @interface Log {
      }

           使用时:<aop:pointcut id="" expression="@annotation(proxy.Log)"/>

    1. 切入点函数的逻辑运算
    • and与操作
      指的是 整合多个切入点函数一起配合工作,进而完成更为复杂的需求
      login 同时 参数 2个字符串
      execution(* login(String,String))等价于: execution(* login(…)) and args(String,String)
      不能使用execution and execution 的语法形式。
    • or或操作
      register方法 和 login方法作为切入点:
      execution(* login(…)) or execution(* register(…))

    9.总结

    面向切面编程的步骤:

    1. 目标类:UserServiceImpl
    2. 增强: MyArround implements MethodInterceptor
    3. 切入点配置 <aop:pointcut id=“pointcut” expression=“execution(* login(…))”/>
    4. 切入点和增强合并为切面进行增强。 <aop:advisor advice-ref=“myBefore” pointcut-ref=“pointcut”/>
    <?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:p="http://www.springframework.org/schema/p"
           xmlns:context="http://www.springframework.org/schema/context"
           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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--目标类 此时和代理类无关-->
        <bean id = "userService" class="proxy.service.impl.UserServiceImpl"/>
        <bean id = "orderService" class="proxy.service.impl.OrderServiceImpl"/>
    
        <!--通知类-->
      <!--  <bean id= "myBefore" class="proxy.service.aop.MyBefore"/>-->
    
        <bean id="myArround" class="proxy.service.aop.MyArround"/>
        <!--aop配置标签,会自动添加工作空间-->
        <aop:config>
            <!--定义接入点,即那些方法需要被加强-->
            <aop:pointcut id="pointcut" expression="execution(* *(..))"/>
            <!--切入点和通知的结合-->
            <aop:advisor advice-ref="myArround" pointcut-ref="pointcut"/>
        </aop:config>
    
    </beans>

    测试:

    @Test
        public void test4() {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext6.xml");
            UserService userService = (UserService)ctx.getBean("userService");
            userService.login("zhangsan","111111");
            userService.regester(new User(2, "222222"));
        }

    测试结果

           MyArround.invoke 【service外围】前面的
        UserServiceImpl.login 我是【service核心】
        MyArround.invoke 【service外围】后面的
        MyArround.invoke 【service外围】前面的
        UserServiceImpl.regester 我是【service核心】
        MyArround.invoke 【service外围】后面的

    动态字节码技术原理:

    动态代理细节分析:

    1.spring创建的动态代理类在哪里呢?

    spring框架在运行的时,通过动态字节码技术,在jvm中创建的,运行在jvm内部, 等程序结束后会和jvm类一起消失。

    2.什么叫动态字节码技术?

    Java运行一个类, 其实就是运行这个类的编译后的字节码--->object

    java在类加载的时候会把字节码加载到jvm的内存中。

    3.那么问题来了,动态字节码的字节码从哪里来的呢?

    (动态字节码其实就是不需要(.Java文件生成.class文件的过程)字节码的一个过程),它是由一些第三方动态字节码框架(如ASM,Javassist,cglib)直接在jvm中生成字节码(动态字节码),当jvm结束,动态字节码也跟着消失了。

    结论:动态代理不需要定义类文件,都是在jvm中自动创建的,所以不会有静态代理,类文件数量过多,影响项目管理的问题。

  • 相关阅读:
    No module named 'pydispatch'
    python 安装 vrml
    python3.7 安装pyopengl,环境搭建
    机智人 激光雷达 配置
    ubuntu server 16.04(amd 64) 配置网桥,多网卡使用激活
    ubuntu server 多网卡
    ubuntu16.04中开启和关闭防火墙
    c++ 判断给定区间是否是一个heap. O(N) (is_heap)
    c++ 判断容器A是否是容器B的子集,如果是,返回true(includes)
    c++ 容器元素填充指定数量的元素(generate_n)
  • 原文地址:https://www.cnblogs.com/cb1186512739/p/14197812.html
Copyright © 2011-2022 走看看