AOP的实现
AOP的介绍
AOP:面向切面编程
思想:在写程序的时候关注点是切面
目的:是弥补面向对象编程(OOP)的缺陷、或者称为对OOP的补充、实现了在原来功能的基础之上实现功能扩展
思想的实现方式:就是一种切入的方式(一般就是在程序运行过程中某个方法被调用时把要新增的功能插入到需要的位置去)
AOP思想的编程实现
1、 配置方式
1.1、service接口和类
public interface CountryService {
public void saveCountry();
}
public class CountryServiceImpl implements CountryService{
@Override
public void saveCountry() {
//这是原来的功能,目前的需求是要在此功能基础之上扩展新的功能,假设扩展到此功能之后
System.out.println("save......country.....");
}
}
public interface PeopleService {
public void savePeople();
}
public class PeopleServiceImpl implements PeopleService{
@Override
public void savePeople() {
//原功能,目前的需求是在此功能基础之上扩展新功能,假设在此功能之后扩展
System.out.println("save........people......");
}
}
1.2、扩展功能
//此类封装这一些要扩展的功能
public class NewClass {
//这就是其中一个扩展功能
public void newMethod(){
//此处省略一万行
System.out.println("new.......method.......");
}
public void newMethod2(){
//此处省略一万行
System.out.println("new.......method.......2");
}
public void newMethod3(){
//此处省略一万行
System.out.println("new.......method.......3");
}
}
1.3、配置
<!-- 配置对象 -->
<bean id="csi" class="com.offcn.service.CountryServiceImpl"></bean>
<bean id="psi" class="com.offcn.service.PeopleServiceImpl"></bean>
<!-- 新增功能方法类的对象配置 -->
<bean id="nc" class="com.offcn.aop.NewClass"></bean>
<!-- 切入配置 -->
<aop:config>
<!-- 关注点 -->
<aop:aspect id="aspect1" ref="nc">
<!-- 切点 -->
<!-- 动作,用来指定要在什么方法上切入(插入功能) -->
<aop:pointcut expression="execution(* com.offcn.service.CountryServiceImpl.saveCountry(..))" id="pc1"/>
<aop:pointcut expression="execution(* com.offcn.service.PeopleServiceImpl.savePeople(..))" id="pc2"/>
<!-- 增强,指定扩展功能是什么,以及在什么位置扩展 -->
<aop:after-returning method="newMethod2" pointcut-ref="pc1"/>
<aop:after-returning method="newMethod" pointcut-ref="pc2"/>
</aop:aspect>
</aop:config>
1.4、测试
@Test
public void cutTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
CountryService cs = (CountryService) context.getBean("csi");
cs.saveCountry();
//假设一组程序执行
PeopleService ps = (PeopleService) context.getBean("psi");
ps.savePeople();
}
2、注解方式
2.1、接口和实现类
public interface CountryService {
public void saveCountry();
}
@Service("csi")
public class CountryServiceImpl implements CountryService{
@Override
public void saveCountry() {
//这是原来的功能,目前的需求是要在此功能基础之上扩展新的功能,假设扩展到此功能之后
System.out.println("save......country.....");
//int i = 10 / 0;
}
}
2.2、切面类
//此类封装这一些要扩展的功能
@Component
@Aspect ///配置一个切面
public class NewClass {
//这就是其中一个扩展功能
@AfterReturning("execution(* com.offcn.service.*.*(..))")
public void newMethod1(){
//此处省略一万行
System.out.println("new.......method.......after....后");
}
@Before("execution(* com.offcn.service.*.*(..))")
public void newMethod2(){
//此处省略一万行
System.out.println("new.......method.......before....前");
}
@Around("execution(* com.offcn.service.*.*(..))")
public void newMethod3(ProceedingJoinPoint joinPoint) throws Throwable{
//此处省略一万行
System.out.println("new.......method.......round.....绕");
//原始功能在执行一次,获取到原来的对象和方法
joinPoint.proceed();
System.out.println("new.......method.......round.....绕");
}
@AfterThrowing("execution(* com.offcn.service.*.*(..))")
public void newMethod4(){
//此处省略一万行
System.out.println("new.......method.......exception....异常");
}
@After("execution(* com.offcn.service.*.*(..))")
public void newMethod5(){
//此处省略一万行
System.out.println("new.......method.......finally....最终");
}
}
2.3、配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 扫描 -->
<context:component-scan base-package="com.offcn"></context:component-scan>
<!-- 自动代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
2.4、测试
@Test
public void cutTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
CountryService cs = (CountryService) context.getBean("csi");
cs.saveCountry();
}
3、相关知识点
(1)切面:是多个类的关注点的模块化,模块主要包括:pointcut切点、增强(换个方式来解释:由切点、增强组成的多个类的模块化结构)。
(2)切点/切入点:是一个谓词,是一种动作,切入(插入)动作,同时还要确定动作作用到什么位置,使用函数+表达式来确定动作作用到的位置,和增强结合起来能够具体到一个方法的前后等什么位置切入。
A、函数:execution()
B、表达式:初步确定要在什么位置切入,表达式的格式:表达式的格式不论如何变化,最终应该确定的是某个、某些方法,在表达式中允许使用一些符号(* .)* com.offcn.service.CountryServiceImpl.saveCountry(..)
表达式格式中*表示方法的任意的返回值类型,小括号中的…表示任意个任意类型的形参
(3)连接点:程序执行过程中一个点;通常spring的aop中,连接点是指一个方法(通常是那个要被切入的方法)被执行时。
(4)增强/通知:在连接点上采取的操作,实际上就是扩展的功能段(通常就是一个被封装起来的方法),包含后通知、前通知、环绕通知、异常后通知、最终通知,具体到在被切入的方法的什么位置实现扩展。
A、后通知:在被切入的方法之后切入(在被切入方法执行之后才插入执行增强功能),如果被切入方法发生异常,那么后通知不会被执行到。
B、前通知:在被切入的方法之前切入(在被切入方法执行之前就插入就执行)。
C、环绕通知:在被切入的方法之前和之后分别执行一次(在被切入的方法执行之前执行一次增强,在之后再执行一次增强),如果被切入方法有异常,那么之后的一次不会执行。
D、异常后通知:当被切入的方法发生异常的时候才切入(只有在被切入方法发生异常时增强才会执行),被切入方法不发生异常则不切入、不执行。
E、最终通知:在被切入方法执行之后插入,不论被切入方法是否发生异常,最终通知都会被切入、被执行。