SpringAOP学习笔记
SpringAOP
实现底层就是对动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强即可。
1、Spring AOP相关概念
小编在这里主要涉及通过注解的方式实现AOP
。所以接下来我们现需要了解AOP
相关术语:
- Target(目标对象)
这里是要被增强的对象,一般而言就是对业务逻辑类
- Proxy(代理)
一个类被AOP织入增强后就会产生一个结果的代理。
- Aspect(切面)
表示增强的功能,就是一些代码完成的某个功能,非业务功能。
- Joinpoint(连接点)
连接点就是被拦截到的点。在spring
中这些点就是指的是方法(即一般类中的业务方法),因为spring
只支持方法类型的连接点。
- Pointcut(切入点)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
被标记为 final
的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
- Advice(通知/增强)
通知是指拦截到 Joinpoint
之后所要做的事情就是通知。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
- Weaving(织入)
指把增强应用到目标对象来创建新的代理对象的过程。 spring
采用动态代理织入,而 AspectJ
采用编译期织入和类装载期织入。
2、AspectJ对AOP的实现
对于AOP的编程思想,很多框架实现了其内容。当然spring就是其中之一可以完成面向切面编程。
AspectJ
也实现了AOP
的功能,且实现方式更为简捷而且还支持注解式开发。所以Spring
将AspectJ
对于AOP
实现也引入到自己的框架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式
2.1 AspectJ通知类型
AspectJ 中常用的通知有5种类型:
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
2.2 AspectJ的切入点表达式
AspectJ 定义了专门的表达式用于指定切入点。
表达式的原型如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
说明:
modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分
以上表达式主要分为四个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象**就是目标方法的方法名。**所以,execution
表达式就是方法的签名。
示例:
execution(* com.xaf.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xaf.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟“*”,表示包、子包下的所有类。
execution(* com.xaf.service.IUserService+.*(..))
指定切入点为:IUserService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
3、通过注解方式实现AOP
开发阶段:关注核心业务和AOP代码。
运行阶段:spring
框架会在运行的时候将核心业务和AOP
代码通过动态代理的方式编织在一起。
1、创建项目引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2、创建spring配置文件引入约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--
<bean id="teamService" class="com.xaf.service.TeamService"></bean>
-->
<!--包扫描-->
<context:component-scan base-package="com.xaf.service,com.xaf.aop"/>
<!--开始注解aop使用-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
3.创建核心业务
package com.xaf.service;
public interface IService {
void add(int id,String name);
boolean update(int num);
}
package com.xaf.service;
import org.springframework.stereotype.Service;
@Service("nbaService")
public class NBAService implements IService{
@Override
public void add(int id, String name) {
System.out.println("NBAService---add---");
}
@Override
public boolean update(int num) {
System.out.println("NBAService---update---");
if(num>666){
return true;
}
return false;
}
}
package com.xaf.service;
import org.springframework.stereotype.Service;
@Service
public class TeamService implements IService{
@Override
public void add(int id, String name) {
System.out.println("TeamService---add---");
}
@Override
public boolean update(int num) {
System.out.println("TeamService----update----");
if(num>666){
return true;
}
return false;
}
}
在包中创建核心业务。
4.定义切口类
package com.xaf.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/*
* 切面类
* */
@Component //切面对象创建权限依旧给spring容器
@Aspect //框架注解,当前是一个切面
public class MyAspect {
@Pointcut("execution(* com.xaf.service..*.*(..))")
private void pointCut(){
}
@Pointcut("execution(* com.xaf.service..*.add*(..))")
private void pointCut2(){
}
//前置的切面通知
@Before("pointCut()")
public void before(JoinPoint jp){
System.out.println("前置通知:在目标方法执行前被调用的通知");
String name = jp.getSignature().getName();
System.out.println("拦截的方法:"+name);
Object[] args = jp.getArgs();
System.out.println("方法参数格式"+args.length);
System.out.println("方法的参数列表");
for (Object arg:args){
System.out.println("\t"+arg);
}
}
@AfterReturning(value = "pointCut2()",returning = "result")
public Object afterReturn(Object result){
if(result!=null){
boolean res=(boolean) result;
if(res){
result=false;
}
}
System.out.println("后置通知:在目标方法执行之后被调用的通知,result="+result);
return result;
}
/**
* 注解声明环绕注解
* */
@Around(value = "pointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕方法---目标方法的执行之前");
Object proceed = pjp.proceed();
System.out.println("环绕方法---目标方法的执行之后");
return proceed;
}
/**
* AfterThrowing 注解声明异常通知方法
* value: 表示切入点表达式
* returning 属性表示 返回的结果,如果需要的话可以在后置通知的方法中修改结果
*/
@AfterThrowing(value = "pointCut()",throwing = "ex")
public void exception(JoinPoint jp,Throwable ex){
//一般会把异常发生的时间、位置、原有都记录下来
System.out.println("异常通知:在目标方法执行出现异常的时候才会别调用的通知,否则不 执行");
System.out.println(jp.getSignature()+"方法出现异常,异常信息 是:"+ex.getMessage());
}
/**
* After 注解声明为最终通知
*/
@After( "pointCut()")
public void myFinally(){
System.out.println("最终通知:无论是否出现异常都是最后被调用的通知");
}
}
详解可以关注注释信息。
5.application.xml
配置文件中开启包扫描和 注册aspectj
的自动代理
如上第2个步骤即可
<!--包扫描-->
<context:component-scan base-package="com.xaf.service,com.xaf.aop"/>
<!--开始注解aop使用-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--aop:aspectj-autoproxy的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的,是基于 AspectJ 的注解适配自动代理生成器。
其工作原理是,aop:aspectj-autoproxy通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。-->
比如我们在第4个步骤已经扫描到类上面的@AspectJ
,然后由切面类根据切入点找到目标方法。
6、测试类
public class Test01 {
@Test
public void test01(){
//1.加载springxml文件
ApplicationContext ac =new ClassPathXmlApplicationContext("application.xml");
TeamService teamService = (TeamService) ac.getBean("teamService");
teamService.add(1001,"湖人队");
System.out.println("---------------");
boolean update = teamService.update(888);
System.out.println("update 结果为:"+update);
System.out.println("================");
NBAService nbaService = (NBAService) ac.getBean("nbaService");
nbaService.add(1002,"热火");
System.out.println("~~~~~~~`~~~`~");
boolean update2 = teamService.update(888);
System.out.println("update结果为:"+update2);
}
}
最后的编译结果如下图:
以上就是AOP的练习和分享,感谢大家的阅读。本节基本的spring
讲解差不多,后续若有补充会继续进行探讨。谢谢大家。