• spring——AOP(静态代理、动态代理、AOP)


    一、代理模式

    代理模式的分类:

    • 静态代理
    • 动态代理

    从租房子开始讲起:中介与房东有同一的目标在于租房

    image-20200728165434019

    1.静态代理

    静态代理角色分析:

    • 抽象角色:一般使用接口或者抽象类来实现(这里为租房接口)

      public interface Rent {
          void rent();
      }
      
    • 真实角色:被打理的角色(这里为房东)

      public class HouseMaster implements Rent{
          public void rent() {
              System.out.println("房东租房啦!");
          }
      
          public static void main(String[] args) {
              HouseMaster houseMaster = new HouseMaster();
              Proxy proxy = new Proxy(houseMaster);
              proxy.rent();
          }
      }
      
    • 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作

      public class Proxy {
          HouseMaster houseMaster;
      
          public Proxy() {
          }
      
          public Proxy(HouseMaster houseMaster) {
              this.houseMaster = houseMaster;
          }
      
          public void rent(){
              lookHouse();
              houseMaster.rent();
              sign();
          }
      
          public void lookHouse(){
              System.out.println("看房子");
          }
      
          public void sign(){
              System.out.println("签合同");
          }
      }
      
    • 客户:使用代理角色来进行一些操作

      public static void main(String[] args) {
          HouseMaster houseMaster = new HouseMaster();
          Proxy proxy = new Proxy(houseMaster);
          proxy.rent();
      }
      

      优点:

      (1).真实对象更加纯粹,不在关注一些琐碎的公共事务

      (2).公共业务由代理对象完成,有利于公共业务的扩展和管理

      缺点:多了代理对象也增加了代码量(所以后面出现了动态代理模式)

    1. 动态代理

      角色分析:与静态代理相同

      特点:动态代理的动态类是动态生成的,不是我们直接写好的

      分类:

      (1).基于接口的动态代理——JDK动态代理(重点理解)

      (2).基于类的动态代理——cglib

      (3).javasist

      核心api:

      (1).Proxy:

      Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

      • newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h):返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。

        参数:

        (1).loader类加载器

        (2).代理对象的接口

        (3).InvocationHandler的实体,因为一般是调用类继承了InvocationHandler,所以许多时候就是this

      (2).InvocatioinHandler:

      InvocationHandler是由代理实例的调用处理程序实现的接口

      每个代理实例都有一个关联的调用处理程序(也就是一个代理类)。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

      • invoke

        参数:

        (1).proxy:调用该方法的代理实例

        (2).method:所述方法对应于调用代理实例上的接口方法的实例

        (3).args:参数,如果没有则为null

      使用:

      1. 构建动态代理对象(这个代理对象可以用于同一个接口的多个实现类)

        public class ProxyInvocationHandler implements InvocationHandler {
        
            //代理对象
            Object target;
        
            //设定动态代理的对象
            public void setTarget(Object target){
                this.target = target;
            }
        
            public Object getProxy(){
                return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
            }
        
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object res = method.invoke(target, args);
                return res;
            }
        }
        
      2. 测试

        public class service {
        
            public static void main(String[] args) throws Throwable {
                ProxyInvocationHandler handler = new ProxyInvocationHandler();
                handler.setTarget(new HostImpl());
                Host proxy = (Host) handler.getProxy();
                proxy.rent();
            }
        }
        
      3. 解释:从构建动态代理对象的过程查看

        (1).设定动态代理对象:setTarget()

        (2).获得动态代理getProxy()

        (3).使用动态代理,表面上看是调用了proxy.rent,本质上还是调用handler实现的invoke函数,注意该函数的第一个参数proxy用处不大

      优点:在静态代理的基础上,静态代理是代理的是一个类;而动态代理是代理一个接口(多个类)

    二、AOP

    AOP:面向切面编程,通过预编译方式和运行期动态代理实现 程序功能的统一维护的一种技术

    导入织入包

    两种方式:

    1. 使用api接口

      (1).MethodBeforeAdvice:方法的前置增强

      (2).AfterReturningAdvice:方法的后置增强

      环境搭建:

      //接口
      public interface service {
          void say();
      }
      
      //实例
      public class serviceImpl implements service {
          public void say() {
              System.out.println("say some thins");
          }
      }
      
      //前置增强
      public class beforeSay implements MethodBeforeAdvice {
          public void before(Method method, Object[] objects, Object o) throws Throwable {
              System.out.println("Good Morning!");
          }
      }
      
      //后置增强
      public class afterSay implements AfterReturningAdvice {
          public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
              System.out.println("后置增强");
          }
      }
      

      配置文件:

      <?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:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
              https://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/aop
               https://www.springframework.org/schema/aop/spring-aop.xsd">
      
          <bean id="serviceImpl" class="com.guan.dao.serviceImpl"/>
          <bean id="beforeSay" class="com.guan.dao.beforeSay"/>
          <bean id="afterSay" class="com.guan.dao.afterSay"/>
      
          <aop:config>
      <!--        choose pointCut-->
      <!--        public method.(args)-->
              <aop:pointcut id="point" expression="execution(* com.guan.dao.service.*(..))"/>
      
              <aop:advisor advice-ref="beforeSay" pointcut-ref="point"/>
              <aop:advisor advice-ref="afterSay" pointcut-ref="point"/>
          </aop:config>
      
      </beans>
      

      注:

      (1).<aop:point>设定切入点

      (2).<aop:advisor>设定环绕对象(主要设定前置函数,后置函数及其目标的切入点)

      (3).execution(* com.guan.dao.service.*(..))execution(* com.guan.dao.*.*(..)):所有修饰符(public,private) com.guan.dao包下的所有类下的所有方法的所有参数

      测试:

      public static void main(String[] args) {
          ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
          service bean = context.getBean("serviceImpl", service.class);
          bean.say();
      }
      

      注:这里返回的必须用接口类型(service),而不能用serviceImpl

    2. 使用切面
      环境搭建:

      package com.guan.dao;
      
      public class Advice {
      
          public void before(){
              System.out.println("before");
          }
      
          public void after(){
              System.out.println("after");
          }
      
      }
      

      配置文件:

      <?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:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
              https://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/aop
               https://www.springframework.org/schema/aop/spring-aop.xsd">
      
          <bean id="advice" class="com.guan.dao.Advice"></bean>
          <bean id="serviceImpl" class="com.guan.dao.serviceImpl"></bean>
      
          <aop:config>
              <aop:aspect ref="advice">
                  <aop:pointcut id="point" expression="execution(* com.guan.dao.serviceImpl.*(..))"/>
      <!--            切面-->
                  <aop:before method="before" pointcut-ref="point"></aop:before>
                  <aop:after method="after" pointcut-ref="point"></aop:after>
              </aop:aspect>
          </aop:config>
      
      </beans>
      

      测试类:

      public static void main(String[] args) {
          ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
          service bean = context.getBean("serviceImpl", service.class);
          bean.say();
      }
      //同上
      

      注:

      (1).注意<aop:aspect>的ref是相关的构成切面的类

      (2).在这个类的内部还是需要切入点的

    3. 使用api接口和使用切面比较

      (1).使用api接口需要记录相关的文档,但使用切面则可以在xml文件中直接配置

      (2).使用api接口需要多个类继承相关的接口,而使用切面只需要一个类承载相关的方法即可

      (3).使用api接口由于有对切入点的结果关联,所以和上下文的关联更密切;使用切面则切面的函数与切入点的上下文关系是割裂的

    4. 使用注解使用AOP

      相关注解:

      (1).@Aspect:标注这个类是一个切面

      (2).@Before("切入点")

      (3).@After("切入点后")

      (4).@Around:功能复杂,需要相关的方法使用ProceedingJoinPoint类型的参数,且这个参数可以直接调用方法

      (5).@EnableAspectJAutoProxy:用于注解开启注解(不用xml配置<aop:aspectj-autoproxy/>)

      注意:切入点都要用execution表达式

      环境搭建:

      @Aspect
      public class AnnotationCutPoint {
          @Before("execution(* com.guan.dao.*.*(..))")
          public void before(){
              System.out.println("before");
          }
      
          @After("execution(* com.guan.dao.*.*(..))")
          public void after(){
              System.out.println("after");
          }
      
          @Around("execution(* com.guan.dao.*.*(..))")
          public void around(ProceedingJoinPoint joinPoint) throws Throwable {
              System.out.println("around1");
              joinPoint.proceed();
              System.out.println("around2");
          }
      }
      

      注意:先要有切面@Aspect,才有切点

      <?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:aop="http://www.springframework.org/schema/aop"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
              https://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/aop
               https://www.springframework.org/schema/aop/spring-aop.xsd">
      
      
          <bean id="serviceImpl" class="com.guan.dao.serviceImpl"></bean>
          <bean id="annotation" class="com.guan.dao.AnnotationCutPoint"></bean>
      
          <aop:aspectj-autoproxy/>
      
      </beans>
      

      测试:

      public class serviceImpl implements service {
          public void say() {
              System.out.println("say some thins");
          }
      
          public static void main(String[] args) {
              ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
              service bean = context.getBean("serviceImpl", service.class);
              bean.say();
          }
      }
      

      注:spring的AOP默认是通过jdk生成,但也可以进行修改

      <!--    jdk,默认-->
          <aop:aspectj-autoproxy proxy-target-class="false"/>
      <!--    cglib-->
          <aop:aspectj-autoproxy proxy-target-class="true"/>
      
  • 相关阅读:
    算术运算
    数据分析
    科学计算
    面向对象
    文件操作-py
    pillow图像处理
    模块
    固定翼飞行过程产生的阻力
    修改行间距等基本操作
    文件操作
  • 原文地址:https://www.cnblogs.com/Arno-vc/p/13410319.html
Copyright © 2020-2023  润新知