AOP(Aspect Oriented Programing) "面向切面编程"

面向切面编程

通过横向抽取机制来解决类中的代码重复问题

  1. 代理目标(target)
  • 被代理类实例代理的目标对象
  1. 代理对象(proxy)
  • 代理其他类的对象
AOP中的概念

  1. 连接点(JoinPoint) :执行点+方位
  2. 执行点 :任意一个可执行的方法
  3. 切点(PoinCut)
  • 对连接点进行筛选的条件 指定执行点和方位 确定在哪里添加代码
  1. Advice :在指定的切点所选择的连接点所加入的代码
  2. 切面(Aspect) :切面( Aspect ) = 切点( Pointcut ) + Advice
  3. 织入
  • 将 Advice 对应的代码 加入到 切点 所选择的 连接点 的过程
  • 实现方式:编译时织入 类加载时织入 运行期织入
  1. 方位

  • 方法执行前(before)
/**
* 任意一个 Advice 对应的方法中 , 第一个参数都可以是 org.aspectj.lang.JoinPoint
* 通过 JoinPoint 可以在 Advice 对应的方法内部 访问 连接点 的信息
* 可以通过 JoinPoint 来访问 连接点 中的信息
 */
 public void before (JoinPoint joinPoint){

        Signature signature = joinPoint.getSignature();//获取 连接点 对应的方法的 签名
        System.out.println("signature:"+signature);
       int mod = signature.getModifiers();//获取 方法 的修饰符 返回值为整数
        System.out.println("Modifier:"+Modifier.toString(mod));
        System.out.println("Name:"+signature.getName());
      Object[] args = joinPoint.getArgs();//获取被拦截方法在执行时接受的参数
        System.out.println("args:"+ Arrays.toString( args ));

        System.out.println( "【 " +joinPoint.getSignature().getName()+ "即将执行】" );
    }
  • 方法执行后(after)
  • 方法执行前后(around)
 public Object around( ProceedingJoinPoint joinPoint) throws Throwable {          //joinPoint 必须是 ProceedingJoinPoint 类型
        String  name = joinPoint.getSignature().getName();
        System.out.println( "开始为" + name + "计时" );
        long begin = System.nanoTime();

        Object result = joinPoint.proceed() ; // 通过 ProceedingJoinPoint 的  proceed 方法让被拦截的方法继续执行

        long end = System.nanoTime();
        System.out.println( "为" + name + "计时结束" );
        System.out.println( "[ " + name + " ]执行耗时 [ " + ( end - begin ) + "ns, ]" );

        return result ; // 返回 由  被拦截的方法执行后 所返回的值
    }
  • 方法正常返回后(after-return)
		   public void afterReturning (JoinPoint joinPoint ,Object returnValue){
        System.out.println("【 "+ joinPoint.getSignature().getName()+" 】方法执行后返回了【"+returnValue+"】");
    }
  • 方法抛出异常后(after-throw)
spring配置文件

  • AspectJ是语言级的AOP实现,AspectJ扩展了Java语言,能够在编译期提供横切代码的织入
  • 添加 aspectj-runtime 和 aspectj-weaver 依赖并引入 aop 命名空间
  • <aop:config proxy-target-class="false"> 是默认值 , 如果给定的类适合使用 JDK 动态代理来产生代理类,则采用JDK动态代理。否则采用 cglib 方式来创建
  • <aop:config proxy-target-class="true"> 表示一律使用 cglib 方式来创建代理类
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http:"http://www.springframework.org/schema/aop
                            http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="advices" class="com.it.aop.Advices"/>
//Advices 类中包含在指定切点 所选择的连接点 需要添加的方法
        <aop:config>
            <aop:aspect ref="advices">
			//aspect 切面引入 需要添加的代码
                <aop:pointcut id="firstpointcut" expression="execution(* com.it.aop.Fish.fly())"/>  //aop:pointcut 选择 某个类中的某个方法 作为切点 第一个*表示修饰符和返回类型
                <aop:after-returning method="afterReturning" pointcut-ref="firstpointcut" returning="returnValue"/>  //在 引入的切点 的指定方位(after-returning)处添加Advices类中afterReturning方法 并指定 返回变量 returnValue
                <aop:around method="around" pointcut-ref="firstpointcut"/>
                <aop:before method="before" pointcut-ref="firstpointcut"/>
                <aop:after method="after" pointcut-ref="firstpointcut"/>

            </aop:aspect>
        </aop:config>
  • introdcation
  • 在 aop:aspect </aop:aspect>中通过配置 declare-parents 为 Cat 类引入 HashMap的实现
<aop:declare-parents types-matching="com.it.aop.schema.Cat"
                                 implement-interface="java.util.Map"
                                 default-impl="java.util.HashMap"/>

<bean id="cat" class="com.it.aop.schema.Cat" p:name="Tom"/>

    <aop:config proxy-target-class="true">

        <aop:aspect>
            
            <aop:declare-parents types-matching="com.it.aop.schema.Cat"
                                 implement-interface="java.util.Map"
                                 default-impl="java.util.HashMap"/>
            
        </aop:aspect>
    </aop:config>
</beans>
测试(Test)
  • Fish 类实现了 flyable 接口
  • <aop:config proxy-target-class="true"> 表示一律使用 cglib 方式来创建代理类
  • 测试结果
【proxy.toString()】: Fish{com.it.aop.schema.Fish:230526532}
【proxy.getClass().getCanonicalName()】: com.it.aop.schema.Fish$$EnhancerBySpringCGLIB$$b63a9d5
【proxy.getClass().getSuperclass()】: class com.it.aop.schema.Fish
  • <aop:config proxy-target-class="false"> 时测试结果
【proxy.toString()】: Fish{com.it.aop.schema.Fish:2005028997}
【proxy.getClass().getCanonicalName()】: com.sun.proxy.$Proxy6
【proxy.getClass().getSuperclass()】: class java.lang.reflect.Proxy

  • Cat 类没有实现接口 proxy-target-class="false" / "true" 测试结果相同
【proxy.toString()】: com.it.aop.schema.Cat@3e7dd664
【proxy.getClass().getCanonicalName()】: com.it.aop.schema.Cat$$EnhancerBySpringCGLIB$$42ce28ee
【proxy.getClass().getSuperclass()】: class com.it.aop.schema.Cat
  • 使用 cglib 方式创建了 proxy 类
【proxy.toString()】: com.it.aop.schema.Cat@51acdf2e
【proxy.getClass().getCanonicalName()】: com.it.aop.schema.Cat$$EnhancerBySpringCGLIB$$42ce28ee
【proxy.getClass().getSuperclass()】: class com.it.aop.schema.Cat
  • 使用 cglib 方式创建的代理类是Fish类的子类
String config = "classpath*:com/it/aop/schema/aop-schema.xml";

        AbstractApplicationContext iocContainer = new ClassPathXmlApplicationContext(config);

       Object proxy =  iocContainer.getBean("fish");

        System.out.println(proxy.toString());

        System.out.println(proxy.getClass().getCanonicalName());

      if (proxy instanceof  flyable){
          flyable fish = (flyable) proxy;
          fish.fly();
      }
      iocContainer.close();
基于jdk下的动态proxy代理测试
  • 主要涉及 java.lang.reflect 包中的 proxy类 和 InvocationHandler 接口
  • 通过实现 InvocationHandler(内部是匿名内部类) 定义横切逻辑,并通过反射机制调用目标类的代码
  • 动态地将横切逻辑和业务逻辑编织在一起
public static void main(String[] args) {

        Prompter p = new Prompter();

        // 代理目标
        final Object target = new Airplane();

        Class<?> targetClass = target.getClass(); //获取代理目标对应的类
        ClassLoader loader = targetClass.getClassLoader(); // 获取 代理目标对应的类 的 "类加载器"
        Class<?>[] interfaces = targetClass.getInterfaces(); // 获取 代理目标对应的类 所实现的接口

        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null ;

                try {
                    // 方位: 方法执行前
                    if( "fly".equals( method.getName() ) ) { // 在所有方法执行之前 、对 方法名称进行了筛选
                        p.before( method.getName() ); // Advice
                    }
                    // 方位: 方法执行前***: before
                    result = method.invoke(target, args); // 执行点	调用 target 所引用的对象的 method 表示的方法
                    // 方位: 方法正常执行前***: after
                    // 方位: 正常返回后 ( 绝对没有发生异常 )
                } catch ( Exception e ) {
                    // 方位: 抛出异常后
                    e.printStackTrace();
                }

                // 方位: 方法执行后
                if( "fly".equals( method.getName() ) ) { // 在所有方法执行之后 、对 方法名称进行了筛选
                    p.after( method.getName() ); // Advice
                }

                return result;
            }
        };

        // 代理对象
        Object proxy = Proxy.newProxyInstance( loader , interfaces , handler );

        String s = proxy.toString(); // handler.invoke

        System.out.println( s );

        if( proxy instanceof Flyable ){
            Flyable f = (Flyable) proxy;
            f.fly();
        }

    }
  • 产生的代理类以及代理类的父类测试结果
  • 在基于JDK下代理,代理类的父类是 java.lang.reflect 类的子类
【proxy】: com.it.proxy.Factory@77b52d12
【proxy.getClass().getCanonicalName()】: com.sun.proxy.$Proxy0
【proxy.getClass().getSuperclass()】: class java.lang.reflect.Proxy

对于proxy代理类 内部的实现进行 猜想


```java

    public interface flyable {
        void fly();
    }

  public class Proxy {

      protected  InvocationHandler h ;

      protected Proxy( InvocationHandler  h ) {
             this.h = h ;
      }

      //......

  }


  // 注意这里不考虑异常处理
  
  public class $Proxy0 extends Proxy implements Nubia {

     private static Method m0 ;
     private static Method m1 ;
     private static Method m2 ;
     private static Method m3 ;
     
     static { 
         
         Class<?> objectClass = Class.forName( "java.lang.Object" ) ;
         
         m0 = objectClass.getDeclaredMethod( "equals" , Object.class );
         m1 = objectClass.getDeclaredMethod( "toString" ) ;
         m2 = objectClass.getDeclaredMethod( "hashCode" ) ;
         
         Class<?> flyClass = Class.forName( "包名.flyable" ) ;
         m3 = flyClass.getDeclaredMethod( "produce" );
         
     }

      public $Proxy0( InvocationHandler handler ){
            super( handler );
      }

      public final boolean equals(Object another) {
          Object[] args = { another };
          return this.h.invoke( this , m0, args ); // m0 ==> equals
      }

      public final  String toString() {
          // 调用  InvocationHandler 实例的 invoke 方法时需要传入 三个参数:
          // 第一个参数表示 代理对象 ( 当前对象 this 就是 代理对象 ) 
          // 第二个参数表示 正在执行的方法 ( 就是 与当前的 toString 同名的方法 )
          // 第三个参数表示 正在执行的方法 需要传入的参数 ( 如果没有参数,可以传一个 空数组 )
          Object[] args = { };
          return this.h.invoke( this , m1 , args ); // m1 ==>  toString
      }

      public final int hashCode() {
          Object[] args = {  };
          return this.h.invoke( this , m2, args ); // m2 ==> hashCode
      }

      public final Phone produce(){
          Object[] args = {  };
          return this.h.invoke( this , m3, args );  // m3 ==> produce
      }

  }

  • 在 ServiceAspect 类中通过 方法和注解 结合使用 定义 切点 , 方位 , Advice

[@Component](https://my.oschina.net/u/3907912)
[@Aspect](https://my.oschina.net/aspect)
public class ServiceAspect {

    @Pointcut("execution(* com.it.aop.annotation.service.StudentService.save(..))")
    public void servicePointcut(){
    }

    @Before("servicePointcut()")
    public void before(JoinPoint joinPoint){
        System.out.println("【"+joinPoint.getSignature().getName()+"】方法即将执行");
    }

    @After("servicePointcut()")
    public void after(JoinPoint joinPoint){
        System.out.println("【"+joinPoint.getSignature().getName()+"】方法即将结束");
    }

    @Around("servicePointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        String  name = proceedingJoinPoint.getSignature().getName();
        System.out.println( "开始为" + name + "计时" );
        long begin = System.nanoTime();

        Object result = proceedingJoinPoint.proceed();

        long end = System.nanoTime();
        System.out.print( "为" + name + "计时结束," );
        System.out.println( "[ " + name + " ]执行耗时 [ " + ( end - begin ) + "ns, ]" );

        return result;
    }
    @AfterReturning(pointcut = "servicePointcut()" , returning = "xx")
    public void afterReturn (JoinPoint joinPoint , Object xx){
        System.out.println(joinPoint.getSignature().getName()+"方法正常返回了"+xx);
    }

    @AfterThrowing( pointcut = "servicePointcut()" , throwing = "ex")
    public void afterThrow( JoinPoint joinPoint , Throwable ex ) {
        System.out.println("【 " + joinPoint.getSignature().getName() + " 】方法执行执行时抛出 【 " + ex +" 】");
    }


}

转载于:https://my.oschina.net/u/4041837/blog/2986469

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值