spring5:Aop思想和注解Aop

一.Aop是什么?

1.Aop

  • 面向切面编程
  • Oop(面向对象编程)

2.举例说明

  • 加入日志功能:记录输入输出
  • 即在核心功能头尾加入非核心功能
  • 举例:在核心功能处加入非核心功能
public class CalculatorLogImpl implements Calculator {
@Override
public int add(int i, int j) {
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
int result = i + j;
System.out.println("方法内部 result = " + result);
System.out.println("[日志] add 方法结束了,结果是:" + result);
return result;
}

3.解决方案:可用代理模式

①代理模式简介:

  • 将不属于目标方法核心逻辑的代码从目标中抽取出来(解耦)
  • 创建一个代理类来让代理类间接访问目标方法并可完成非核心代码的功能

②静态代理(写死的代码不具备灵活性)

  • 静态代理重点:
    要实现目标对象同样实现的抽象方法
    重写抽象方法并且加入附加功能调用目标对象的该方法
  • 举例
public class CalculatorStaticProxy implements Calculator {
// 将被代理的目标对象声明为成员变量
private Calculator target;
public CalculatorStaticProxy(Calculator target) {
this.target = target;
}
@Override
public int add(int i, int j) {
// 附加功能由代理类中的代理方法来实现
System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
// 通过目标对象来实现核心业务逻辑
int addResult = target.add(i, j);
System.out.println("[日志] add 方法结束了,结果是:" + addResult);
return addResult;
}
}

③动态代理(具备灵活性)

  • 动态代理注意点
    获得类加载器加载类(代理类需要被加载)
    获得目标对象实现的接口(代理类需要实现相同接口)
    获得重写抽象方法的方式(方法名,参数)
  • 举例
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxy(){
/**
* newProxyInstance():创建一个代理实例
* 其中有三个参数:
* 1、classLoader:加载动态生成的代理类的类加载器
* 2、interfaces:目标对象实现的所有接口的class对象所组成的数组
* 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接
口中的抽象方法
*/
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/**
* proxy:代理对象
* method:代理对象需要实现的方法,即其中需要重写的方法
* args:method所对应方法的参数
*/
Object result = null;
try {
System.out.println("[动态代理][日志] "+method.getName()+",参
数:"+ Arrays.toString(args));
result = method.invoke(target, args);
System.out.println("[动态代理][日志] "+method.getName()+",结
果:"+ result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[动态代理][日志] "+method.getName()+",异
常:"+e.getMessage());
} finally {
System.out.println("[动态代理][日志] "+method.getName()+",方法
执行完毕");
}
return result;
}
};
return Proxy.newProxyInstance(classLoader, interfaces,
invocationHandler);
}
}
@Test
public void testDynamicProxy(){
ProxyFactory factory = new ProxyFactory(new CalculatorLogImpl());
Calculator proxy = (Calculator) factory.getProxy();
proxy.div(1,0);
//proxy.div(1,1);
}
  • 动态代理的两种方式
    jdk动态代理:动态代理实现接口
    cglib动态代理:动态代理继承目标类

二.注解Aop

1.概念

  • 横切关注点:对于目标对象来说的非核心业务(日志功能)
  • 通知:横切关注点封装到切面中的方法叫通知
  • 切面:封装通知方法的类
  • 目标:被代理的对象
  • 代理:创建的代理对象
  • 切入点:从代码层面定位到需要连接点的方式

2.两个过程

  • 抽取:抽取非核心代码即横切关注点到切面中形成通知
  • 套:将通知加入到切入点中。

3.Aop思想过程

①整体框架

在这里插入图片描述

②通知方法的声明

  • 前置通知:使用@Before注解标识,在被代理的目标方法前执行
  • 返回通知:使用@AfterReturning注解标识,在被代理的目标方法成功结束后执行(寿终正寝)
  • 异常通知:使用@AfterThrowing注解标识,在被代理的目标方法异常结束后执行(死于非命)
  • 后置通知:使用@After注解标识,在被代理的目标方法最终结束后执行(盖棺定论)
  • 环绕通知:使用@Around注解标识,使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

③切入点的参数

  • 返回值+方法名的具体位置
    • public int com.atguigu.aop.annotation.CalculatorImpl.*(…)
      表示返回值int具体位置为.CalculatorImpl下载全部方法参数不限制
    • (* com.atguigu.aop.annotation.CalculatorImpl.*(…)
      表示返回值不限具体位置为.CalculatorImpl下载全部方法参数不限制
  • 切入点表达式的重用
@Pointcut("execution(* com.atguigu.aop.annotation.*.*(..))")
public void pointCut(){}
  • 同一切面中使用
@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
  • 不同切面中使用
@Before("com.atguigu.aop.CommonPointCut.pointCut()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}

④方法的参数

  • 获取方法的签名即方法的名称参数等
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
//获取连接点的签名信息
String methodName = joinPoint.getSignature().getName();
//获取目标方法到的实参信息
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参数:"+args);
}
  • 获取方法的返回值
@AfterReturning(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*
(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",结果:"+result);
}
  • 获取目标方法的异常
@AfterThrowing(value = "execution(* com.atguigu.aop.annotation.CalculatorImpl.*
(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}

5.整体举例

// @Aspect表示这个类是一个切面类
@Aspect
// @Component注解保证这个切面类能够放入IOC容器
@Component
public class LogAspect {
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*
(..))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println("Logger-->前置通知,方法名:"+methodName+",参
数:"+args);
}
@After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->后置通知,方法名:"+methodName);
}
@AfterReturning(value = "execution(*
com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->返回通知,方法名:"+methodName+",结
果:"+result);
}
@AfterThrowing(value = "execution(*
com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
String methodName = joinPoint.getSignature().getName();
System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}
@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
Object result = null;
try {
System.out.println("环绕通知-->目标对象方法执行之前");
//目标对象(连接点)方法的执行
result = joinPoint.proceed();
System.out.println("环绕通知-->目标对象方法返回值之后");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("环绕通知-->目标对象方法出现异常时");
} finally {
System.out.println("环绕通知-->目标对象方法执行完毕");
}
return result;
}
}

        ApplicationContext ioc = new ClassPathXmlApplicationContext("Spring-AOP.xml");
        Calculator calculator = ioc.getBean(Calculator.class);
        int add = calculator.add(10, 1);
        System.out.println(add);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值