3.4-AOP
优质
小牛编辑
137浏览
2023-12-01
在《手写简易的 Tomcat 服务器》的基础上,介绍 Spring 的实现原理。
1. Spring AOP 的使用方法
1.1 PointCut Expression
切面应用于哪些方法。
1.1.1 各种 designators(指示器) 的区别
通过什么方法去匹配。
需求 | 指示器 |
---|---|
匹配方法 | @execution |
匹配注解/TYPE | @target |
@args | |
匹配注解/TYPE | @within |
匹配注解/METHOD | @annotation |
匹配包/类型 | within |
匹配对象 | this |
bean | |
target | |
匹配参数 | args |
1.1.2 通配符
通配符 | 功能 |
---|---|
* | 匹配任意数量的字符。 |
+ | 匹配指定类及其子类。 |
.. | 匹配任意数量的子包或参数。 |
1.1.3 Operators(运算符)
运算符 | 功能 | ||
---|---|---|---|
&& | 与运算符。 | ||
\ | \ | 或运算符。 | |
! | 非运算符。 |
1.1.4 匹配包/类型(within)
@Pointcut("within(com.iecas.soundsystem.aop.service.ProductService)")
public void matchType(){}
@Pointcut("within(com.iecas.soundsystem.aop.service..*)")
public void matchPackage(){}
1.1.5 匹配注解
@Pointcut("@target(com.iecas.soundsystem.aop.anno.NeedSecured)")
public void matchAnnotationType(){}
@Pointcut("@annotation(com.iecas.soundsystem.aop.anno.NeedSecured)")
public void matchAnnotationMethod(){}
@Pointcut("@args(com.iecas.soundsystem.aop.anno.NeedSecured)")
public void matchAnnotationArgs(){}
1.1.6 匹配方法
@Pointcut("execution(* com.iecas.soundsystem.aop.service.ProductService.exDemo())")
public void matchMethod(){}
1.1.7 匹配参数
@Pointcut("args(Long))")
public void matchArgs(){}
1.1.8 匹配对象
@Pointcut("bean(*Service))")
public void matchBean(){}
@Pointcut("this(com.iecas.soundsystem.aop.service.ProductService))")
public void matchThis(){}
@Pointcut("target(com.iecas.soundsystem.aop.service.IService))")
public void matchTarget(){}
1.2 5种 Advice 以及参数和结果的绑定
注解 | 功能 |
---|---|
Before | 前置通知。 |
After(Finally) | 后置通知,方法执行完之后。 |
AfterReturning | 返回通知,方法执行之后。 |
AfterThrowing | 异常通知,抛出异常之后。 |
Around | 环绕通知 |
@Before("matchTarget()")
public void before(){
System.out.println("Before:匹配 ProductService 类里面的所有方法");
}
@After("matchTarget()")
public void after(){
System.out.println("After:匹配 ProductService 类里面的所有方法");
}
@AfterReturning(value = "matchTarget()",returning = "returnValue")
public void afterReturning(String returnValue){
System.out.println("AfterReturning:匹配 ProductService 类里面的所有方法");
System.out.println("返回值:"+returnValue);
}
@AfterThrowing(value = "matchTarget()")
public void afterThrowing(){
System.out.println("AfterThrowing:匹配 ProductService 类里面的所有方法");
}
2. Spring AOP 的实现原理解析
2.1 概述
2.1.2 织入的时机
- 编译时织入(AspectJ)
- 类加载时织入(AspectJ 5+)
- 运行时织入(Spirng AOP)
2.2 设计模式
2.2.1 代理模式
静态代理模式
动态代理模式
2.2.2 责任链模式
2.3 实现
2.3.1 基于 JDK 的实现
2.3.2 基于 Cglib 的实现
3. Spring AOP经典代码解读
3.2 安全校验 @PreAuthorize
url 被指定的用户访问。
1. 反射
1.1 原理
思考题1:有哪些方法可以在运行时生成一个 Java 类。 答案:
- 方法1 利用 Java 程序生成一段代码,用 ProcessBuilder 之类启动 javac 进程,指定上面的生成文件作为输入,进行编译。最后,利用类加载器,在运行时进行加载即可。
- 方法2 利用字节码操纵工具和类库生成字节码。
1.2 实现方式
1.2.1 获取 Class 对象的三种实现方式
- 调用对象的 getClass 方法
- 调用类的 class 属性
- 调用 forName(全限定类名)
1.3 应用
1.3.1 同名属性批量复制
copyProperties(Object source,Class<T> clazz){ //获取源对象的所有属性名称。 Field[] sourceFileds=source.getClass.getField(); for(f:sourceFields){ map.put(f.getName(),f.get(source)); } }
2. 动态代理
2.1 实现方式
2.1.1 jdk
- 定义接口。
public interface Hello { void sayHello(); }
- 被代理类实现接口,重写接口方法。
public class HelloImpl implements Hello { @Override public void sayHello() { System.out.println("Hello World"); } }
实现 InvocationHandler 接口,重写 invoke 方法。
public class MyInvocationHandler implements InvocationHandler { //传入被代理的目标对象。 private Object target; public MyInvocationHandler(Object target) { this.target = target; } // target 为被代理对象,args 包含类加载器信息和接口信息。 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before:"+method.getName()); Object result=method.invoke(target,args); System.out.println("After:"+method.getName()); return result; } }
- 在应用中使用 Proxy 静态类的 newProxyInstance 方法创建增强类,调用增强类的方法。
思考2:如果 interface 中定义了两个方法呢,如何对针对方法使用不同切面。 答案:被代理类的需要实现两个方法,hadler 中调用 Method 参数对 getName 方法区分不同方法。 思考3:使用 jdk 提供的代理机制,被代理类需要继承接口,对源代码侵入,有没有更加优雅的方法。答案:cglib。public class MyDynamicProxyTest { public static void main(String[] args) { HelloImpl hello=new HelloImpl(); //传入 handler 对象 MyInvocationHandler handler=new MyInvocationHandler(hello); Hello proxyHello=(Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(),handler); proxyHello.sayHello(); } }
public class MyInterceptor implements MethodInterceptor { public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // object 表示要拦截的对象。 // method 表示要增强的方法。 // args 表示参数列表。 // methodProxy 表示代理的方法。 //invokeSuper方法表示对被代理对象方法的调用。 System.out.println("Before:"+method.getName()); Object result=methodProxy.invokeSuper(object,args); System.out.println("After:"+method.getName()); return result; } }
public class MyInterceptorTest { public static void main(String[] args) { MyInterceptor interceptor=new MyInterceptor(); Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(HelloImpl.class); enhancer.setCallback(interceptor); HelloImpl hello=(HelloImpl) enhancer.create(); hello.sayHello(); } }