spring–基础–06–Spring框架的AOP
代码地址
https://gitee.com/DanShenGuiZu/learnDemo.git
1、介绍
面向切面编程 当执行一个方法时,可以在方法执行之前或之后添加额外的功能。
2、AOP术语
@Aspec:声明这个类是切面 Joinpoint(连接点):
程序能够应用通知的一个"时机",这些"时机"就是连接点,例如方法被调用时、异常被抛出时等等。 可以理解为被aop拦截的类或者方法就是连接点。 Advice(通知):这是实际行动之前或之后执行的方法。 Pointcut(切点):这是一组一个或多个切点,也就是触发切面的拦截条件 Introduction:引用允许你添加新方法或属性到现有的类中。 Target object:一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。 Weaving:把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。 AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,Target Object实现了接口时,则采用JDK动态代理;反之,采用CGLIB代理。 织入(Weaving)把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才能做到,例如AspectJ的织入编译器; 类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码; 运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理是使用了JDK的动态代理
3、通知的类型
@Before前置通知(Beforeadvice)
在某连接点(JoinPoint)——核心代码(类或者方法)之前执行的通知 这个通知不能阻止连接点前的执行。
@Before注解的方法入参不能传ProceedingJoinPoint,而只能传入JoinPoint。要知道从aop走到核心代码就是通过调用ProceedingJionPoint的proceed()方法。而JoinPoint没有这个方法。 Proceedingjoinpoint继承了JoinPoint。是在JoinPoint的基础上暴露出proceed这个方法。 proceed很重要,这个是aop代理链执行的方法。暴露出这个方法,就能支持aop:around这种切面(而其他的几种切面只需要用到JoinPoint,这跟切面类型有关),能决定是否走代理链还是走自己拦截的其他逻辑。 建议看一下JdkDynamicAopProxy的invoke方法,了解一下代理链的执行原理。这样你就能明白proceed方法的重要性。 @After后通知(Afteradvice):
当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。 @AfterReturning返回后通知(Afterreturnadvice):
在某连接点正常完成后执行的通知,不包括抛出异常的情况。 @Around环绕通知(Aroundadvice):
方法执行前或者执行后的通知 这个注解的方法入参传的是ProceedingJionPointpjp,可以决定当前线程能否进入核心方法中。
通过调用pjp.proceed() @AfterThrowing抛出异常后通知(Afterthrowingadvice):
在方法抛出异常退出时执行的通知。
4、advice(通知)注解的执行先后顺序
4.1、测试配置
4.2、测试正常数据
4.3、测试异常数据
5、实现方式
基于配置的XML来实现 基于@Aspect来实现
5.1、基于配置的XML来实现
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<bean id="HelloAOP1" class="com.example.demolearn.other.spring.demo4.HelloAOP1">
</bean>
<!-- 切面 -->
<bean id="HelloAOP1_ASPECT" class="com.example.demolearn.other.spring.demo4.HelloAOP1_ASPECT"/>
<!-- 切面规则 -->
<aop:config>
<aop:aspect id="aspect1" ref="HelloAOP1_ASPECT">
<!-- 切点 -->
<aop:pointcut id="pointcut1"
expression="execution(* com.example.demolearn.other.spring.demo4.*.*(..))"/>
<aop:before pointcut-ref="pointcut1" method="beforeAdvice"/>
<aop:after pointcut-ref="pointcut1" method="afterAdvice"/>
<aop:after-returning pointcut-ref="pointcut1"
returning="retVal"
method="afterReturningAdvice"/>
<aop:after-throwing pointcut-ref="pointcut1"
throwing="ex"
method="AfterThrowingAdvice"/>
</aop:aspect>
</aop:config>
</beans>
public class HelloAOP1 {
private Integer age;
public void setAge(Integer age){
this.age = age;
}
public Integer getAge(){
System.out.println("Age : " + age);
return age;
}
public void printThrowException(){
throw new IllegalArgumentException();
}
}
//切面
public class HelloAOP1_ASPECT {
public void beforeAdvice(){
System.out.println("beforeAdvice");
}
public void afterAdvice(){
System.out.println("afterAdvice");
}
public void afterReturningAdvice(Object retVal){
System.out.println("afterReturningAdvice:" + retVal);
}
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("AfterThrowingAdvice");
}
}
public class HelloAOP1_TEST {
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("./other/AOP.xml");
HelloAOP1 HelloAOP1 =(HelloAOP1)context.getBean("HelloAOP1");
HelloAOP1.setAge(111);
System.out.println("----------------");
HelloAOP1.getAge();
// System.out.println("----------------");
// HelloAOP1.printThrowException();
}
}
输出1
输出2
5.2、基于Aspect来实现—>方法规则式拦截
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!--开启aspectj注解-->
<aop:aspectj-autoproxy/>
<bean id="HelloAOP2" class="com.example.demolearn.other.spring.demo4.HelloAOP2">
</bean>
<bean id="HelloAOP2_ASPECT" class="com.example.demolearn.other.spring.demo4.HelloAOP2_ASPECT">
</bean>
</beans>
public class HelloAOP2 {
private Integer age;
public void setAge(Integer age){
this.age = age;
}
public Integer getAge(){
System.out.println("Age : " + age);
return age;
}
public void printThrowException(){
throw new IllegalArgumentException();
}
}
//切面
@Aspect
public class HelloAOP2_ASPECT {
@Pointcut("execution(* com.example.demolearn.other.spring.demo4.*.*(..))")
private void pointcut1(){
}
@Before("pointcut1()")
public void beforeAdvice(){
System.out.println("beforeAdvice");
}
@After("pointcut1()")
public void afterAdvice(){
System.out.println("afterAdvice");
}
@AfterReturning(pointcut = "pointcut1()", returning = "retVal")
public void afterReturningAdvice(Object retVal){
System.out.println("afterReturningAdvice:" + retVal);
}
@AfterThrowing(pointcut = "pointcut1()", throwing = "ex")
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("AfterThrowingAdvice");
}
}
public class HelloAOP2_TEST {
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("./other/AOP2.xml");
HelloAOP2 HelloAOP2 =(HelloAOP2)context.getBean("HelloAOP2");
HelloAOP2.setAge(111);
System.out.println("----------------");
HelloAOP2.getAge();
// System.out.println("----------------");
// HelloAOP1.printThrowException();
}
}
输出
-----
beforeAdvice
afterReturningAdvice:null
afterAdvice
----------------
beforeAdvice
Age : 111
afterReturningAdvice:111
afterAdvice
5.3、基于Aspect来实现—>方法注解式拦截
@Component
public class HelloAOP3 {
private Integer age = 111;
@HelloAOP3_annotation(name = "注解式拦截")
public Integer getAge(){
System.out.println("Age : " + age);
return age;
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HelloAOP3_annotation {
String name();
}
//切面
@Aspect
@Component//自动注入
public class HelloAOP3_ASPECT {
//注解切点
@Pointcut("@annotation(com.example.demolearn.other.spring.demo4.autotationAop.HelloAOP3_annotation)")
private void pointcut1(){
}
@Before("pointcut1()")
public void beforeAdvice(){
System.out.println("beforeAdvice");
}
@After("pointcut1()")
public void afterAdvice(JoinPoint joinPoint){
MethodSignature signature =(MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
HelloAOP3_annotation helloAOP3_annotation = method.getAnnotation(HelloAOP3_annotation.class);
System.out.println("注解式拦截 " + helloAOP3_annotation.name()); //5
System.out.println("afterAdvice");
}
@AfterReturning(pointcut = "pointcut1()", returning = "retVal")
public void afterReturningAdvice(Object retVal){
System.out.println("afterReturningAdvice:" + retVal);
}
@AfterThrowing(pointcut = "pointcut1()", throwing = "ex")
public void AfterThrowingAdvice(IllegalArgumentException ex){
System.out.println("AfterThrowingAdvice");
}
}
@Configuration
@ComponentScan("com.example.demolearn.other.spring.demo4.autotationAop")//扫描范围
@EnableAspectJAutoProxy //1 开启对AOP的支持
public class HelloAOP3_config {
}
public class HelloAOP3_TEST {
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HelloAOP3_config.class); // 1
HelloAOP3 helloAOP3 =(HelloAOP3)context.getBean("helloAOP3");
System.out.println("----------------");
helloAOP3.getAge();
context.close();
}
}