• 后端——框架——切面框架——aop——静态切面


      本篇介绍静态切面,它主要分为两类,

    第一类是Inner type declaration,简称ITD。主要是修改类,有三种子类型,给类添加字段,方法成员;修改类的继承或实现。动态在类或其成员上添加注解。

    第二类是weave the declaration,主要是修改类编译阶段的行为,有三种子类型,添加警告;添加错误;忽略必检异常。

    本篇总共七个小节。

    第一小节介绍给单个类添加成员。

    第二小节介绍修改单个类的结构,动态添加接口。

    第三小节介绍结合第一小节和第二小节,批量的添加成员。

    第四小节介绍动态添加注解。

    第五小节介绍添加编译警告,错误。

    第六小节介绍忽略必检异常。

    第七小节总结,略。

    1、ITD

    引用原著中的定义:

    Inner-type declaration(ITD):one type (an aspect) makes declaration for another type (an interface, a class or even an aspect). It consists of support for member introduction, type hierarchy modification, and annotation supplementation.

    添加新字段,改变类的层次结构,给任何元素上的添加注解。(操作只提到了添加,没有删除,没有修改原始的元素)

    1.1   member introduction

    原著中通过演示Customer示例介绍添加成员。示例实现的目标是当监听属性的变化。核心类

    package ch5;
    
    import java.beans.PropertyChangeListener;
    import java.beans.PropertyChangeSupport;
    /**
     * 
     * @File Name: BeanMakerAspect.aj 
     * @Description: 不再修改原始Customer方法,而是在Aspect中添加PropertyChangeSupport属性,以及相关的方法
     * @version 1.0
     * @since JDK 1.8
     */
    public aspect BeanMakerAspect {
    	// 添加propertyChangeSupport属性, 格式为类名+点+属性名
    	private PropertyChangeSupport OrginalCustomer.propertyChangeSupport;
    	
    	// 添加addPropertyChangeListener方法
    	public void OrginalCustomer.addPropertyChangeListener(PropertyChangeListener listener){
    		// 不再有类名,属性可以单独使用
    		propertyChangeSupport.addPropertyChangeListener(listener);
    	}
    	
    	// 添加removePropertyChangeListener方法
    	public void OrginalCustomer.removePropertyChangeListener(PropertyChangeListener listener){
    		// 不再有类名,属性可以单独使用
    		propertyChangeSupport.removePropertyChangeListener(listener);
    	}
    	
    	/**
    	 *  
    	 * @Title: beanCreation
    	 * @Author: wrd
    	 * @Description:添加pointcut,
    	 * 				 initialization()为对象初始化的join point, 
    	 *               public 为修饰符
    	 *               OrginalCustomer+.new(..) 调用任何new OrginalCustomer或者是它的子类
    	 *               this(bean) 用于收集上下文,收集的是当前创建的实例
    	 * @param bean
    	 */
    	pointcut beanCreation(OrginalCustomer bean): initialization(public OrginalCustomer+.new(..)) && this(bean);
    	
    	/**
    	 * 
    	 * @Title: afterReturning
    	 * @Description:定义After returning类型的advice,利用beanCreation收集到的当前实例
    	 * @param bean
    	 */
    	after(OrginalCustomer bean) returning : beanCreation(bean){
    		// 实例名+点+属性名,设置当前实例的propertyChangeSupport属性
    		bean.propertyChangeSupport = new PropertyChangeSupport(bean);
    	}
    	
    	/**
    	 * 
    	 * @Title: beanPropertyChange
    	 * @Author: wrd
    	 * @Description:添加pointcut
    	 * 				 execution()为对象方法执行的join point
    	 *               void OrginalCustomer+.set*(..) 定义任何OrginalCustomer或子类的setXXX方法,返回值为void
    	 *               args:用于收集setXXX方法传入的参数
    	 *               this:用于收集当前创建的实例
    	 * @param bean
    	 * @param newValue
    	 */
    	pointcut beanPropertyChange(OrginalCustomer bean, Object newValue): execution(void OrginalCustomer+.set*(..)) && args(newValue) && this(bean);
    	
    	/**
    	 * 
    	 * @Title: around
    	 * @Description:定义around类型的advice,使用beanPropertyChange收集的bean,newValue
    	 * @param bean
    	 * @param newValue
    	 */
    	void around(OrginalCustomer bean,Object newValue) : beanPropertyChange(bean,newValue){
    		// 获取当代Set方法的名称
    		String methodName =thisJoinPointStaticPart.getSignature().getName();
    		// 获取字段的名称,因为address属性没有第二个单词,所以直接调用toLowerCase,将Address改为address
    		// 当属性中存在驼峰时,需要单独写个方法将第一个字母小写。
    		String propertyName = methodName.substring(3).toLowerCase();
    		// 获取bean的address属性的旧值,BeanUtils是Apache的一个工具类
    //		Object oldValue =  BeanUtils.getProperty(bean,propertyName);
    		Object oldValue = bean.getAddress();
    		// 调用它的set方法
    		proceed(bean,newValue);
    		// 触发监听器
    		bean.propertyChangeSupport.firePropertyChange(propertyName,oldValue,newValue);
    		System.out.println("bean:"+bean.toString() + "
    oldValue:"+oldValue + "
    newValue" + newValue);
    	}
    }

    使用时的注意事项:

    An aspect may only introduce members with public or private access specification

    Aspect添加的新字段,新方法,修饰符可以是public,private 

    Multiple aspects may introduce the same named members as long as they have private access

    多个Aspect可以添加private修饰的,同名称的字段 

    An aspect may introduce fields (final as well as non-final), methods, and constructors to classes as well as interfaces, notice that the aspect may introduce methods along with their implementations to an interface

    Aspect可以在类,接口上,添加字段,方法,构造器。对于同一接口的不同实现类同时添加方法的实现

    If a class contains a method, and an aspect introduces the same method to its base interfaces, the implementation in the class takes precedence

    若Aspect添加方法时,该方法在类中已存在,类中的方法具有高优先级 

    A member-introduction declaration can use only one type

    Aspect添加新字段,新方法时,只能在单个类上。类签名不能使用通配符

    1.2   modify hierarchy

    修改类层次结构的语法有两种

    declare parents : (TypePattern) implements (interfaceList)
    declare parents : (TypePattern) extends (Class or InterfaceList)
    

      第一种语法是修改类的实现结构。

      第二种语法是修改类的继承结构。

    无论哪种方式都需要遵循Java的语法规则,例如不能多继承,类不能继承接口等。

    例如给包下面的所有实体添加BeanSupport接口,代码如下:

    // 为Customer添加BeanSupport接口
    declare parents : ch5.*Customer implements BeanSupport;
    

    1.3      batch member introduction

    实现批量添加类成员的步骤如下:

    第一步,编写任意的Marker interface,它的作用类似于标签,主要是把归类。

    第二步,通过AspectJ给Marker interface上动态的添加成员。

    第三步,验证,示例如下

    /**
     * 
     * @File Name: TimeTracker.aj 
     * @Description: 跟踪接口的最后一次调用时间
     * @version 1.0
     * @since JDK 1.8
     */
    public aspect TimeTracker {
    	
    	private static interface LastAccessTimeHolder {
    		static aspect Impl {
    			private long LastAccessTimeHolder.lastAccessTime;
    			
    			public long LastAccessTimeHolder.getLastAccessTime(){
    				return lastAccessTime;
    			}
    			
    			public void LastAccessTimeHolder.setLastAccessTime(long time){
    				lastAccessTime = time;
    			}
    		}
    	}
    	// 所有标注@Service注解的类实现LastAccessTimeHolder接口
    	declare parents : ch5.* implements LastAccessTimeHolder;
    	
    	// 定义Before Advice
    	before(LastAccessTimeHolder service) : execution(* LastAccessTimeHolder+.*(..)) && this(service) && !within(TimeTracker){
    		service.setLastAccessTime(System.currentTimeMillis());
    		System.out.println("something happen,"+ System.currentTimeMillis());
    	}
    }
    

    1.4   add annotation

    添加注解的语法有以下四种:

    declare @method : <Method signature pattern>: <Annotation>
    

      给方法上添加注解,例如declare @method : * ch5.*Customer.*(..): @Test(‘test’),给ch5,类名后缀为Customer的所有方法添加注解@Test,value属性值为test

    declare @constructor : <Constructor signature pattern>: <Annotation>
    

      给构造器上添加注解,格式与方法一样。  

    declare @field : <Field signature pattern> : <Annotation>
    

      给字段上添加注解,例如 declare @field : * ch5.Customer.required* : @NotNull,给ch5包下的Customer类,以required为前缀的字段添加@NotNull注解。

    declare @type : <Type signature pattern> : <Annotation>
    

      给类上添加注解,例如 declare @type : ch5..* @Test,给ch5包,以及子包下的所有类添加@Test注解。

      注:

    各类型添加的注解@Target属性必须支持,例如给方法上添加@Test注解时,该注解的@Target必须包含ElementType.METHOD

    注解的@Rention属性必须为RUNTIME

    2、WTD

    2.1    weave time error

    编译的错误,警告提示,它的语法有两种:

    declare error : <pointcut> : <message>
    

      错误提示,其中pointcut只能是kinded pointcut,不能包含runtime时的pointcut,例如this(),message是错误提示的信息。例如 declare error : test() : “测试错误提示”。其中test()为pointcut名称,例如pointcut test(): execution (* Customer.*(..))。它会对Customer类下所有方法进行错误提示。

    2.2    weave time warning

    declare warning : <pointcut> : <message>
    

      警告提示,与error语法格式类似,区别只是将关键字error替换为warning

    2.3   soft checked exception

    解除CheckedException限制的语法格式为:

    declare soft : <ExceptionTypePattern> : <pointcut>
    

      ExceptionTypePattern为必检异常,pointcut类型为方法的call。

  • 相关阅读:
    Android Jetpack之WorkManager: 观察结果
    解决'androidx.arch.core:core-runtime' has different version for the compile (2.0.0) and runtime (2.0.1)
    我要研究一下minio,管理大量的照片
    分发消息的写法
    百度地图坐标转换
    HighChart 实现从后台取数据来实时更新柱状和折线组图
    导出Excel
    Java 8新特性之集合
    java中的Switch case语句
    提问:"~"运算符
  • 原文地址:https://www.cnblogs.com/rain144576/p/14708701.html
Copyright © 2020-2023  润新知