AOP
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
AOP技术恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为“Aspect”,即切面。所谓“切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。
名词解释
1.横切关注点:影响应用多处的功能(安全、事务、日志)
2.切面(aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象。即切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和在何处完成其功能。
3.连接点(joinpoint):被拦截到的点,因为Spring只支持方法类型的连接点,其实就是方法和方法之间调用的过程。所以Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。也即在应用执行过程中能够插入切面的一个点。。
4.切入点(pointcut):对连接点进行拦截的定义,指定一个通知将被引发的一些列连接点的集合。即如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处”。因此,切点其实就是定义了需要执行在哪些连接点上执行通知。
5.通知(advice):切面的工作被称为通知,所谓通知指的就是指拦截到连接点之后要执行的代码,即切面是什么以及何时使用,通知分为前置、后置、异常、最终、环绕通知五类。
前置通知(Before):在目标方法被调用之前调用通知功能
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么
返回通知(After-returning):在目标方法成功执行之后调用通知
异常通知(After-throwing):在目标方法抛出异常后调用通知
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和之后执行自定义的行为
后置通知和返回通知的区别:后置通知是不管方法是否有异常,都会执行该通知;而返回通知是方法正常结束时才会执行。
6.目标对象:代理的目标对象
7.引入(Introduction):引入允许我们向现有的类添加新方法或属性。
8.织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期中有很多个点可以进行织入:
编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。 AspectJ 5的加载时织入就支持这种方式织入切面。
运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态的创建一个代理对象。Spring AOP就是以这种方式织入切面的。
注意:因为Spring基于动态代理,所以Spring只支持方法连接点。
1.导入依赖jar包
<!--springIoc的依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--AOP切面依赖包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
2.UserMapper.java
@Component
public class UserMapper {
public void saveUser(){
System.out.println("执行添加操作!");
}
}
3.Transcation.java
@Component
@Aspect//定义它是一个切面
public class Transcation {
@Pointcut("execution(* *.*.*(..))")
public void abc(){
}
@After("abc()") //任意返回类型 任意包中的任意类中的任意方法的任意参数 或者括号里面:execution(* *.*.*(..))
public void after(){
System.out.println("我负责提交事务!");
}
@Before("abc()")
public void before(){
System.out.println("我会在之前执行!");
}
@Around("abc()")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("我是环绕之前...");
pjp.proceed();
System.out.println("我是环绕之后...");
}
@AfterReturning("abc()")
public void afterReturn(){
System.out.println("最终会被执行的");
}
@AfterThrowing("abc()") //在方法抛出异常之后执行 如果try catch了,则不会执行
public void afterThrow(){
System.out.println("出异常了!");
}
}
4.applicationContext.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
<context:component-scan base-package="advice,target"/>
<!--AOP的自动扫描切面-->
<aop:aspectj-autoproxy/>
</beans>
5.TestUserMapper.java
public class TestUserMapper {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = applicationContext.getBean("userMapper", UserMapper.class);
userMapper.saveUser();
}
}