文章目录
AOP的概念
• Aspect Oriented Programing,即面向方面(切面)编程。
• AOP是一种编程思想,是对OOP(面向对象编程)的补充,可以进一步提高编程的效率。
AOP术语
连接点表示的并不是一个点,而是一段代码或一整个方法等
AOP的实现
AspectJ
- AspectJ是语言级的实现(一种新的语言),它扩展了Java语言,定义了AOP语法。
- AspectJ在编译期织入代码,它有一个专门的编译器,用来生成遵守Java字节码规范的class文件。
Spring AOP
- Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器。
- Spring AOP在运行时通过代理的方式织入代码,只支持方法类型的连接点。
- Spring支持对AspectJ的集成。(遇到解决不了得问题,再用AspectJ)
Spring AOP原理
代理:给某个对象生成一个新的对象来代替它,调用时仅调用代理对象,而不调用原始对象
Spring中的bean都是通过容器去调用的,当容器调用时发现目标对象上有AOP的作用,就会去找代理对象,调用代理对象,而代理对象上有织入的代码,能解决问题。
JDK动态代理
目标对象必须要有接口(要求),才能被代理。代理时,自动生成接口的实现类,代替原实现类
- Java提供的动态代理技术,可以在运行时创建接口的代理实例。
- Spring AOP默认采用此种方式,在接口的代理实例中织入代码。
CGLib动态代理
创建目标对象的子类,用子类代替原来的对象(java支持多态,故可用子类型来代替父类型)
- 采用底层的字节码技术,在运行时创建子类代理实例。
- 当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码。
SpringAOP代码编写规则
自定义切面
@Aspect:声明当前类是一个切面组件
@Component // 将切面组件声明为bean令容器管理它
@Aspect
public class AlphaAspect {
... ... }
自定义切点
@Pointcut:自定义切点,声明当前方法是切点。
自定义一个空方法,加上 @Pointcut 注解,在注解中声明要将切面织入到哪些bean哪些方法中,即使用哪些连接点
* 符号表示任意,在不同位置代表不同的数据
* 在开头方法返回值位置上,代表任意类型的返回值
com.nowcoder.community.service.*.*(…) : service包下的 任意类中 的 任意方法,(…) 表示方法的所有参数
execution(* com.nowcoder.community.service..(…)): 表示service包下所有 业务组件(即类)的所有方法(不限参数和返回值)都要处理,都要匹配
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
public void pointcut() {
}
自定义通知
自定义方法上加不同注解 表示在连接点不同位置织入当前代码,注解中需指定 针对哪些连接点植入该方法,即适用的链接点范围
@Before:连接点前植入代码
@After:连接点后植入代码
@AfterReturning:抛异常时植入代码
@Around:连接点前后都植入代码
@After("pointcut()")
public void after() {
System.out.println("after");
}
自定义的方法可以带上一个参数,代表连接点对象。通过连接点对象可以获取链接点的信息,如连接点在哪个类中,是哪个方法。或调用连接点中的代码执行
-
@Around标识的方法中加 ProceedingJoinPoint
@Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("around before"); // proceed() 调用目标组件的方法,用Object接收目标组件方法的返回值 Object obj = joinPoint.proceed(); System.out.println("around after"); return obj; }
-
其他注解 标识的方法中加 JoinPoint
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
// 得到织入目标类名
String className = joinPoint.getSignature().getDeclaringTypeName();
// 得到织入目标方法名
String functionName = joinPoint.getSignature().getName();
System.out.println(className+"."+functionName);
}
在通知中获取当前请求
目的:当前请求对象中包含很多信息,如用户ip地址,请求内容等,通过请求对象我们可以获取这些信息
用于通知的方法不可以随意添加Request对象作为参数
获取Request对象:
-
利用 Request的工具类RequestContextHolder,通过getRequestAttributes()方法可以获得RequestAttributes类型对象
-
ServletRequestAttributes 是 RequestAttributes 的子类,拥有更多的方法,故通常将返回类型强转为子类型
-
通过ServletRequestAttributes可以获得Request对象
@Before("pointcut()")
public void before() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
}
代码实例
@Component // 将切面组件声明为bean令容器管理它
@Aspect // 表示这是一个切面组件
public class AlphaAspect {
// SpringAOP仅支持方法类型连接点
@Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
public void pointcut() {
}
// 连接点前植入代码
@Before("pointcut()")
public void before() {
// 打印ip:用户ip地址要从request中获取
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String ip = request.getRemoteHost();
System.out.println(ip);
}
// 连接点后植入代码
@After("pointcut()")
public void after() {
System.out.println("after");
}
// 连接点有返回值以后植入代码
@AfterReturning("pointcut()")
public void afterRetuning() {
System.out.println("afterRetuning");
}
// 抛异常时植入代码
@AfterThrowing("pointcut()")
public void afterThrowing() {
System.out.println("afterThrowing");
}
// 连接点前后都植入代码
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before");
// proceed() 调用目标组件的方法,用Object接收目标组件方法的返回值
Object obj = joinPoint.proceed();
System.out.println("around after");
return obj;
}
}
一些选择题
B
A
D
D
图片转存中…(img-sP6qo5m5-1709223538149)]
B
[外链图片转存中…(img-zaELyrk5-1709223538150)]
A
[外链图片转存中…(img-44XalgTf-1709223538150)]
D
[外链图片转存中…(img-7ey2TUah-1709223538150)]
D
[外链图片转存中…(img-yevsttjk-1709223538151)]
A