SpringAOP|面向切面编程|AOP术语定义|经典SpringAOP实现(基于动态代理)|自定义类|使用注解实现AOP

Spring AOP


前记:

  • 参考书籍《Spring实战(第4版)》
  • 很久之前看了AOP相关概念,但并没有进行实践,这次再次理解相关概念并实践
  • 学习前,最好先理解一下设计模式中的代理模式:https://blog.csdn.net/qq_47234534/article/details/114106579
  • 个人理解:AOP是进行水平扩展的经典实例,垂直扩展:改变原有系统的结构,不引入新资源,如垂直分表分库,垂直功能开发等;水平扩展:不改变原有的系统的结构,引入新资源(如增加服务器),如水平分表分库。要增加新功能,水平是扩展性比较强的。

一、概述
1、使用AOP优点
  1. 可以实现横向关注点与业务的分离
  2. 将横向关注点模块化之后(即切面),可以使关注点代码更集中
  3. 将横向关注点模块化之后(即切面),服务业务更精简
  4. 扩展更简单。可以为现有类增加方法和属性
2、AOP术语
  1. 横切关注点:通用的功能,与业务功能相对,如安全,日志等。

在这里插入图片描述

  1. 切面:横切关注点被模块化成一个个具体特殊的类,这些类就称为切面
  2. 通知:切面要做的工作,即切面中的方法
  3. 连接点:所有潜在的,能够应用通知的点,即能被切面切入的所有点
  4. 切点:说明通知的具体应用位置,即通过切点来选择连接点
  5. 织入:把切面应用到目标对象并创建新的代理对象的过程,即环绕增强
  6. 引入:向现有的类添加新的方法或者属性
3、切点表达式

基本格式

concert是一个包,Performance是一个类。

concert. * . *代表concert下的所在这里插入图片描述
有类和所有方法

在这里插入图片描述

可以加入一些AspectJ指示器来细化该表达式。用时查。

切点指示器P107

限制切点范围P109

带参数P115

只有execution是具体执行的,其它都是用来限制匹配的。

4、依赖
    <properties>
        <spring.version>5.2.2.RELEASE</spring.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.12</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.12</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
    </dependencies>
二、经典代理方法
  • 只支持方法级别的代理。原因是底层使用了动态代理
  • 功能相对于以下两种最强大

抽象对象及真实对象

public interface UserService {
    void add();

    void delete();
}

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("add a user");
    }

    public void delete() {
        System.out.println("delete a user");
    }
}

切面

//前绕通知切面
public class LogBefore implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法:" + method.getName() + ",进行了调用前通知");
    }
}

//后绕通知切面
public class LogAfter implements AfterReturningAdvice {

    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法:" + 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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userServiceImpl" class="com.hao.service.UserServiceImpl"/>
    <bean id="logBefore" class="com.hao.aspect.LogBefore"/>
    <bean id="logAfter" class="com.hao.aspect.LogAfter"/>

    <!--aop相关配置-->
    <aop:config>
        <!--定义切点-->
        <aop:pointcut id="userPointCut" expression="execution(* com.hao.service.UserServiceImpl.*(..))"/>
        <!--环绕增强-->
        <aop:advisor advice-ref="logBefore" pointcut-ref="userPointCut"/>
        <aop:advisor advice-ref="logAfter" pointcut-ref="userPointCut"/>
    </aop:config>
</beans>

测试

public class TestMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = (UserService)classPathXmlApplicationContext.getBean("userServiceImpl");
        userService.add();
        userService.delete();
    }
}

/**
方法:add,进行了调用前通知
add a user
方法:add,进行了调用后通知
方法:delete,进行了调用前通知
delete a user
方法:delete,进行了调用后通知

Process finished with exit code 0
**/
三、纯POJO方法(自定义类)
//抽象对象
public interface UserService {
    void add();

    void delete();
}
//真实对象
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("add a user");
    }

    public void delete() {
        System.out.println("delete a user");
    }
}
//切面
public class LogAspect {

    public void before() {
        System.out.println("-------前绕通知-------");
    }

    public void after() {
        System.out.println("-------后绕通知-------");
    }

    /**
     *
     * @param proceedingJoinPoint 会自动注入进来。包含一些被代理对象的一些信息
     */
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("---------执行带参数的环绕通知前------");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("被代理对象签名为:" + proceedingJoinPoint.getSignature());
        System.out.println("--------执行带参数的环绕通知后-------");

    }
}
//测试
public class TestMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = (UserService)classPathXmlApplicationContext.getBean("userServiceImpl");
        userService.add();
        userService.delete();
    }
}
/**
-------前绕通知-------
---------执行带参数的环绕通知前------
add a user
被代理对象签名为:void com.hao.service.UserService.add()
--------执行带参数的环绕通知后-------
-------后绕通知-------


-------前绕通知-------
---------执行带参数的环绕通知前------
delete a user
被代理对象签名为:void com.hao.service.UserService.delete()
--------执行带参数的环绕通知后-------
-------后绕通知-------

Process finished with exit code 0
**/

配置

<?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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册Bean-->
    <bean id="userServiceImpl" class="com.hao.service.UserServiceImpl"/>
    <bean id="logAspect" class="com.hao.aspect.LogAspect"/>

    <aop:config>
        <!--定义通用切点。可在多个切面中使用-->
        <aop:pointcut id="userPointCut" expression="execution(* com.hao.service.UserServiceImpl.*(..))"/>
        <!--定义切面:logAspect-->
        <aop:aspect ref="logAspect">
            <!--通知织入,环绕增强-->
            <aop:before method="before" pointcut-ref="userPointCut"/>
            <aop:after method="after" pointcut-ref="userPointCut"/>
            <aop:around method="around" pointcut-ref="userPointCut"/>
        </aop:aspect>
    </aop:config>
</beans>
四、注解
  • 本质上,它依然是Spring基于代理的切面P114
  • 注解和方法三中的配置内容是一一对应的
//抽象对象
public interface UserService {
    void add();

    void delete();
}



//真实对象
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("add a user");
    }

    public void delete() {
        System.out.println("delete a user");
    }
}



//切面
@Aspect
//配置中可以用这个注解替代@EnableAspectJAutoProxy
public class LogAspect {

    @Before("execution(* com.hao.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("-------前绕通知-------");
    }

    @After("execution(* com.hao.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("-------后绕通知-------");
    }

    /**
     *
     * @param proceedingJoinPoint 会自动注入进来。包含一些被代理对象的一些信息
     */
    @Around("execution(* com.hao.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("---------执行带参数的环绕通知前------");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("被代理对象签名为:" + proceedingJoinPoint.getSignature());
        System.out.println("--------执行带参数的环绕通知后-------");

    }
}



//测试
public class TestMain {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = (UserService)classPathXmlApplicationContext.getBean("userServiceImpl");
        userService.add();
        userService.delete();
    }
}
/**
-------前绕通知-------
---------执行带参数的环绕通知前------
add a user
被代理对象签名为:void com.hao.service.UserService.add()
--------执行带参数的环绕通知后-------
-------后绕通知-------


-------前绕通知-------
---------执行带参数的环绕通知前------
delete a user
被代理对象签名为:void com.hao.service.UserService.delete()
--------执行带参数的环绕通知后-------
-------后绕通知-------

Process finished with exit code 0
**/

配置

<?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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userServiceImpl" class="com.hao.service.UserServiceImpl"/>
    <bean id="logAspect" class="com.hao.aspect.LogAspect"/>

    <!--开启自动代理配置-->
    <aop:aspectj-autoproxy/>
</beans>
五、技巧

1注解切点语句重用

//切面
@Aspect
//配置中可以用这个注解替代@EnableAspectJAutoProxy
public class LogAspect {

    @Before("execution(* com.hao.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("-------前绕通知-------");
    }

    @After("execution(* com.hao.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("-------后绕通知-------");
    }

    /**
     *
     * @param proceedingJoinPoint 会自动注入进来。包含一些被代理对象的一些信息
     */
    @Around("execution(* com.hao.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("---------执行带参数的环绕通知前------");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("被代理对象签名为:" + proceedingJoinPoint.getSignature());
        System.out.println("--------执行带参数的环绕通知后-------");

    }
}


----------------------------------------------------------------------
可以被简化为:
@Aspect
//配置中可以用这个注解替代@EnableAspectJAutoProxy
public class LogAspect {

    @Pointcut("execution(* com.hao.service.UserServiceImpl.*(..))")
    public void action() {

    }

    @Before("action()")
    public void before() {
        System.out.println("-------前绕通知-------");
    }

    @After("action()")
    public void after() {
        System.out.println("-------后绕通知-------");
    }

    /**
     *
     * @param proceedingJoinPoint 会自动注入进来。包含一些被代理对象的一些信息
     */
    @Around("action()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("---------执行带参数的环绕通知前------");
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("被代理对象签名为:" + proceedingJoinPoint.getSignature());
        System.out.println("--------执行带参数的环绕通知后-------");

    }
}

2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Meow_Sir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值