zoukankan      html  css  js  c++  java
  • AOP相关

    AOP

    (一)动态代理

    1. 方式

    • 基于接口实现动态代理: JDK动态代理
    • 基于继承实现动态代理: Cglib、Javassist动态代理

    2. 一个方法

    Object proxyObject = Proxy.newProxyInstance(ClassLoader classLoader, Class[] interfces, InvocationHandler h)

    方法的作用:动态的创建实现了interfaces数组中所有指定接口的实现类对象:

    参数:

    • ClassLoader:类加载器,把.class文件加载到内存,形成Class对象。
    • Class[] interfaces:指定要实现的接口们
    • InvocationHandler:代理对象的所有方法(个别不执行,getClass())都会调用InvocationHandler的invoke()方法

    3. InvocationHandler

    public Object invoke(Object proxy, Method method, Object[] args)

    这个invoke()方法什么时候被调用:在调用代理对象所实现接口中的方法时。

    invoke()方法的参数:
    • Object proxy:当前对象,即代理对象(在调用谁的方法)
    • Method method:当前被调用的方法(目标方法)
    • Object[] args:实参(传入目标方法中需要传入的实际参数)

    4. 动态代理作用

    最终是学习AOP(面向切面编程),它与装饰者模式有点相似,它比装饰者模式还要灵活!

    目标对象:被增强的对象
    代理对象:需要目标对象,然后在目标对象上添加了增强后的对象!
    目标方法:增强的内容

    代理对象 = 目标对象 + 增强

    5. 代码实现

    public Object getProxy(){
      //获取当前类的类加载器,用来加载代理对象所属类
      ClassLoader loader = this.getClass().getClassLoader();
      //获取目标对象实现的所有接口的Class,代理类回合目标类实现相同的接口,最终通过代理对象实现功能
      Class[] interfaces = mathImpl.getClass().getInterfaces();
      
      return Proxy.newProxyInstance(loader,interfaces,new InvocationHander(){
        //代理对象实现功能的方式
        @Override
        public Object invoke(Object proxy,Method method,Object[] args)throws Throwale{
          try{
            MyLogger.before(method.getName(), Arrays.toString(args));//增强的功能
            Object result = method.invoke(mathImpl, args);//动态代理对象实现功能
            MyLogger.after(method.getName(), result);//增强的功能
            return result;
          } catch (Exception e) {
    					MyLogger.throwing();
    					e.printStackTrace();
    				} finally {
    					System.out.println("哪都有我");
    				}
    				return null;
        }
      });
    }
    

    (二)AOP概述

    AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。

    • 面向对象 纵向继承机制

    • 面向切面 横向抽取机制

    AOP编程操作的主要对象是切面(aspect),而切面用于模块化横切关注点(公共功能)。在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的类里——这样的类我们通常称之为“切面”

    (三)AOP术语

    横切关注点

    每个方法中抽取出来了的同一类非核心业务

    切面(Aspect)

    封装横切关注点信息的类,每个关注点体现为一个通知方法。

    通知(Advice)

    切面必须要完成的各个具体工作,就是在其它类中的横切关注点。

    分为五种通知

    ① @Before:前置通知,在方法执行之前执行

    ② @After:后置通知,在方法执行之后执行

    ③ @AfterRunning:返回通知,在方法返回结果之后执行

    ④ @AfterThrowing:异常通知,在方法抛出异常之后执行

    ⑥ @Around:环绕通知,围绕着方法执行

    目标(Target)

    被通知的对象,就是被的代理的对象

    代理(Proxy)

    向目标对象应用通知之后创建的代理对象

    连接点(Joinpoint)

    就是横切关注点抽取的位置,例如:类某个方法调用前、调用后、方法捕获到异常后等

    切入点(pointcut)

    相对于代理类来说的,就是找到连接点的位置,在此位置切入方法。

    切入表达式

    @Before(value = "execution(public int com.atguigu.spring.aop.MathImpl.add(int, int))")

    (四)AspectJ

    AspectJ:Java社区里最完整最流行的AOP框架。

    1. 在Spring中启用AspectJ注解支持

    1)导包

    com.springsource.net.sf.cglib-2.2.0.jar

    com.springsource.org.aopalliance-1.0.0.jar

    com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

    spring-aop-4.0.0.RELEASE.jar

    spring-aspects-4.0.0.RELEASE.jar

    2)引入aop命名空间

    3)配置

    <aop:aspectj-autoproxy>
    当Spring IOC容器侦测到bean配置文件中的<aop:aspectj-autoproxy>元素时,会自动为	与AspectJ切面匹配的bean创建代理
    
    
      <context:component-scan base-package="com.atguigu.spring.aop"></context:component-scan>
    
      <!-- 开启aspectJ的自动代理功能 -->
      <aop:aspectj-autoproxy />
    

    2. 用AspectJ注解声明切面

    1. 要在 Spring 中声明 AspectJ 切面,只需要在IOC容器中将切面声明为 bean 实例

    2. 当在SpringIOC 容器中初始化 AspectJ 切面之后,SpringIOC 容器就会为那些与 AspectJ 切面相匹配的 bean 创建代理

    3. 在 AspectJ注解中,切面只是一个带有@Aspect 注解的 Java 类,他往往要包含很多通知。

    4. 通知是标识有某种注解的简单的Java 方法

    5. AspectJ 支持5中类型的通知注解:

      ① @Before:前置通知,在方法执行之前执行

      ② @After:后置通知,在方法执行之后执行

      ③ @AfterRunning:返回通知,在方法返回结果之后执行

      ④ @AfterThrowing:异常通知,在方法抛出异常之后执行

      ⑥ @Around:环绕通知,围绕着方法执行

    (五)AOP细节

    1. 切入点表达式

    @Before(value="execution(* com.atguigu.spring.aop.*.*(..))")
         * 第一个*代表任意的访问修饰符和返回值类型
         * 第二个*代表任意类
         * 第三个*代表类中任意方法
         * ..代表任意的参数列表
    

    注意:在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来

    2. 当前连接点细节

    切入点表达式通常都会是从宏观上定位一组方法,和具体某个通知的注解结合起来就能够确定对应的连接点。那么就一个具体的连接点而言,我们可能会关心这个连接点的一些具体信息,例如:当前连接点所在方法的方法名、当前传入的参数值等等。这些信息都封装在JoinPoint接口的实例对象中。

    @Before(value="execution(* com.atguigu.spring.aop.*.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
      Object[] args = joinPoint.getArgs();//获取方法的参数
      String methodName = joinPoint.getSignature().getName();//获取方法名
      System.out.println("method:"+methodName+",arguments:"+Arrays.toString(args));
    }
    

    3. 通知

    • 在具体的连接点上要执行的操作。

    • 一个切面可以包括一个或者多个通知。

    • 通知所使用的注解的值往往是切入点表达式

    1)前置通知

    • 前置通知:在方法执行之前执行的通知

    • 使用@Before注解

    2)后置通知

    • 后置通知:后置通知是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候。作用于方法的finally语句块,即不管有没有异常都会执行

    • 使用@After注解

    3)返回通知

    • @AfterReturning:将方法标注为返回通知
    • 返回通知:作用于方法执行之后,就是try{} 语句块中的最后一句
    • 在返回通知中,只要将returning属性添加到@AfterReturning注解中,就可以访问连接点的返回值。该属性的值即为用来传入返回值的参数名称
    • 要想在方法中使用,必须在方法的形参中设置和变量名相同的参数名的参数(必须在通知方法的签名中添加一个同名参数。在运行时Spring AOP会通过这个参数传递返回值)

    4)异常通知

    • 异常通知:@AfterThrowing:将方法标注为异常通知(例外通知),只在连接点抛出异常时才执行异常通知

    • 将throwing属性添加到@AfterThrowing注解中,也可以访问连接点抛出的异常。Throwable是所有错误和异常类的顶级父类,所以在异常通知方法可以捕获到任何错误和异常。

    • 如果只对某种特殊的异常类型感兴趣,可以将参数声明为其他异常的参数类型。然后通知就只在抛出这个类型及其子类的异常时才被执行

    5)环绕通知

    • 环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。

    • 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。

    • 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。

    • 注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。

    代码

    package com.atguigu.spring.aop;
    
    import java.util.Arrays;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    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.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect//标注当前类为切面
    @Order(1)//定义切面作用的优先级,值越小优先级越高,默认值为int的最大值
    public class MyloggerAspect {
    	//重用切入点定义
    	@Pointcut(value="execution(* com.atguigu.spring.aop.*.*(..))")
    	public void test() {}
    
    	/**
    	 * @Before:将方法指定为前置通知
    	 * 必须设置value,其值为切入点表达式
    	 * 前置通知:作用于方法执行之前
    	 */
    	//@Before(value = "execution(public int com.atguigu.spring.aop.MathImpl.add(int, int))")
    	@Before(value="test()")
    	public void beforeMethod(JoinPoint joinPoint) {
    		Object[] args = joinPoint.getArgs();//获取方法的参数
    		String methodName = joinPoint.getSignature().getName();//获取方法名
    		System.out.println("method:"+methodName+",arguments:"+Arrays.toString(args));
    	}
    	
    	/**
    	 * @After:将方法标注为后置通知
    	 * 后置通知:作用于方法的finally语句块,即不管有没有异常都会执行
    	 */
    	//@After(value="execution(* com.atguigu.spring.aop.*.*(..))")
    	@After(value="test()")
    	public void afterMethod() {
    		System.out.println("后置通知");
    	}
    	
    	/**
    	 * @AfterReturning:将方法标注为返回通知
    	 * 返回通知:作用于方法执行之后
    	 * 可通过returning设置接收方法返回值的变量名
    	 * 要想在方法中使用,必须在方法的形参中设置和变量名相同的参数名的参数
    	 */
    	//@AfterReturning(value="execution(* com.atguigu.spring.aop.*.*(..))", returning="result")
    	@AfterReturning(value="test()", returning="result")
    	public void afterReturningMethod(JoinPoint joinPoint, Object result) {
    		String methodName = joinPoint.getSignature().getName();
    		System.out.println("method:"+methodName+",result:"+result);
    	}
    	
    	/**
    	 * @AfterThrowing:将方法标注为异常通知(例外通知)
    	 * 异常通知(例外通知):作用于方法抛出异常时
    	 * 可通过throwing设置接收方法返回的异常信息
    	 * 在参数列表中课通过具体的异常类型,来对指定的异常信息进行操作
    	 */
    	//@AfterThrowing(value="execution(* com.atguigu.spring.aop.*.*(..))", throwing="ex")
    	@AfterThrowing(value="test()", throwing="ex")
    	public void afterThrowingMethod(ArithmeticException ex) {
    		System.out.println("有异常了,messages:"+ex);
    	}
    	
    	//环绕通知
    	@Around(value="execution(* com.atguigu.spring.aop.*.*(..))")
    	public Object aroundMethod(ProceedingJoinPoint joinPoint) {
    		
    		Object result = null;
    		
    		try {
    			//前置通知
    			System.out.println("前置通知");
    			result = joinPoint.proceed();//执行方法
    			//返回通知
    			System.out.println("返回通知");
    			return result;
    		} catch (Throwable e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			//异常通知
    			System.out.println("异常通知");
    		} finally {
    			//后置通知
    			System.out.println("后置通知");
    		}
    		
    		return -1;
    	}
    }
    
    

    4. 重用切入点定义 和 指定切面的优先级

    优先级

    1. 在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的。

    2. 切面的优先级可以通过实现Ordered接口或利用@Order注解指定。

    3. 实现Ordered接口,getOrder()方法的返回值越小,优先级越高。

    4. 若使用@Order注解,序号出现在注解中

    (六)以XML方式配置切面

    ​ 除了使用AspectJ注解声明切面,Spring也支持在bean配置文件中声明切面。这种声明是通过aop名称空间中的XML元素完成的。

    ​ 正常情况下,基于注解的声明要优先于基于XML的声明。通过AspectJ注解,切面可以与AspectJ兼容,而基于XML的配置则是Spring专有的。由于AspectJ得到越来越多的 AOP框架支持,所以以注解风格编写的切面将会有更多重用的机会。

    1. 配置

    在bean配置文件中,所有的Spring AOP配置都必须定义在<aop:config>元素内部。对于每个切面而言,都要创建一个<aop:aspect>元素来为具体的切面实现引用后端bean实例。

    切面bean必须有一个标识符,供<aop:aspect>元素引用。

    2. 声明切入点

    1. 切入点使用<aop:pointcut>元素声明。

    2. 切入点必须定义在<aop:aspect>元素下,或者直接定义在<aop:config>元素下。

    ​ ① 定义在<aop:aspect>元素下:只对当前切面有效

    ​ ② 定义在<aop:config>元素下:对所有切面都有效

    1. 基于XML的AOP配置不允许在切入点表达式中用名称引用其他切入点。

    3. 声明通知

    1. 在aop名称空间中,每种通知类型都对应一个特定的XML元素。

    2. 通知元素需要使用<pointcut-ref>来引用切入点,或用<pointcut>直接嵌入切入点表达式。

    3. method属性指定切面类中通知方法的名称

    	<context:component-scan base-package="com.atguigu.spring.aopxml"></context:component-scan>
    	<!-- 配置 -->
    	<aop:config>
        
        <!-- 声明切面 -->
    		<aop:aspect ref="myLogger">
          
          <!-- 声明切入点 -->
    			<aop:pointcut expression="execution(* com.atguigu.spring.aopxml.*.*(..))" id="cut"/>
    			
          <!-- 声明通知的两种方式 -->
          <!-- <aop:before method="before" pointcut="execution(* com.atguigu.spring.aopxml.*.*(..))"/> -->
    			<aop:before method="before" pointcut-ref="cut"/>
          
    		</aop:aspect>
    	</aop:config>
    
  • 相关阅读:
    struts1.2一些知识
    iText的用法
    <action里的input的作用
    CSS超链接样式常见四种状态控制
    Win7系统IIS7+Tomcat7集成
    Marquee 使用方法详解
    JAVA中String与StringBuffer的区别
    2020 8 31 每日总结
    2020 10 1
    2020 9 30
  • 原文地址:https://www.cnblogs.com/chaozhengtx/p/13792410.html
Copyright © 2011-2022 走看看