三、AOP(Aspect Oriented Programming)

一、简介

  1. AOP(Aspect Oriented Programming)面向切面编程。将横向分布在系统中的与业务功能无关的代码,如日志,抽取出来,单独管理,减少重复。

二、AOP核心概念

  1. 横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
  2. 切面(Aspect):切面是一个类,是对横切关注点的抽象
  3. 连接点(Join point):被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
  4. 切入点(Pointcut):对连接点进行拦截的定义
  5. 通知(Advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
  • 前置通知(Before advice):在某个连接点(Join point)之前执行的通知,但这个通知不能阻止连接点的执行(除非它抛出一个异常;如果抛出了异常,连接点会不会执行,但后续的通知会继续执行)
  • 后置通知(After(finally)advice):当某个连接点(Join point)退出的时候执行的通知(不论是正常返回还是发生异常退出)(如果连接点的方法发生了异常,后续通知会继续执行)
  • 异常通知:在方法抛出异常后执行的通知
  • 返回后通知(After returning advice):在某个连接点(Join point)正常完成后执行的通知。例如,一个方法没有抛出任何异常正常返回
  • 环绕通知(Around advice):包围一个连接点(Join point)的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行
  1. 目标对象(Target):代理的目标对象
  2. 织入(Weaving):将切面应用到目标对象并导致代理对象创建的过程
  3. 引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

三、Spring对AOP的支持

1. Spring创建代理的规则为

  1. Spring默认使用Java的动态代理来创建AOP代理
  • Java动态代理:
  1. 当需要代理的类不是代理接口的时候,Spring会使用CGLIB代理,也可以强制使用CGLIB代理
  • CGLIB(Code Generation Library):是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口

四、案例

1. xml配置文件形式

项目结构
三、AOP(Aspect Oriented Programming)_第1张图片

  1. 引入相关jar(需要单独引入合适的aspectj,aopalliance)

	4.0.0
	com.lt.aop
	spring_aop
	0.0.1-SNAPSHOT
	war
	
		4.3.23.RELEASE
	
	
		
		    org.springframework
		    spring-aop
		    ${spring-version}
		
		
		    org.springframework
		    spring-context
		    ${spring-version}
		
		
		
		    org.aspectj
		    aspectjweaver
		    1.8.8
		
		
		
		    aopalliance
		    aopalliance
		    1.0
		
	

  1. 配置文件(AOP模板)


	
	
	
		
			
			
			
		
	

  1. 业务类
package com.lt.aop01;

/**
 * @author lt
 * @date 2019年4月29日
 * @version v1.0
 */
public class Printer {

	public void doPrint() {
		System.out.println("进入PrintImpl,并调用doPrint()方法!");
	}

}
  1. 日志类
package com.lt.aop01;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author lt
 * @date 2019年4月29日
 * @version v1.0
 */
public class Log {

	public void doLogBefore(){
		System.out.println("before:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
	}
	
	public void doLogAfter(){
		System.out.println("after:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
	}
	
	public void doLogException(){
		System.out.println("exception:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
	}
}
  1. 客户端测试
package com.lt.aop01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author lt
 * @date 2019年4月29日
 * @version v1.0
 */
@SuppressWarnings("all")
public class Client {

	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("aop_01.xml");
		Printer print = (Printer) ctx.getBean("printer");
		print.doPrint();
	}
}
  1. 测试结果
五月 05, 2019 10:20:39 上午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6576fe71: startup date [Sun May 05 10:20:39 CST 2019]; root of context hierarchy
五月 05, 2019 10:20:39 上午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [aop_01.xml]
before:2019-05-05 10:20:40
进入PrintImpl,并调用doPrint()方法!
after:2019-05-05 10:20:40

AOP中切入点表达式:execution

1. execution(* com.lt.aop01..*.doPrint(..))
  • execution():表达式主体
  • 第一个*表示返回返回类型,*表示返回所有类型
  • com.lt.aop01..表示需要拦截的包名,..表示拦截当前包,子包,子孙包
  • *.doPrint(..)*表示所有类,表示拦截doPrint()方法,方法中的两个圆点表示拦截任何参数
  • 2. 半注解方式(配置文件+注解)

    1. 项目结构
      三、AOP(Aspect Oriented Programming)_第2张图片
    2. 配置文件
    
    
        
    	
    	
    	
    	
    
    

    CGLIB与jdk动态代理

    1. 深入理解JDK动态代理机制
    2. Spring AOP中的JDK和CGLib动态代理哪个效率更高?
    1. 业务类
    package com.lt.aop02;
    
    import org.springframework.stereotype.Component;
    
    /**
     * @author lt
     * @date 2019年4月29日
     * @version v1.0
     */
    @Component
    public class Printer {
    
    	public void doPrint() {
    		System.out.println("进入PrintImpl,并调用doPrint()方法!");
    	}
    
    }
    
    1. 日志类
    package com.lt.aop02;
    
    import java.text.SimpleDateFormat;
    import java.util.Arrays;
    import java.util.Date;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    /**
     * @author lt
     * @date 2019年4月29日
     * @version v1.0
     */
    @Component
    @Aspect
    public class Log {
    	
    	/**
    	 * 1.此空方法用于声明切点表达式,定义切点需要拦截的内容
    	 * 2.@Pointcut("execution(* com.lt.aop02..*.*)")表示要拦截的内容
    	 * 3.通知直接使用此切点方法名即可引入切点表达式
    	 * @author lt
    	 * @date 2019年5月6日
    	 */
    	@Pointcut("execution(* com.lt.aop02..*.*(..))")
    	public void pointcutDeclaration(){}
    
    	/**
    	 * 前置通知
    	 * @author lt
    	 * @date 2019年5月6日
    	 * @param point
    	 */
    	@Before("pointcutDeclaration()")
    	public void doLogBefore(JoinPoint point){
    		String method = point.getSignature().getName();
    		System.out.print("before:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    		System.out.println(",调用方法:"+method+",参数:"+Arrays.asList(point.getArgs()));
    	}
    
    	/**
    	 * 后置通知
    	 * @author lt
    	 * @date 2019年5月6日
    	 * @param point
    	 */
    	@After("pointcutDeclaration()")
    	public void doLogAfter(JoinPoint point){
    		String method = point.getSignature().getName();
    		System.out.print("after:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    		System.out.println(",调用方法:"+method+",参数:"+Arrays.asList(point.getArgs()));
    	}
    	
    	/**
    	 * 异常通知
    	 * @author lt
    	 * @date 2019年5月6日
    	 * @param point
    	 */
    	@AfterThrowing("pointcutDeclaration()")
    	public void doLogException(JoinPoint point){
    		String method = point.getSignature().getName();
    		System.out.print("exception:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    		System.out.println(",调用方法:"+method+",参数:"+Arrays.asList(point.getArgs()));
    	}
    	
    	/**
    	 * 环绕通知
    	 * @author lt
    	 * @date 2019年5月6日
    	 * @param point
    	 */
    	@Around("pointcutDeclaration()")
    	public void doLogAround(ProceedingJoinPoint point){
    		String method = point.getSignature().getName();
    		try {
    			//环绕通知-前置通知
    			System.out.print("around-before:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    			System.out.println(",调用方法:"+method+",参数:"+Arrays.asList(point.getArgs()));
    			point.proceed();
    			//环绕通知-后置通知
    			System.out.print("around-after:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    			System.out.println(",调用方法:"+method+",参数:"+Arrays.asList(point.getArgs()));
    		} catch (Throwable e) {
    			System.out.print("around-exception:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    			System.out.println(",调用方法:"+method+",参数:"+Arrays.asList(point.getArgs()));
    			e.printStackTrace();
    		}
    	}
    }
    
    1. 客户端测试类
    package com.lt.aop02;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    /**
     * @author lt
     * @date 2019年4月29日
     * @version v1.0
     */
    @SuppressWarnings("all")
    public class Client {
    
    	public static void main(String[] args) {
    		ApplicationContext ctx = new ClassPathXmlApplicationContext("aop_02.xml");
    		Printer print = (Printer) ctx.getBean("printer");
    		print.doPrint();
    	}
    }
    
    1. 测试结果
    五月 06, 2019 2:47:16 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
    信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6576fe71: startup date [Mon May 06 14:47:16 CST 2019]; root of context hierarchy
    五月 06, 2019 2:47:16 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    信息: Loading XML bean definitions from class path resource [aop_02.xml]
    before:2019-05-06 14:47:17,调用方法:doPrint,参数:[]
    进入PrintImpl,并调用doPrint()方法!
    after:2019-05-06 14:47:17,调用方法:doPrint,参数:[]
    

    Spring中常用注解

    1. Spring系列之Spring常用注解总结

    参考

    【1】Spring3:AOP
    【2】spring AspectJ的Execution表达式

    你可能感兴趣的:(Spring,AOP,05.spring)