Spring5 AOP的使用

1. AOP的概念

(1)AOP是Aspect Oriented Programming的缩写,意思是面向切面编程,提供从另一个角度来考虑程序结构以完善面向对象编程(相对于OOP),即可以通过在编译期间、装载期间或运行期间实现在不修改源代码的情况下给程序动态添加功能的一种技术。通俗点说就是把可重用的功能提取出来,然后将这些通用功能在合适的时候织入到应用程序中;比如安全,日记记录,这些都是通用的功能,我们可以把它们提取出来,然后在程序执行的合适地方织入这些代码并执行它们,从而完成需要的功能并复用了这些功能。
(2)使用用户登录例子说明AOP

在这里插入图片描述
解释:假如现在我们在简单用户登录的逻辑上想要增加一个逻辑——通过判断登录用户的权限(管理员,普通用户等),然后使其登录到不同的页面。
原始解决方法:通过修改源代码来实现不同权限用户的登录。

if 管理员
	...
else if 普通用户
	...

源代码相互之间的耦合性较高,可能加一个条件判断语句也会影响其他代码,所以为了降低耦合性,AOP横空出世,可以“凌驾”于源代码之上,不用修改源代码就实现不同权限用户的登录。

2. AOP底层原理

AOP 底层使用动态代理 ,动态代理有两种情况

2.1 有接口情况,使用JDK动态代理

创建接口实现类代理对象,增强类的方法
在这里插入图片描述

2.2 没有接口,使用CGLIB动态代理

创建子类的代理对象,增强类的方法。
在这里插入图片描述

3. AOP常用术语

  1. 连接点
    一个类中哪些方法可以被增强,这些被增强的方法称为连接点
  2. 切入点(被增强方法中)
    一个类可以有多个方法被增强,但只有被实际增强的方法,才可称为切入点
  3. 通知(增强方法中)
    1. 增强方法中,实际去做增强的逻辑部分被称作通知
    2. 通知的类型
      a) 前置通知
      b) 后置通知
      c) 环绕通知
      d) 异常通知
      e) 最终通知
  4. 切面(一个动作过程)
    切面是把通知应用到切入点的过程。

4. AOP操作

4.1 准备工作

  1. Spring 框架一般都是基于 AspectJ 实现 AOP 操作,AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作
  2. 基于 AspectJ 实现 AOP 操作有两种方式:1)基于 xml 配置文件实现 (2)基于注解方式实现  推荐使用
  3. 引入相关jar包
  4. 切入点表达式,如下:
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强 
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
(3)例子如下:
    例 1:对 com.spring.dao.BookDao 类里面的 add 进行增强
		execution(* com.spring.dao.BookDao.add(..))
 	例 2:对 com.spring.dao.BookDao 类里面的所有的方法进行增强
		execution(* com.spring.dao.BookDao.* (..))
    例 3:对 com.spring.dao 包里面所有类,类里面所有方法进行增强
		execution(* com.spring.dao.*.* (..))

4.2 基于全注解方式实现AOP操作

  1. 创建被增强类
//被增强类
@Component //此注解的作用为创建对象
public class User {
    public void add() {
        System.out.println("add...... ");
    }
}
  1. 创建增强类
@Component
public class UserProxy {
    
}
  1. 创建配置类
@Configuration  //作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.spring"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}

@ComponentScan(basePackages = {“com.spring”})@EnableAspectJAutoProxy(proxyTargetClass = true)等价于xml文件中的如下配置

<!--开启注解扫描-->
<context:component-scan base-package="com.spring"></context:component-scan>
<!--开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  1. 配置不同类型的通知
@Component
@Aspect //生成代理对象
public class UserProxy {
    //前置通知
    //@before注解表示作为前置通知
    @Before(value = "execution(* com.spring.aop.User.add(..))")
    public void before() {
        System.out.println("before.....");
    }

    @After(value = "execution(* com.spring.aop.User.add(..))")
    public void after() {
        System.out.println("After.....");
    }

    @AfterReturning(value = "execution(* com.spring.aop.User.add(..))")
    public void afterReturning() {
        System.out.println("AfterReturning.....");
    }

    @AfterThrowing(value = "execution(* com.spring.aop.User.add(..))")
    public void afterThrowing() {
        System.out.println("AfterThrowing.....");
    }

    @Around(value = "execution(* com.spring.aop.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("环绕之前....");
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("环绕之后....");
    }
}
  1. 测试类
public class TestAop {
    @Test
    public void testAop() {
        ApplicationContext context = new AnnotationConfigApplicationContext(ConfigAop.class);
        User user = context.getBean("user", User.class);
        user.add();
    }
  1. 测试结果1
    在这里插入图片描述

  2. 代码优化——相同切入点抽取

@Component
@Aspect //生成代理对象
public class UserProxy {
    //相同切入点抽取
    @Pointcut(value = "execution(* com.spring.aop.User.add(..))")
    public void point() {

    }

    //前置通知
    //@before注解表示作为前置通知
    @Before(value = "point()")
    public void before() {
        System.out.println("before.....");
    }

    @After(value = "point()")
    public void after() {
        System.out.println("After.....");
    }

    @AfterReturning(value = "point()")
    public void afterReturning() {
        System.out.println("AfterReturning.....");
    }

    @AfterThrowing(value = "point()")
    public void afterThrowing() {
        System.out.println("AfterThrowing.....");
    }

    @Around(value = "point()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("环绕之前....");
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("环绕之后....");
    }
}
  1. 设置增强类优先级

当有多个增强类对一个方法进行增强时,需要设置增强类的优先级。
通过@Order(数字类型)注解实现,数字越小,优先级越高

//增强类1
@Component
@Aspect //生成代理对象
@Order(3)
public class UserProxy {
    //相同切入点抽取
    @Pointcut(value = "execution(* com.spring.aop.User.add(..))")
    public void point() {

    }

    //前置通知
    //@before注解表示作为前置通知
    @Before(value = "point()")
    public void before() {
        System.out.println("before.....");
    }

    @After(value = "point()")
    public void after() {
        System.out.println("After.....");
    }

    @AfterReturning(value = "point()")
    public void afterReturning() {
        System.out.println("AfterReturning.....");
    }

    @AfterThrowing(value = "point()")
    public void afterThrowing() {
        System.out.println("AfterThrowing.....");
    }

    @Around(value = "point()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("环绕之前....");
        try {
            proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("环绕之后....");
    }
}

//增强类2
@Component
@Aspect
@Order(1) //数字越小,优先级越高
public class PersonProxy {
    @Pointcut(value = "execution(* com.spring.aop.User.add(..))")
    public void point() {

    }

    @Before(value = "point()")
    public void before() {
        System.out.println("增强类2的before...");
    }
}
  1. 测试结果
    在这里插入图片描述

4.3 基于XML配置文件实现AOP操作

<?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 id="user" class="com.spring.aop.User"></bean>
    <bean id="userProxy" class="com.spring.aop.UserProxy"></bean>
    <!--配置aop增强-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="p" expression="execution(* com.spring.aop.User.add(..))"/>
        <!--配置切面-->
        <aop:aspect ref="userProxy">
            <aop:before method="before" pointcut-ref="p"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>

跟上面的基于注解的通知效果是一样的

//测试类
@Test
public void testAop() {
    ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("bean1.xml");
    User user = context.getBean("user", User.class);
    user.add();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值