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("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); } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)