Spring实战学习笔记整理(4)-AOP(面向切面编程)
AOP与OOP概念
OOP(Object Oriented Programming,面向对象编程),通过封装、继承将程序抽象为各个层次的对象,进而组合为模块或者程序,达到了软件工程中的重用性、灵活性、扩展性。程序的运行笼统地可以看为各层次对象之间的相互调用。
AOP(Aspect Oriented Programming,面向切面编程),将程序运行过程分解为一个个的切面,对特定的切面(某个步骤或者阶段)进行提取,达到解耦各种不同逻辑代码。
OOP是在程序分块层面上进行考虑,而AOP则是在程序运行的切面上进行考虑。
AOP使用
xml的使用方式
1.使用步骤
(1)环境搭建,注意:导入aspectjweaver-1.9.5.jar
(2)创建业务类、切面类
(3)创建配置文件,并配置切面信息:以上涉及到的类必须纳入到Spring IOC容器管理
(4)测试
2.记忆的点
2.1切点的声明:标签、表达式
2.2通知:前置通知、后置通知、返回通知、异常通知、环绕通知(!joinPoint.proceed())
注解的使用方式
1.使用步骤
(1)环境搭建,注意:导入aspectjweaver-1.9.5.jar
(2)创建业务类、切面类(定义了切面所有的东西)
(3)开启包扫描、开启切面编程
(4)测试
2.记忆的点
2.1切点的声明:注解、表达式、声明在方法上
2.2通知:前置通知、后置通知、返回通知、异常通知、环绕通知(!joinPoint.proceed())
2.3切面类中@Aspect必须写
疫情足迹举例:
使用自我扫码进入形式和神奇设备负责记录形式两种形式,如图所示:
自我扫码进入形式
代码部分:
Person.java
/**
* Person类,用于演示疫情足迹登录
* @author Katrina
*
*/
public class Person {
/**
* 回家
*/
public void goHome() {
System.out.println("回家扫码登记...");
System.out.println("回家了");
System.out.println("离家扫码登记...");
}
/**
* 去商场购物生活必需品
*/
public void goMarket() {
System.out.println("去商场扫码登记...");
System.out.println("去商场购买生活必需品");
System.out.println("离开商场扫码登记...");
}
/**
* 去工作
*/
public void goWork() {
System.out.println("去工作扫码登记...");
System.out.println("去工作");
System.out.println("离开扫码登记...");
}
}
PersonTest.java
/**
* 测试类,用于演示疫情足迹记录
* @author Katrina
*
*/
import org.junit.Test;
public class PersonTest {
/**
* 测试非AOP疫情登记
*/
@Test
public void test1() {
Person person = new Person();
person.goHome();
person.goWork();
person.goMarket();
}
}
效果图:
神奇设备负责记录形式【xml形式配置AOP】
引入aspectjweaver-1.9.5.jar
jar包
代码部分:
part1:
AOPPerson.java
/**
* AOPPerson类,用于演示AOP
* @author Katrina
*
*/
public class AOPPerson {
/**
* 回家方法
*/
public void goHome() {
System.out.println("回家了");
}
/**
* 去商场方法
*/
public void goMarket(String markerName) {
System.out.println("去商场");
}
/**
* 去工作方法
*/
public void goWork() {
System.out.println("去工作");
}
}
SuperMachine.java
/**
* 超级机器,用于定义通知
* @author Katrina
*
*/
public class SuperMachine {
/**
* 进入前(方法执行前)进行登记信息
*/
public void beforeIn() {
System.out.println("进入前登记信息...");
}
/**
* 离开前(方法执行之后)进行登记信息
*/
public void beforeOut() {
System.out.println("离开之前登记信息...");
}
}
config.xml
<?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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 注入类 -->
<bean id="AOPPerson" class="demo.AOPPerson"></bean>
<bean id="superMachine" class="demo.SuperMachine"></bean>
<!-- AOP配置 -->
<aop:config>
<!-- 切面配置 -->
<aop:aspect ref="superMachine">
<!--
定义切点:
1.execution(执行):
2.表达式:execution(public * demo.AOPPerson.goHome(..) -> public void goHome() {
表达式就是方法的描述:【访问修饰符 返回值 方法的全路径(参数列表)】
通配符:*(全部)、..(0个或者多个)
-->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.goHome())" id="pointCut"/> -->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.*())" id="pointCut"/> -->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.*(..))" id="pointCut"/> -->
<aop:pointcut expression="execution(public * demo.AOPPerson.*(..))" id="pointCut"/>
<!-- 注意:必须需要ref关联 -->
<!-- 前置通知:执行方法之前执行 -->
<aop:before method="beforeIn" pointcut-ref="pointCut"/>
<!-- 后置通知:执行方法之后执行 -->
<aop:after method="beforeOut" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
AOPPersonTest.java
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* aop方式测试类
* @author Katrina
*
*/
public class AOPPersonTest {
private ClassPathXmlApplicationContext context;
@Before
public void before() {
context = new ClassPathXmlApplicationContext("config.xml");
}
/**
* aop的demo
*/
@Test
public void test1() {
AOPPerson person = context.getBean(AOPPerson.class);
person.goHome();
person.goMarket("万达超市");
person.goWork();
}
@After
public void after() {
context.close();
}
}
效果图:
part2:
SuperMachine.java
/**
* 超级机器,用于定义通知
* @author Katrina
*
*/
public class SuperMachine {
/**
* 进入前(方法执行前)进行登记信息
*/
public void beforeIn() {
System.out.println("进入前登记信息...");
}
/**
* 离开前(方法执行之后)进行登记信息
*/
public void beforeOut() {
System.out.println("离开之前登记信息...");
}
/**
* 返回通知
*/
public void returnMethod() {
System.out.println("返回通知执行了...");
}
}
config.xml
<?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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 注入类 -->
<bean id="AOPPerson" class="demo.AOPPerson"></bean>
<bean id="superMachine" class="demo.SuperMachine"></bean>
<!-- AOP配置 -->
<aop:config>
<!-- 切面配置 -->
<aop:aspect ref="superMachine">
<!--
定义切点:
1.execution(执行):
2.表达式:execution(public * demo.AOPPerson.goHome(..) -> public void goHome() {
表达式就是方法的描述:【访问修饰符 返回值 方法的全路径(参数列表)】
通配符:*(全部)、..(0个或者多个)
-->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.goHome())" id="pointCut"/> -->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.*())" id="pointCut"/> -->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.*(..))" id="pointCut"/> -->
<aop:pointcut expression="execution(public * demo.AOPPerson.*(..))" id="pointCut"/>
<!-- 注意:必须需要ref关联 -->
<!-- 前置通知:执行方法之前执行 -->
<aop:before method="beforeIn" pointcut-ref="pointCut"/>
<!-- 后置通知:执行方法之后执行 -->
<aop:after method="beforeOut" pointcut-ref="pointCut"/>
<!-- 返回通知:方法成功执行之后执行 -->
<aop:after-returning method="returnMethod" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
效果图:
不对test进行修改,效果如图所示:
其他情况,若修改回家方法(添加参数),且在赋值的时候有误,则效果如图所示:
part3:
SuperMachine.java
/**
* 超级机器,用于定义通知
* @author Katrina
*
*/
public class SuperMachine {
/**
* 进入前(方法执行前)进行登记信息
*/
public void beforeIn() {
System.out.println("进入前登记信息...");
}
/**
* 离开前(方法执行之后)进行登记信息
*/
public void beforeOut() {
System.out.println("离开之前登记信息...");
}
/**
* 返回通知
*/
public void returnMethod() {
System.out.println("返回通知执行了...");
}
/**
* 异常通知
*/
public void afterThrowing() {
System.out.println("出现异常了...");
}
}
config.xml
<?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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 注入类 -->
<bean id="AOPPerson" class="demo.AOPPerson"></bean>
<bean id="superMachine" class="demo.SuperMachine"></bean>
<!-- AOP配置 -->
<aop:config>
<!-- 切面配置 -->
<aop:aspect ref="superMachine">
<!--
定义切点:
1.execution(执行):
2.表达式:execution(public * demo.AOPPerson.goHome(..) -> public void goHome() {
表达式就是方法的描述:【访问修饰符 返回值 方法的全路径(参数列表)】
通配符:*(全部)、..(0个或者多个)
-->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.goHome())" id="pointCut"/> -->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.*())" id="pointCut"/> -->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.*(..))" id="pointCut"/> -->
<aop:pointcut expression="execution(public * demo.AOPPerson.*(..))" id="pointCut"/>
<!-- 注意:必须需要ref关联 -->
<!-- 前置通知:执行方法之前执行 -->
<aop:before method="beforeIn" pointcut-ref="pointCut"/>
<!-- 后置通知:执行方法之后执行 -->
<aop:after method="beforeOut" pointcut-ref="pointCut"/>
<!-- 返回通知:方法成功执行之后执行 -->
<aop:after-returning method="returnMethod" pointcut-ref="pointCut"/>
<!-- 异常通知:方法出现异常后执行 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
效果图:
情况一,在测试类中修改代码为:person.goHome("123abc");
,效果如图所示:
情况二,在测试类中修改代码为:person.goHome("123");
,效果如图所示:
part4:
SuperMachine.java
import org.aspectj.lang.ProceedingJoinPoint;
/**
* 超级机器,用于定义通知
* @author Katrina
*
*/
public class SuperMachine {
/**
* 环绕通知
* @param joinPoint,一定要调用proceed方法
*/
public void around(ProceedingJoinPoint joinPoint) {
System.out.println("进入前进行登记信息");
boolean hasError = false;
try {
joinPoint.proceed(); //执行业务方法
} catch (Throwable e) {
e.printStackTrace();
System.out.println("出现异常了...");
hasError = true;
}
if (hasError) {
System.out.println("出现异常信息");
} else {
System.out.println("成功返回值信息");
}
System.out.println("离开前进行登记信息");
}
/**
* 进入前(方法执行前)进行登记信息
*/
public void beforeIn() {
System.out.println("进入前登记信息...");
}
/**
* 离开前(方法执行之后)进行登记信息
*/
public void beforeOut() {
System.out.println("离开之前登记信息...");
}
/**
* 返回通知
*/
public void returnMethod() {
System.out.println("返回通知执行了...");
}
/**
* 异常通知
*/
public void afterThrowing() {
System.out.println("出现异常了...");
}
}
config.xml
<?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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 注入类 -->
<bean id="AOPPerson" class="demo.AOPPerson"></bean>
<bean id="superMachine" class="demo.SuperMachine"></bean>
<!-- AOP配置 -->
<aop:config>
<!-- 切面配置 -->
<aop:aspect ref="superMachine">
<!--
定义切点:
1.execution(执行):
2.表达式:execution(public * demo.AOPPerson.goHome(..) -> public void goHome() {
表达式就是方法的描述:【访问修饰符 返回值 方法的全路径(参数列表)】
通配符:*(全部)、..(0个或者多个)
-->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.goHome())" id="pointCut"/> -->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.*())" id="pointCut"/> -->
<!-- <aop:pointcut expression="execution(public void demo.AOPPerson.*(..))" id="pointCut"/> -->
<aop:pointcut expression="execution(public * demo.AOPPerson.*(..))" id="pointCut"/>
<!-- 注意:必须需要ref关联 -->
<!-- 前置通知:执行方法之前执行 -->
<!-- <aop:before method="beforeIn" pointcut-ref="pointCut"/> -->
<!-- 后置通知:执行方法之后执行 -->
<!-- <aop:after method="beforeOut" pointcut-ref="pointCut"/> -->
<!-- 返回通知:方法成功执行之后执行 -->
<!-- <aop:after-returning method="returnMethod" pointcut-ref="pointCut"/> -->
<!-- 异常通知:方法出现异常后执行 -->
<!-- <aop:after-throwing method="afterThrowing" pointcut-ref="pointCut"/> -->
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
效果图:
情况一,在测试类中修改代码为:person.goHome("123");
,效果如图所示:
情况二,在测试类中修改代码为:person.goHome("abc");
,效果如图所示:
注解形式配置AOP
代码部分:
part1:
UserControllor.java
import org.springframework.stereotype.Controller;
/**
* UserControllor,用于演示注解形式的切面编程
* @author Katrina
*
*/
@Controller
public class UserControllor {
/**
* 添加用户操作
*/
public void addUser() {
System.out.println("添加用户成功...");
}
/**
* 编辑用户操作
*/
public void editUser() {
System.out.println("编辑用户成功...");
}
}
LogAOP.java
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 日志的切面
* @author Katrina
*
*/
@Component
@Aspect //标明LogAOP是切面类
public class LogAOP {
/*
* 1.声明切点
* 2.声明通知
*/
@Pointcut(value = "execution(public * anno.UserControllor.*(..))")
public void pointCut() { }
/**
* 使用@Before声明前置通知
*/
@Before(value = "pointCut()")
public void log() {
System.out.println("在此处记录日志...");
}
/**
* 使用@After声明后置通知
*/
@After(value = "pointCut()")
public void after() {
System.out.println("后置通知执行了...");
}
}
anno-config.java
<?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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 开启包扫描 -->
<context:component-scan base-package="anno"></context:component-scan>
<!-- 开启AOP -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
UserControllorTest.java
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 用于演示注解形式使用AOP
* @author Katrina
*
*/
public class UserControllorTest {
@Test
public void test() {
//1.获取容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("anno-config.xml");
//2.取出实例,做操作
UserControllor userControllor = context.getBean(UserControllor.class);
userControllor.addUser();
//3.关闭容器
context.close();
}
}
效果图:
part2:
UserControllor.java
import org.springframework.stereotype.Controller;
/**
* UserControllor,用于演示注解形式的切面编程
* @author Katrina
*
*/
@Controller
public class UserControllor {
/**
* 添加用户操作
*/
public void addUser(String number) {
System.out.println("添加用户成功...");
Integer.parseInt(number); //解析,可制造错误(如:1/0)
}
/**
* 编辑用户操作
*/
public void editUser() {
System.out.println("编辑用户成功...");
}
}
LogAOP.java
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 日志的切面
* @author Katrina
*
*/
@Component
@Aspect //标明LogAOP是切面类
public class LogAOP {
/*
* 1.声明切点
* 2.声明通知
*/
@Pointcut(value = "execution(public * anno.UserControllor.*(..))")
public void pointCut() { }
/**
* 使用@Before声明前置通知
*/
@Before(value = "pointCut()")
public void log() {
System.out.println("在此处记录日志...");
}
/**
* 使用@After声明后置通知
*/
@After(value = "pointCut()")
public void after() {
System.out.println("后置通知执行了...");
}
/**
* 使用@AfterReturning声明返回通知
*/
@AfterReturning(value = "pointCut()")
public void afterReturning() {
System.out.println("方法成功执行了...");
}
}
效果图:
情况一,在测试类中修改代码为:person.goHome("111");
,效果如图所示:
情况二,在测试类中修改代码为:person.goHome("abc");
,效果如图所示:
part3:
LogAOP.java
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 日志的切面
* @author Katrina
*
*/
@Component
@Aspect //标明LogAOP是切面类
public class LogAOP {
/*
* 1.声明切点
* 2.声明通知
*/
@Pointcut(value = "execution(public * anno.UserControllor.*(..))")
public void pointCut() { }
/**
* 使用@Before声明前置通知
*/
@Before(value = "pointCut()")
public void log() {
System.out.println("在此处记录日志...");
}
/**
* 使用@After声明后置通知
*/
@After(value = "pointCut()")
public void after() {
System.out.println("后置通知执行了...");
}
/**
* 使用@AfterReturning声明返回通知
*/
@AfterReturning(value = "pointCut()")
public void afterReturning() {
System.out.println("方法成功执行了...");
}
/**
* 使用@AfterThrowing表明异常通知
*/
@AfterThrowing(value = "pointCut()")
public void afterThrowing() {
System.out.println("发生异常了...");
}
}
效果图:
情况一,在测试类中修改代码为:person.goHome("abc");
,效果如图所示:
情况二,在测试类中修改代码为:person.goHome("111");
,效果如图所示:
part4:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 日志的切面
* @author Katrina
*
*/
@Component
@Aspect //标明LogAOP是切面类
public class LogAOP {
/*
* 1.声明切点
* 2.声明通知
*/
@Pointcut(value = "execution(public * anno.UserControllor.*(..))")
public void pointCut() { }
/**
* 使用@Before声明前置通知
*/
// @Before(value = "pointCut()")
public void log() {
System.out.println("在此处记录日志...");
}
/**
* 使用@After声明后置通知
*/
// @After(value = "pointCut()")
public void after() {
System.out.println("后置通知执行了...");
}
/**
* 使用@AfterReturning声明返回通知
*/
// @AfterReturning(value = "pointCut()")
public void afterReturning() {
System.out.println("方法成功执行了...");
}
/**
* 使用@AfterThrowing表明异常通知
*/
// @AfterThrowing(value = "pointCut()")
public void afterThrowing() {
System.out.println("发生异常了...");
}
/**
* 使用@Around注解声明环绕通知
* @param joinPoint
*/
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint joinPoint) {
System.out.println("执行方法之前");
try {
joinPoint.proceed(); //执行业务方法
} catch (Throwable e) {
e.printStackTrace();
System.out.println("出现异常了...");
}
System.out.println("执行方法之后");
}
}
效果图:
情况一,在测试类中修改代码为:person.goHome("111");
,效果如图所示:
情况二,在测试类中修改代码为:person.goHome("abc");
,效果如图所示: