Spring学习之AOP(面向切面编程)

动态代理

    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务

    动态代理好比如影星和经纪人,实际演戏的是影星,经纪人为影星处理好拍戏前后的事情.实际演戏的是影星.影公司找影星拍戏前需要先找到经纪人,但最终拍戏还是得找影星执行这个动作. 

 public interface Dinner {
        //吃晚饭的方法
        public void haveDinner();
}
//委托类
public class MyDinner implements Dinner{
         @Override
         public void haveDinner() {
         System.out.println("妈妈做的晚饭真好吃....");
         }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//代理类
public class MyDinnerProxy implements InvocationHandler {
         private Object originalObject; //被代理的原始对象
 
         //绑定被代理对象,返回一个代理对象
         public Object getProxy(Object obj)
         {
              this.originalObject = obj;
          //返回一个代理对象
              return Proxy.newProxyInstance(obj.getClass().getClassLoader(), 
                  obj.getClass().getInterfaces(), this);
         } 
         @Override
         public Object invoke(Object proxy, Method method, Object[] args)
           throws Throwable {
              Object result = null;
              System.out.println("吃饭之前洗手保持个人卫生...");
              result = method.invoke(this.originalObject, args);
              System.out.println("吃饭之后洗碗保持厨房卫生....");
              return result;
         } 
}
//测试类
public class MyDinnerProxyDemo {
     public static void main(String[] args) {
          Dinner din = new MyDinner();
          //不是使用代理对象的效果
          //din.haveDinner();
          MyDinnerProxy proxy = new MyDinnerProxy();
          //返回了一个代理对象
          din = (Dinner)proxy.getProxy(din);
          //执行代理对象的方法
          din.haveDinner();
     }
}

AOP简介 

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

AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.

在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.

AOP 的好处:

–每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级

–业务模块更简洁, 只包含核心业务代码.

 

 

切面(Aspect):  横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象

通知(Advice):  切面必须要完成的工作

目标(Target): 被通知的对象

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

连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置

切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。  wKioL1ZXxsjCBUUQAACe8RLetc8184.png 

切面就是一个带有 @Aspect 注解的 Java 类.

切面需要在IOC容器中,@Component

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

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

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

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

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

–@AfterThrowing: 异常通知, 在方法抛出异常之后

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

 可以用@Order(1)指定不同切面的优先级(也就是执行顺序),数字越小优先级越高

 在 AspectJ切面中, 可以通过 @Pointcut注解将一个切入点声明成简单的方法. 切入点的方法体通常是空的, 因为将切入点定义与应用程序逻辑混在一起是不合理的

wKiom1ZYCP6yIc1VAAElhgpGawU540.png

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
 * AOP 的 helloWorld
 * 1. 加入 jar 包
 * 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-aspects-4.0.0.RELEASE.jar
 * 
 * 2. 在 Spring 的配置文件中加入 aop 的命名空间。 
 * 
 * 3. 基于注解的方式来使用 AOP
 * 3.1 在配置文件中配置自动扫描的包: <context:component-scan base-package="com.atguigu.spring.aop"></context:component-scan>
 * 3.2 加入使 AspjectJ 注解起作用的配置: <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 使@Before和@After起作用
 * 为匹配的类自动生成动态代理对象. 
 * 
 * 4. 编写切面类: 
 * 4.1 一个一般的 Java 类
 * 4.2 在其中添加要额外实现的功能. 
 *
 * 5. 配置切面
 * 5.1 切面必须是 IOC 中的 bean: 添加 @Component 注解
 * 5.2 声明是一个切面: 添加 @Aspect
 * 5.3 配置切面优先级@Order(1)
 * 5.3.1 给方法配置通知注解.
前置通知: @Before("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(int, int))")

 * 6. 在通知中访问连接细节: 可以在通知方法中添加 JoinPoint 类型的参数, 从中可以访问到目标方法的签名和方法的参数. 
 * 
 */
//通过添加 @Aspect 注解声明一个 bean 是一个切面!
@Order(2)
@Aspect
@Component
public class LoggingAspect {
/**
  * 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码. 
  * 使用 @Pointcut 来声明切入点表达式. 
  * 后面的其他通知直接使用方法名来引用当前的切入点表达式. 
  */
 @Pointcut("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
 public void declareJointPointExpression(){}
    //声明是一个前置通知,在目标方法之前执行
 @Before("declareJointPointExpression()")
 public void beforeMethod(JoinPoint joinPoint){
  String methodName = joinPoint.getSignature().getName();
  Object [] args = joinPoint.getArgs();
  
  System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
 }
//声明是一个后置通知,在目标方法执行后(发生异常后)执行,还不能访问目标方法的执行结果
 
 @After("execution(* com.atguigu.spring.aop.*.*(..))")
 public void afterMethod(JoinPoint joinPoint){
  String methodName = joinPoint.getSignature().getName();
  System.out.println("The method " + methodName + " ends");
 }
 /**
  * 在方法法正常结束受执行的代码
  * 返回通知是可以访问到方法的返回值的!
  */
 @AfterReturning(value="declareJointPointExpression()",
   returning="result")
 public void afterReturning(JoinPoint joinPoint, Object result){
  String methodName = joinPoint.getSignature().getName();
  System.out.println("The method " + methodName + " ends with " + result);
 }
 
 /**
  * 在目标方法出现异常时会执行的代码.
  * 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
  */
 @AfterThrowing(value="declareJointPointExpression()",
   throwing="e")
 public void afterThrowing(JoinPoint joinPoint, Exception e){
  String methodName = joinPoint.getSignature().getName();
  System.out.println("The method " + methodName + " occurs excetion:" + e);
 }
 
 /**
  * 环绕通知需要携带 ProceedingJoinPoint 类型的参数. 
  * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
  * 且环绕通知必须有返回值, 返回值即为目标方法的返回值
  */
 /*
 @Around("execution(public int com.atguigu.spring.aop.ArithmeticCalculator.*(..))")
 public Object aroundMethod(ProceedingJoinPoint pjd){
  
  Object result = null;
  String methodName = pjd.getSignature().getName();
  
  try {
   //前置通知
   System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
   //执行目标方法
   result = pjd.proceed();
   //返回通知
   System.out.println("The method " + methodName + " ends with " + result);
  } catch (Throwable e) {
   //异常通知
   System.out.println("The method " + methodName + " occurs exception:" + e);
   throw new RuntimeException(e);
  }
  //后置通知
  System.out.println("The method " + methodName + " ends");
  
  return result;
 }
}
//另一个切面
package com.atguigu.spring.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1)
@Aspect
@Component
public class VlidationAspect {
//重用另一个包的一个类里的切入点表达式
 @Before("com.atguigu.spring.aop.LoggingAspect.declareJointPointExpression()")
 public void validateArgs(JoinPoint joinPoint){
  System.out.println("-->validate:" + Arrays.asList(joinPoint.getArgs()));
 }
 
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章