【示例】Spring-AOP理解

发布于:2024-04-10 ⋅ 阅读:(143) ⋅ 点赞:(0)

前言

本文不仅介绍Spring中AOP的几种实现方式。还整体介绍一下:静态代理、JDK API动态代理和CGLIB动态代理

文中示例的代码地址:

GitHub https://github.com/Web-Learn-GSF/Java_Learn_Examples
父工程 Java_Framework_Spring

静态代理、动态代理*2

他人文章参考

这三个示例可以直接看文章:https://segmentfault.com/a/1190000011291179#item-4

自己编写示例

下边示例是自己写的,有空再补充吧:

示例 在上述工程的具体Module里面
静态代理 AOP_0_Static_Proxy
JDK API动态代理 暂无
CGLIB动态代理 暂无

三种方式优缺点对比

静态代理:

  • 特点:实现简单,只需要代理对象对目标对象封装,即可实现功能增强。

  • 缺点:静态代理只能为一个目标对象服务,目标对象过多,就会产生很多的代理类

    这个缺点存疑。如果将目标对象实例化在代理类中,即一个静态代理类代理一个目标对象,那肯定是目标对象越多,代理类就越多。但如果通过在代理对象中定义set方法,就可以实现:一个代理类代理同一个接口下的多个目标对象,就没有这个缺点了。

动态代理 | JDK API:

  • 特点:动态代理必须实现InvocationHandler接口,且要求目标对象有接口实现。通过反射代理方法,实现对目标对象的增强
  • 缺点:目标对象必须要有接口,不然无法应用

动态代理 | CGLIB:

  • 特点:无需目标对象有接口实现,通过生成类字节码实现代理,比反射稍快,不存在性能问题。
  • 缺点:代理类需要继承目标对象,即目标对象不能被final修饰

Spirng中实现AOP

Spring中实现AOP有如下几种方式:

实现方式 在上述工程中的Module位置
xml + 实现Spring的API AOP_1_Xml_SpringAPI
xml + 自定义类 AOP_2_Xml_CustomClass
xml + 注解 + 自定义类 AOP_3_Xml_Annotation
注解 + 自定义类 暂空

下边展示每种实现方式里面的关键部分代码。

xml + 实现Spring的API

public class LogAfterMethod implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("AOP后置通知:" + "在" + target.getClass().getName() + "的" + method.getName() + "方法调用后执行。目标对象-方法-参数,都可以获取到");
    }
}
public class LogBeforeMethod implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("AOP前置通知:" + "在" + target.getClass().getName() + "的" + method.getName() + "方法调用前执行。目标对象-方法-参数,都可以获取到");
    }
}
<?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.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!--  注册bean  -->
    <bean id="userService" class="GSF.Example.Service.UserServiceImpl" />
    <bean id="logBefore" class="GSF.Example.Log.LogBeforeMethod" />
    <bean id="logAfter" class="GSF.Example.Log.LogAfterMethod"/>

	<!-- Aop的设置-->
    <aop:config>
		<!-- 切入点-->
        <aop:pointcut id="pointcut" expression="execution(* GSF.Example.Service.UserServiceImpl.*(..))"/>
		<!-- 执行前置通知-->
        <aop:advisor advice-ref="logBefore" pointcut-ref="pointcut" />
        <aop:advisor advice-ref="logAfter" pointcut-ref="pointcut" />
    </aop:config>
</beans>

xml + 自定义类

package GSF.Example.Log;

public class CustomLogClass {
    public void before(){
        System.out.println("---------基于XML自定义类方式实现,前置通知:方法执行前---------");
    }

    public void after(){
        System.out.println("---------基于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"
       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">

	<!--  注册bean  -->
    <bean id="userService" class="GSF.Example.Service.UserServiceImpl" />
    <bean id="customLogClass" class="GSF.Example.Log.CustomLogClass" />

	<!--Aop的设置-->
    <aop:config>
		<!-- 切入点-->
        <aop:aspect ref="customLogClass">
            <aop:pointcut id="pointcut" expression="execution(* GSF.Example.Service.UserServiceImpl.*(..))"/>
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut" />
        </aop:aspect>
    </aop:config>
</beans>

xml + 注解 + 自定义类

package GSF.Example.Log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnotationClass {
    @Before("execution(* GSF.Example.Service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("---------基于注解方式实现,前置通知:方法执行前---------");
    }

    @After("execution(* GSF.Example.Service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("---------基于注解方式实现,后置通知:方法执行后---------");
    }

    @Around("execution(* GSF.Example.Service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable{
        System.out.println("环绕通知:环绕前");
        System.out.println(jp.getSignature());

        // 执行目标方法
        Object proceed = jp.proceed();
        System.out.println(proceed);

        System.out.println("环绕通知:环绕后");
    }
}
<?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.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- 注册bean  -->
    <bean id="userService" class="GSF.Example.Service.UserServiceImpl" />
    <bean id="annotationPointcut" class="GSF.Example.Log.AnnotationClass" />
    
    <!-- 自动代理 -->
    <aop:aspectj-autoproxy/>

</beans>

注解 + 自定义类

在上述:xml+注解+自定义类的实现方式中,xml文件的作用已经弱化为只有:注册bean + 开启允许注解实现AOP

只需要通过注解取代上述两种方法,就可以抛弃xml文件,完成仅:注解+自定义类实现AOP。

有空再写。

理解AOP中的概念

通过该参考文章,理解AOP中的相关概念:https://www.cnblogs.com/aduner/p/14656427.html

接下来相关概念的解释,也都用上述文章中的示例来讲。

概念 | 连接点

  • Spring是方法级的AOP,一般对某个方法进行增强。选择的这个方法就是连接点的意思
  • 这里选择的切入点就是:Landlord类的service()方法
@Component
public class Landlord {
    public void service() {
        System.out.println("签合同");
        System.out.println("收钱");
    }
}

概念 | 切面

  • 切面可以理解一个拦截器,在切面上,我们对连接点的前边、后边进行方法增强。
  • 切面对应一个类,也是Spring中的一个Bean
  • 这里选择的切面就是Broker类,通过@Aspect注解定义该类为一个切面
@Component
@Aspect
class Broker {

    @Before("execution(* com.aduner.demo03.pojo.Landlord.service())")
    public void before(){
        System.out.println("带租客看房");
        System.out.println("谈钱");
    }

    @After("execution(* com.aduner.demo03.pojo.Landlord.service())")
    public void after(){
        System.out.println("给钥匙");
    }
}

概念 | 切入点

  • 切面中的方法都对一个连接点进行增强,有些重复的代码。可以定义一个切入点。

  • 切面中的方法想要对某个切入点进行增强,就使用该切入点

  • 上述代码可以修改为:

@Component
@Aspect
class Broker {

    // 切入点
    @Pointcut("execution(* com.aduner.demo03.pojo.Landlord.service())")
    public void pointcut() {
    }

    // 使用上述切入点
    @Before("pointcut()")
    public void before() {
        System.out.println("带租客看房");
        System.out.println("谈钱");
    }

    @After("pointcut()")
    public void after() {
        System.out.println("给钥匙");
    }
}

概念 | 通知、目标、代理、横切关注点

  • 通知:上述代码中已经体现。before()方法就是一个前置通知(通过注解@Before赋予该方法前置通知的功能)
  • 目标:Spring的AOP是对方法进行增强,被增强的方法叫做:连接点。那连接点所属的类就是目标
  • 代理:Spring AOP的实现是动态代理,会有一个代理对象,代码中没有体现,但这个概念好理解
  • 横切关注点:又是一个宏观的概念。日志、安全、权限都可以认为是横切关注点。示例中的横切关注点可以理解为:租房的准备工作

其余问题

  • 多个切面:如果不同切面的切点相同,那就有多个切面,需要规定每个切面的执行顺序

  • 通知Advice的类别(5种):

通知类型 连接点 实现接口
前置通知 方法前 org.springframework.aop.MethodBeforeAdvice
后置通知 方法后 org.springframework.aop.AfterReturningAdvice
环绕通知 方法前后 org.springframework.aop.MethodInterceptor
异常抛出通知 方法抛出异常 org.springframework.aop.ThrowsAdvice
引介通知 类中增加新的方法属性 org.springframework.aop.IntroductionInterceptor


网站公告

今日签到

点亮在社区的每一天
去签到