zoukankan      html  css  js  c++  java
  • spring-101-springAOP

    前言:

    在很多场景下有一个共同的业务模块。比如用户在网站购物时,加入商品到购物车,下单,支付,等等,但是所有的业务都需要打日志,但是每个业务重写一遍日志又比较麻烦。此时springAOP可以适用这样的情况。

    AOP(Aspect Oriented Programming),即面向切面编程(也叫面向方面编程,面向方法编程)。其主要作用是,在不修改源代码的情况下给某个或者一组操作添加额外的功能。像日志记录,事务处理,权限控制等功能,都可以用AOP来“优雅”地实现,使这些额外功能和真正的业务逻辑分离开来,软件的结构将更加清晰。

    springAOP概念较多,但是最为关键的有2个:

    • Advice
          通知,多个业务模块都用到一个共同的功能,那么这个共同的功能,比如打日志,这个就是通知。
      
    • Pointcut
          切入点,多个业务模块都用到一个共同的功能,那么这个多个业务模块,比如加入商品到购物车,这个就是切入点。
      

    spring为了方便,对这两个进行了组合

    • Advisor
          这个其实是Advice和Pointcut的组合,Advice和Pointcut组成的独立的单元。
      也就是说,多个业务模块都用到一个共同的功能,那么这个整体就叫Advisor
      

    就依用户网站购物,加入商品到购物车,下单,支付都用到了加入日志为例子做一个DEMO。

    1.用户多业务场景

    用户在网站购物三个业务模块,加入商品到购物车,下单,支付代码,如下,代码清单1:

    package wang.conge.springdemo.aop.service;
    
    import org.springframework.stereotype.Component;
    
    import wang.conge.springdemo.aop.annotation.AopLogAnnotation;
    
    @Component
    public class UserService {
    	
    	@AopLogAnnotation
    	public void addCar() {
    		System.out.println("加入购物车");
    	}
    	
    	@AopLogAnnotation
    	public void createOrder() {
    		System.out.println("下单");
    	}
    	
    	@AopLogAnnotation
    	public void pay() {
    		System.out.println("付款");
    	}
    }
    
    

    代码清单1 用到了注解作为切入点的最小单元,先列出来,下面说明,代码清单2:

    package wang.conge.springdemo.aop.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AopLogAnnotation {
    	String value() default "";
    }
    
    

    2.共同业务模块

    用户在网站购物三个业务模块,加入商品到购物车,下单,支付代码,但是都用到了加入日志,如下,代码清单3:

    package wang.conge.springdemo.aop.service;
    
    import java.lang.reflect.Method;
    import java.util.Optional;
    
    import org.aopalliance.aop.Advice;
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.Pointcut;
    import org.springframework.aop.support.AbstractPointcutAdvisor;
    import org.springframework.aop.support.StaticMethodMatcherPointcut;
    import org.springframework.stereotype.Component;
    
    import wang.conge.springdemo.aop.annotation.AopLogAnnotation;
    
    @Component
    public class CommonLogAdvisor extends AbstractPointcutAdvisor{
    	private static final long serialVersionUID = 1L;
    	
    	@Override
    	public Pointcut getPointcut() {
    		return new StaticMethodMatcherPointcut() {
    			
    			@Override
    			public boolean matches(Method method, Class<?> targetClass) {
    				//TODO matches targetClass 
    				//if(targetClass matches AopTargetClass) return true;
    				
    				//TODO matches method 
    				//if(method matches AopMethod) return true;
    				
    				if(method.isAnnotationPresent(AopLogAnnotation.class)) {
    					return true;
    				}
    				
    				return false;
    			}
    			
    		};
    	}
    
    	@Override
    	public Advice getAdvice() {
    		return new MethodInterceptor() {
    			@Override
    			public Object invoke(MethodInvocation invocation) throws Throwable {
    				Object ret = null;
    				try {
    					//TODO before
    					System.out.println("=====SpringAopAdvisor-调用前before逻辑===");
    					
    					ret = invocation.proceed();
    					
    					//TODO after
    					System.out.println("=====SpringAopAdvisor-调用后after逻辑===");
    					
    					return ret;
    				} catch (Exception e) {
    					//TODO throw
    					System.out.println("=====SpringAopAdvisor-调用异常throw逻辑===");
    					return Optional.empty();
    				} finally {
    					//TODO finally
    					System.out.println("=====SpringAopAdvisor-调用最终结束逻辑===");
    				}
    			}
    		};
    	}
    	
    }
    
    

    下面对代码说明,为整个DEMO中核心代码:

    • public Advice getAdvice() ;该方法就是返回共同的业务所做的事情,在业务调用时就会在合适的逻辑之前,或之后,或发生异常,去完成相应的共同逻辑处理。
    • public Pointcut getPointcut() ; 该方法返回拦截业务的规则,事实上,加入商品到购物车,下单,支付并不是切入点,而加入商品到购物车,下单,支付这几个业务怎样在哪里加入共同日志的方式,或者规则,才叫切入点。方法里利用注解的方式,如果方法上注解了AopLogAnnotation,就会拦截,否则不拦截。也可以进进一步从类的Class做拦截的规则,这个看具体业务拦截的方式了。

    3.配置代码

    剩下的代码则是对上述代码进行组织运行了。
    配置代码,启用EnableAspectJAutoProxy支持。代码清单4:

    package wang.conge.springdemo.aop.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass=true)
    @ComponentScan("wang.conge.springdemo.aop.service")
    public class AppConfig {
    	
    }
    

    运行主代码,代码清单5:

    package wang.conge.springdemo.aop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import wang.conge.springdemo.aop.config.AppConfig;
    import wang.conge.springdemo.aop.service.UserService;
    
    public class AppStart {
    
    	public static void main(String[] args) {
    		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    		
    		UserService myService = applicationContext.getBean(UserService.class);
    		myService.addCar();
    		
    		myService.createOrder();
    	}
    
    }
    
    

    OK,整个DEMO就结束了,紧紧抓住AOP的主题思想:

    • 多个业务有共同功能,多个业务切入点,也就是被拦截的地方,或者拦截这些多个业务的规则
    • 多个业务共同的功能,也就是该共同功能所做的事情。

    上述代码是以springAOP做实例,事实上spring也支持aspectj ,但是运行的方式还是根据代理的形式。
    而spring实现AOP的技术,主要分为两大类:

    一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;
    二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。


    如果要是原生的aspectj 还是比较复杂,牵扯到织入的细节。aspectj是aop的Java实现方案。下面是几点关于aspectj的描述:

    • AspectJ是一个代码生成工具(CodeGenerator),其中AspectJ语法就是用来定义代码生成规则的语法。基于自己的语法编译工具,编译的结果是Java Class文件,运行的时候classpath需要包含AspectJ的一个jar文件(Runtime lib),支持编译时织入切面,即所谓的CTW机制,可以通过一个Ant或Maven任务来完成这个操作。
    • AspectJ有自己的类装载器,支持在类装载时织入切面,即所谓的LTW机制。使用AspectJ LTW有两个主要步骤,第一,通过JVM的-javaagent参数设置LTW的织入器类包,以代理JVM默认的类加载器;第二,LTW织入器需要一个 aop.xml文件,在该文件中指定切面类和需要进行切面织入的目标类
    • AspectJ同样也支持运行时织入,运行时织入是基于动态代理的机制。(默认机制)

    附录1,AOP详细概念:

    附录2,spring aop官方网站介绍资料:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html

  • 相关阅读:
    Python 递归函数详解
    CentOS7 删除virbr0虚拟网卡
    /usr/bin/docker-current: Error response from daemon: oci runtime error: container_linux.go:247: starting container process caused "process_linux.go:245: running exec setns .....
    Linux系统添加永久静态路由的方法(包含Centos7)
    正则表达式
    Unity3D -- shader语法内置函数
    Unity3D -- shader光照常用函数和变量
    Unity3D -- shader常用函数和变量
    Unity 着色器训练营(2)
    Unity Shader着色器优化
  • 原文地址:https://www.cnblogs.com/conge/p/5508066.html
Copyright © 2011-2022 走看看