zoukankan      html  css  js  c++  java
  • Spring AOP组件

    1、Pointcut

       这个类位于 org.springframework.aop 包中,它的作用就是定义切面的匹配点。(简单的说就是我去切哪些类、哪些方法…) 在 Spring Aop 中匹配的点主要是 class 与 method 这两个方面, 分别为ClassFilter 与 MethodFilter

    // 由 ClassFilter 与 MethodMatcher 组成的 pointcut
    public interface Pointcut {
        // 类过滤器, 可以知道哪些类需要拦截
        ClassFilter getClassFilter();
        // 方法匹配器, 可以知道哪些方法需要拦截
        MethodMatcher getMethodMatcher();
        // 匹配所有对象的 Pointcut
        Pointcut TRUE = TruePointcut.INSTANCE;
    }
    ClassFilter和MethodMatcher分别用于匹配将执行织入操作的对象以及相应的方法。 
    在 Spring 中主要有以下几个类,介绍如下:
    1、NameMatchMethodPointcut:通过刚发名进行精确匹配的。 (PS: 其中 ClassFilter = ClassFilter.TRUE)
    2、ControlFlowPointcut:根据在当前线程的堆栈信息中的方法名来决定是否切入某个方法(效率较低)
    3、ComposablePointcut:组合模式的 Pointcut, 主要分成两种: 1.组合中所有都匹配算成功 2. 组合中都不匹配才算成功
    4、JdkRegexpMethodPointcut:通过 正则表达式来匹配方法(PS: ClassFilter.TRUE)
    5、AspectJExpressionPointcut:通过 AspectJ 包中的组件进行方法的匹配(切点表达式)
    6、TransactionAttributeSourcePointcut:通过 
    7、TransactionAttributeSource 在 类的方法上提取事务注解的属性 @Transactional 来判断是否匹配, 提取到则说明匹配, 提取不到则说明匹配不成功
    8、AnnotationJCacheOperationSource:支持JSR107的cache相关注解的支持

    2、Advice

      Advice: 建议忠告, 劝告, 通知。它其实最开始是 aopalliance 包中的一个空接口, 接口的存在主要是为了标示对应类为 Advice;

     在Spring Aop 中 Advice 其实表示的是在 Pointcut 点上应该执行的方法。而这些方法可以在目标方法之前、之后、包裹、抛出异常等等任何地方执行。

    public interface Advice {
    
    }

    Advice: 其主要分成两类:普通advice 与Interceptor/MethodInterceptor

    3、Advisor

      Advisor 其实它就是 Pointcut 与 Advice 的组合, Advice 是执行的方法, 而要知道方法何时执行, 则 Advice 必需与 Pointcut 组合在一起, 这就诞生了 Advisor 这个类

    1、PointcutAdvisor: 我们在 Spring 中常用的 Advisor, 包含一个 Pointcut 与一个 advice;
    2、DefaultPointcutAdvisor: 最常用的 Advisor, 在使用编程式aop时, 很多时候会将 Advice / MethodInterceptor 转换成 DefaultPointcutAdvisor;
    3、NameMatchMethodPointcutAdvisor: 这个是在使用 NameMatchPointcutAdvisor时创建的 Advisor, 主要是通过 方法名来匹配是否执行 Advice;
    4、DefaultBeanFactoryPointcutAdisor:自身绑定BeanFactory,需要绑定Spring的IOC容器,可以通过容器中的Advice注册的beanName来关联对应的Advice。
            //声明一个aspectj切点,一张切面
            JdkRegexpMethodPointcut cut = new JdkRegexpMethodPointcut();
            //cut.setPattern("com.fsx.maintest.Person.run"); //它会拦截Person类下所有run的方法(无法精确到方法签名)
            //cut.setPattern(".*run.*");//.号匹配除"
    "之外的任何单个字符。*号代表零次或多次匹配前面的字符或子表达式  所以它拦截任意包下任意类的run方法
            cut.setPatterns(new String[]{".*run.*", ".*say.*"}); //可以配置多个正则表达  式...  sayHi方法也会被拦截
            // 声明一个通知(此处使用环绕通知 MethodInterceptor )
            Advice advice = (MethodInterceptor) invocation -> {
                System.out.println("============>放行前拦截...");
                Object obj = invocation.proceed();
                System.out.println("============>放行后拦截...");
                return obj;
            };
            //切面=切点+通知
            // 它还有个构造函数:DefaultPointcutAdvisor(Advice advice); 用的切面就是Pointcut.TRUE,所以如果你要指定切面,请使用自己指定的构造函数
            // Pointcut.TRUE:表示啥都返回true,也就是说这个切面作用于所有的方法上/所有的方法
            // addAdvice();方法最终内部都是被包装成一个 `DefaultPointcutAdvisor`,且使用的是Pointcut.TRUE切面,因此需要注意这些区别  相当于new DefaultPointcutAdvisor(Pointcut.TRUE,advice);
            Advisor advisor = new DefaultPointcutAdvisor(cut, advice);

    4、Advised

       承载一个代理对象需要的必要信息:如相关目标类、Advice、Advisor

    // 这个 Advised 接口的实现着主要是代理生成的对象与AdvisedSupport (Advised的支持器)
    public interface Advised extends TargetClassAware {
         // 这个 frozen 决定是否 AdvisedSupport 里面配置的信息是否改变,设置为true,那么一旦代理对象生成的各项代理信息配置完成,不容许进行更改
        boolean isFrozen();
         //如果该值为true,那么就是用CGLIB对对象进行代理,默认值为false
        boolean isProxyTargetClass();
         // 返回代理的接口
        Class<?>[] getProxiedInterfaces();
        // 判断这个接口是否是被代理的接口
        boolean isInterfaceProxied(Class<?> intf);
        // 设置代理的目标对象
        void setTargetSource(TargetSource targetSource);
        // 获取代理的对象
        TargetSource getTargetSource();
        // 判断是否需要将代理的对象暴露到 ThreadLocal中,如果目标对象希望获取代理对象,则可以通过AopContext.currentProxy()取得默认值false
        void setExposeProxy(boolean exposeProxy);
        // 返回是否应该暴露代理对象
        boolean isExposeProxy();
         // 设置 Advisor 是否已经在前面过滤过是否匹配 Pointcut (极少用到)
        void setPreFiltered(boolean preFiltered);
        // 获取 Advisor 是否已经在前面过滤过是否匹配 Pointcut (极少用到)
        boolean isPreFiltered();
        // 获取所有的 Advisor
        Advisor[] getAdvisors();
        // 增加 Advisor 到链表的最后
        void addAdvisor(Advisor advisor) throws AopConfigException;
        // 在指定位置增加 Advisor
        void addAdvisor(int pos, Advisor advisor) throws AopConfigException;
        // 删除指定的 Advisor
        boolean removeAdvisor(Advisor advisor);
        // 删除指定位置的 Advisor
        void removeAdvisor(int index) throws AopConfigException;
        // 返回 Advisor 所在位置的index
        int indexOf(Advisor advisor);
        // 将指定的两个 Advisor 进行替换
        boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;
         // 增加 Advice <- 这个Advice将会包裹成 DefaultPointcutAdvisor
        void addAdvice(Advice advice) throws AopConfigException;
        // 在指定 index 增加 Advice <- 这个Advice将会包裹成 DefaultPointcutAdvisor
        void addAdvice(int pos, Advice advice) throws AopConfigException;
        // 删除给定的 Advice
        boolean removeAdvice(Advice advice);
        // 获取 Advice 的索引位置
        int indexOf(Advice advice);
        // 将 ProxyConfig 通过 String 形式返回
        String toProxyConfigString();
    }

    5、TargetSource

     TargetSource(目标源)是被代理的target(目标对象)实例的来源。

    public interface TargetSource extends TargetClassAware {
       //目标对象类型
       Class<?> getTargetClass();
       // 这个方法用户返回当前bean是否为静态的,比如常见的单例bean就是静态的,而prototype就是动态的,
       // 这里这个方法的主要作用是,对于静态的bean,spring是会对其进行缓存的,在多次使用TargetSource
       // 获取目标bean对象的时候,其获取的总是同一个对象,通过这种方式提高效率
       boolean isStatic();
       //获取目标对象
       Object getTarget() throws Exception;
       // Spring在完目标bean之后会调用这个方法释放目标bean对象,对于一些需要池化的对象,这个方法是必须
       // 要实现的,这个方法默认不进行任何处理
       void releaseTarget(Object target) throws Exception;
    }

     TargetSource最主要的特性:每次方法调用都会触发TargetSource的getTarget()的方法,该方法会从TargetSource中获取类中具体的目标对象;

      在通常情况下,无论是使用setTarget(),还是通过setTargetName()方法等设置的目标对象,在框架内部都会通过一个TargetSource实现类对这个设置的目标对象进行封装,框架内部会以统一的方式处理调用链终点的目标对象。

    public void setTarget(Object target) {
            setTargetSource(new SingletonTargetSource(target));
        }

      TargetSource的实现类:

      (1)、SingletonTargetSource

        在通过ProxyFactoryBean的setTarget()方法设置目标对象就是使用SingletonTargetSource,ProxyFactoryBean内部会自行使用一个SingletonTargetSource对设置的目标对象进行封装。

    public class SingletonTargetSource implements TargetSource, Serializable {
        /** use serialVersionUID from Spring 1.2 for interoperability */
        private static final long serialVersionUID = 9031246629662423738L;
        /** Target cached and invoked using reflection */
        private final Object target;
        /**
         * Create a new SingletonTargetSource for the given target.
         * @param target the target object
         */
        public SingletonTargetSource(Object target) {
            Assert.notNull(target, "Target object must not be null");
            this.target = target;
        }
        @Override
        public Class<?> getTargetClass() {
            return this.target.getClass();
        }
    
        @Override
        public Object getTarget() {
            return this.target;
        }
    
        @Override
        public void releaseTarget(Object target) {
            // nothing to do
        }
    
        @Override
        public boolean isStatic() {
            return true;
        }
        /**
         * Two invoker interceptors are equal if they have the same target or if the
         * targets or the targets are equal.
         */
        @Override
        public boolean equals(Object other) {
            if (this == other) {
                return true;
            }
            if (!(other instanceof SingletonTargetSource)) {
                return false;
            }
            SingletonTargetSource otherTargetSource = (SingletonTargetSource) other;
            return this.target.equals(otherTargetSource.target);
        }
        /**
         * SingletonTargetSource uses the hash code of the target object.
         */
        @Override
        public int hashCode() {
            return this.target.hashCode();
        }
        @Override
        public String toString() {
            return "SingletonTargetSource for target object [" + ObjectUtils.identityToString(this.target) + "]";
        }
    
    }

    (2)、PrototypeTargetSource 

       每次getTarget()将生成prototype类型的bean,即其生成的bean并不是单例的,因而使用这个类型的TargetSource时需要注意,封装的目标bean必须是prototype类型的。PrototypeTargetSource继承了AbstractBeanFactoryBasedTargetSource拥有了创建bean的能力。

    public class PrototypeTargetSource extends AbstractPrototypeBasedTargetSource {
       @Override
       public Object getTarget() throws BeansException {
          return newPrototypeInstance();
       }
       @Override
       public void releaseTarget(Object target) {
          destroyPrototypeInstance(target);
       }
       @Override
       public String toString() {
          return "PrototypeTargetSource for target bean with name '" + getTargetBeanName() + "'";
       }
    }
    public abstract class AbstractPrototypeBasedTargetSource extends AbstractBeanFactoryBasedTargetSource {
       @Override
       public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
          super.setBeanFactory(beanFactory);
          // Check whether the target bean is defined as prototype.
          if (!beanFactory.isPrototype(getTargetBeanName())) {
             throw new BeanDefinitionStoreException(
                   "Cannot use prototype-based TargetSource against non-prototype bean with name '" +
                   getTargetBeanName() + "': instances would not be independent");
          }
       }
       /**
        * Subclasses should call this method to create a new prototype instance.
        * @throws BeansException if bean creation failed
        */
       protected Object newPrototypeInstance() throws BeansException {
          if (logger.isDebugEnabled()) {
             logger.debug("Creating new instance of bean '" + getTargetBeanName() + "'");
          }
          //使用容器创建一个bean,如果getTargetBeanName()是prototype的,则target目标对象也是prototype的
          return getBeanFactory().getBean(getTargetBeanName());
       }
       /**
        * Subclasses should call this method to destroy an obsolete prototype instance.
        * @param target the bean instance to destroy
        */
       protected void destroyPrototypeInstance(Object target) {
          if (logger.isDebugEnabled()) {
             logger.debug("Destroying instance of bean '" + getTargetBeanName() + "'");
          }
          if (getBeanFactory() instanceof ConfigurableBeanFactory) {
             ((ConfigurableBeanFactory) getBeanFactory()).destroyBean(getTargetBeanName(), target);
          }
          else if (target instanceof DisposableBean) {
             try {
                ((DisposableBean) target).destroy();
             }
             catch (Throwable ex) {
                logger.warn("Destroy method on bean with name '" + getTargetBeanName() + "' threw an exception", ex);
             }
          }
       }
       //---------------------------------------------------------------------
       // Serialization support
       //---------------------------------------------------------------------
       private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
          throw new NotSerializableException("A prototype-based TargetSource itself is not deserializable - " +
                "just a disconnected SingletonTargetSource or EmptyTargetSource is");
       }
     
       /**
        * Replaces this object with a SingletonTargetSource on serialization.
        * Protected as otherwise it won't be invoked for subclasses.
        * (The {@code writeReplace()} method must be visible to the class
        * being serialized.)
        * <p>With this implementation of this method, there is no need to mark
        * non-serializable fields in this class or subclasses as transient.
        */
       protected Object writeReplace() throws ObjectStreamException {
          if (logger.isDebugEnabled()) {
             logger.debug("Disconnecting TargetSource [" + this + "]");
          }
          try {
             // Create disconnected SingletonTargetSource/EmptyTargetSource.
             Object target = getTarget();
             return (target != null ? new SingletonTargetSource(target) :
                   EmptyTargetSource.forClass(getTargetClass()));
          }
          catch (Exception ex) {
             String msg = "Cannot get target for disconnecting TargetSource [" + this + "]";
             logger.error(msg, ex);
             throw new NotSerializableException(msg + ": " + ex);
          }
       }
    }

     

  • 相关阅读:
    oracle判断是否包含字符串的方法
    linux环境下卸载mysql
    分辨率与px的关系
    plsql登录,tables表为空解决方案
    excle导出、导入、下载_jeesite注解@ExcelField
    安卓中常用的弹出框
    c#将数据导出到excel中
    将表里的数据分组,并取每组中的最大的一条数据
    c#如何写服务,打包和卸载服务
    C#排列组合类,写彩票算法的朋友们可以来看一看
  • 原文地址:https://www.cnblogs.com/mayang2465/p/12132170.html
Copyright © 2011-2022 走看看