AOP spring面向切面编程

AOP
1, 什么是AOP?
AOP = Aspect Oriented Programming 面向切面的编程
(aop是对oop的不足做了补充, oop的基本单元是类, aop的基本单元是切面)
Aspect 切面
概念: AOP通过预编译方式和运行期动态代理实现,在不修改源代码的情况下,给程序动态统一添加功能的一种技术.
2, 在springboot里实现AOP?
在这里插入图片描述

提出3个问题
(1), 谁来拦截?
(2), 拦截谁? 怎么拦?
(3), 拦截后干什么?

2.1 谁来拦截?
切面就是用来拦截的类, 所以我们需要创建一个切面
@Component //组件
@Aspect //切面
public class Qiemian {
}
创建一个类, 在类上面加入注解@Component,把此类交给spring来管理, 加入注解@Aspect将此类变为切面类.
2.2 拦截谁? 怎么拦?

@Pointcut(“execution(void com.turing.manage.service.impl.LuBianTanServiceImpl.maiKaochuan(Integer))”)
public void anyMethod(){}
用注解@Pointcut和execution来编写拦截的表达式
在execution的()里,写上你要拦截的方法的全路径,包括参数类型和返回类型
2.3 拦截后干什么
@Around(“anyMethod()”)
public void lanjie(){
System.out.println("=拦截====");
}
@Around 环绕通知
3, @Around标准写法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

环绕通知标准写法

4, 切入点表达式

  • 任意字符
    … 忽略个数,任意字符

拦截 com.turing.manage.service.impl.LuBianTanServiceImpl.maiRoujiamo方法,参数必须是Integer类型, 返回int类型
execution(int com.turing.manage.service.impl.LuBianTanServiceImpl.maiRoujiamo(Integer))

拦截LuBianTanServiceImpl类中的所有方法
execution(int com.turing.manage.service.impl.LuBianTanServiceImpl.*(Integer))

拦截impl包内的所有类中的所有方法

execution(int com.turing.manage.service.impl.*.*(Integer))

拦截com.turing包内所有包中的service.impl包中的所有类中的所有方法,返回任何类型.

execution(* com.turing.*.service.impl.*.*(Integer))

参数不计(任意字符,不计个数) 包中间的…表示前面是com.turing后面是service.impl中间随意.
所以 com.turing.a.b.c.service.impl也是被拦截的路径
所以 com.turing.manage.service.impl也是被拦截的路径
所以 com.turing.service.impl也是被拦截的路径

execution(* com.turing..service.impl.*.*(..))

如果业务里只有一个切入点和一个通知方式, 可以将代码写到一起, 例如:

@Around("execution(* com.turing..service.impl.*.*(..))")

5, 获取目标类,目标方法和参数
String className = pj.getTarget().getClass().getName(); //获取目标类
String methodName = pj.getSignature().getName(); //获取目标方法
Object[] args = pj.getArgs(); //获取参数
如果对参数做了修改, 则在执行连接点时,必须传参.
args[0] = 10;
result = pj.proceed(args);
6, 通知方式
环绕通知 @Around 包围一个连接点, 在连接点执行前执行后可以添加用户的自定义行为
前置通知 @Before 连接点执行前的通知方式
后置通知 @AfterReturning 连接点 正常 执行后的通知方式
异常通知 @AfterThrowing 连接点发生异常时的通知方式
最终通知 @After 连接点退出时的通知方式 (正常或者异常都能退出)
最终通知又叫finally通知,表示一定会执行的通知方式
在这里插入图片描述

执行顺序
环绕通知 – 前置通知 – 最终通知 – 后置通知
在这里插入图片描述

7, 环绕通知和其他4种通知方式的区别?
(1),环绕通知的参数是ProceedingJoinPoint,其他4种通知方式的参数是JoinPoint
(2),就因为参数的不同, 环绕通知可以控制连接点是否执行,其他4种通知方式不能控制
(3),环绕通知可以在连接点执行前,执行后,添加自定义的行为.
8, 最终通知和后置通知的区别?
后置通知是连接点正常执行后的通知方式,而最终通知是连接点退出时的通知方式
当连接点发生异常时, 执行异常通知而不会执行后置通知
但是不管有没有异常一定会执行最终通知.
9, 代理机制
Spring使用JDK动态代理或者CGLIB创建代理对象.
JDK动态代理, 需要目标对象实现了至少一个接口,如果目标对象没有实现接口则创建的是CGLIB代理对象.
如果强制使用CGLIB则会有以下缺点
(1), final方法不能被通知, 因为CGLIB采用基于子类的方法创建代理, 父类中的final方法不能被重写.
(2), 如果是spring 4.0以下的版本, 则代理对象的构造方法会被执行两次, 4.0以上的版本(包括4.0版本),代理对象构造函数不会执行两次.
如果想强制使用CGLIB代理,映入如下配置
proxy-target-class=“true”

在springboot中
spring.aop.auto=true #开启AOP,相当于使用注解@EnableAspectJAutoProxy
spring.aop.proxy-target-class=true #强制使用CGLIB代理

通过调试模式能看到目标类创建的是CGLIB代理对象.
在这里插入图片描述

10, Spring两大核心
IOC 控制反转
AOP 面向切面的编程
IOC实现的是组件之间的松耦合
AOP 实现的是功能代码和业务代码的完全解耦

11.代码

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 切面类,谁来拦截
 */
@Component //组件
@Aspect //切面
public class Qiemian {
    /**
     * 切入点,拦截谁,怎么拦截?
     */
    @Pointcut("execution(* com.turing.manage.service.impl.*.*(..))")
    public void anymethod(){}

    /**
     * 拦截后干什么?
     * 通知方式,当发生拦截时,通知我,我好执行一个方法做拦截后干什么
     * @Around 环绕通知
     * 这段要背下来可以默写下来
     */
    @Around("anymethod()")
    public Object lanjie(ProceedingJoinPoint pj){
        System.out.println("================拦截后进入环绕通知===============");
        String className = pj.getTarget().getClass().getName();
        System.out.println("className = " + className);
        String methodName = pj.getSignature().getName();
        System.out.println("methodName = " + methodName);
        Object[] args = pj.getArgs();
        System.out.println("args = " + args);
        args[0] = 10;
        Object result = null;
        try {
            //执行被拦截的方法 = 执行连接点
            System.out.println("前置");
            result = pj.proceed(args);
            System.out.println("后置");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("异常");
        } finally {
            System.out.println("最终");
        }
        return result;
    }

    @Before("anymethod()")
    public void qianzhi(JoinPoint joinPoint){
        System.out.println("前置通知");
    }
    @AfterReturning("anymethod()")
    public void houzhi(JoinPoint joinPoint){
        System.out.println("后置通知");
    }
    @AfterThrowing("anymethod()")
    public void yichang(JoinPoint joinPoint){
        System.out.println("异常通知");
    }
    @After("anymethod()")
    public void zuizhong(JoinPoint joinPoint){
        System.out.println("最终通知");
    }

}

import com.turing.manage.service.ILuBianTanService;
import org.springframework.stereotype.Component;

@Component("lbt")
public class LuBianTanServiceImpl implements ILuBianTanService {

	@Override
	public void maiLengMian(Integer money, Integer danqian, Integer changqian) {
		System.out.println("冷面:"+money+"元,加蛋:"+(money+danqian)+",加肠:"+(money+danqian+changqian));
		
	}

	@Override
	public void maiKaochuan(Integer money) {
		System.out.println("烤串:"+money);
	}

	@Override
	public void maiShuiGuo(Integer money) {
		System.out.println("水果:"+money);
	}

	@Override
	public int maiRoujiamo(Integer money) {
		System.out.println("肉夹馍:"+money);
		return money;
	}

}
import com.turing.manage.service.impl.LuBianTanServiceImpl;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

@SpringBootApplication
public class StudyaopApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(StudyaopApplication.class);
        LuBianTanServiceImpl lbt = (LuBianTanServiceImpl) context.getBean("lbt");
        lbt.maiLengMian(5,1,1);
//        lbt.maiKaochuan(8);
//        lbt.maiShuiGuo(20);
//        lbt.maiRoujiamo(7);
    }

}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值