Spring5框架day02之AOP

一、AOP基本概念

(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

(3)使用登录例子说明 AOP

二、AOP底层原理

AOP通过动态代理机制降低各功能直接的耦合度,使可以在不修改源码的情况下添加新的功能,其中有两种动态代理机制用于增强类的方法

1.存在接口时,JDK动态代理机制

使用JDK动态代理机制,是通过创建接口实现类的动态代理对象,来增强类的方法

首先创建接口UserDao以及接口实现类UserDaoImpl

package spring02day.AOP_JDK_work01.UserDao;

public interface UserDao {
    public int plus(int a,int b);
    public void get();
}


package spring02day.AOP_JDK_work01.UserDao;

public class UserDaoImpl implements UserDao{
    @Override
    public int plus(int a, int b) {
        System.out.println("plus....");
        return a+b;
    }

    @Override
    public void get() {
        System.out.println("getting....");
    }
}

创建执行类,在其中使用Proxy.newProxyInstance方法创建动态代理对象

package spring02day.AOP_JDK_work01.UserService;


import spring02day.AOP_JDK_work01.UserDao.UserDao;
import spring02day.AOP_JDK_work01.UserDao.UserDaoImpl;

import java.lang.reflect.Proxy;

public class Service {
    public static void main(String[] args) {
        //要创建代理对象,就要调用静态Proxy.newProxyInstance方法
        //其中有三个参数,第一个是实现代理类的加载,比如是Service类实现的代理,那使用其加载器
        //第二个参数是实现接口类的接口,可以实现多个接口,都放在一个接口数组中
        //第三个是实现InvocationHandler接口的类,用于写代码增强部分
        Class[] interfaces = {UserDao.class};
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao userDao1 = (UserDao)Proxy.newProxyInstance(Service.class.getClassLoader(), interfaces, new UserProxy(userDao));
        int plus = userDao1.plus(1,2);
        System.out.println(plus);
    }

}

 其中Proxy.newProxyInstance方法的第三个参数需要实现InvocationHandler接口的类,那创建一个UserProxy方法用于实现InvocationHandler接口

package spring02day.AOP_JDK_work01.UserService;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class UserProxy implements InvocationHandler {
    private Object obj;

    public UserProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("增强前......");
        //使用名称来判断调用了哪个方法
        if(method.getName().equals("plus")){
            args[0] = (Integer)args[0] + 1;
        }
        //使用method.invoke用于调用需实现增强的方法,在其前写的代码属于增强前操作,其后属于增强后操作
        Object invoke = method.invoke(obj, args);

        System.out.println("增强后......");

        return invoke;
    }
}

 InvocationHandler接口中的invoke方法就是增强方法,JDK代理机制的底层实现完毕。

2.CGLIB动态代理机制

使用CGLIB动态代理机制是没有接口时,创建类的继承子类的代理对象,增强类的方法

三、AOP术语

1.连接点

类中可以被增强的方法称为连接点

2.切入点

实际上被增强的方法,即在连击点的基础上,真正被增强的方法

3.通知

增强方法的逻辑部分被称为通知

通知有五种:(1)前置通知:方法执行前的通知

                        (2)后置通知:方法执行后的通知

                        (3)环绕通知:方法执行前后都可以实现的通知

                        (4)异常通知:方法执行中遇到异常的通知

                        (5)最终通知:方法执行最后无论是否有异常都会执行的通知,类似于finally

4.切面

是一种动作,即将通知应用于切入点的动作

四、AOP操作

1.准备阶段

(1)Spring 框架一般都是基于 AspectJ 实现 AOP 操作

AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使 用,进行 AOP 操作

(2)基于 AspectJ 实现 AOP 操作

(1)基于 xml 配置文件实现

(2)基于注解方式实现(使用)

(3)切入点表达式

(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强

(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )

(注:*表示任意)

举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强

execution(* com.atguigu.dao.BookDao.add(..))

即不写权限表示任意权限,第一个*表示任意返回类型,路径为 com.atguigu.dao.BookDao,方法为add(),任意参数

举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强

execution(* com.atguigu.dao.BookDao.* (..))

举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强

execution(* com.atguigu.dao.*.* (..))

2.Aspect注解

(1)基本使用

Aspect注解在增强类前使用,并且需要配置xml文件,且最后需要切入点表达式连接被增强类

创建被增强类

package spring02day.AOP_JDK_work02.USER;


import org.springframework.stereotype.Component;

//被增强类
@Component
public class User {
    //设置切入点
    public void add(){
        System.out.println("相加成功...");
    }
}

创建增强类,其中五种通知的标签罗列在下代码中

package spring02day.AOP_JDK_work02.UserProxy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

//增强类
@Component
@Aspect     //生成代理对象
public class UserProxy {

    //前置通知,使用@Before注解,后使用切入点表达式
    @Before(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
    public void before(){
        System.out.println("before....");
    }

    //最终通知,使用@After注解,无论是否有异常,都会执行
    @After(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
    public void after(){
        System.out.println("after....");
    }

    //后置通知,使用@AfterReturning注解
    @AfterReturning(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
    public void afterReturning(){
        System.out.println("afterReturning....");
    }

    //异常通知,使用@AfterThrowing注解,只有有异常抛出时才会执行
    @AfterThrowing(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing....");
    }

    //环绕通知,使用@Around注解
    //其中加入ProceedingJoinPoint proceedingJoinPoint参数用于分开前置后置
    //在其前是前置,在其后是后置,如果没有该参数,那程序只会执行其前指令
    @Around(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around....");
        proceedingJoinPoint.proceed();
        System.out.println("around down....");
    }

}

配置xml文件,其中要创建用于bean注解的context空间和代理注解的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: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,aop空间,用于注释-->
    <!--context扫描-->
    <context:component-scan base-package="spring02day.AOP_JDK_work02"></context:component-scan>
    <!--aop注解自动查找增强类-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

(2)抽取相同切入点

使用@Pointcut注解抽取相同切入点,最后调用方法名可以直接使用

    @Pointcut(value = "execution(* spring02day.AOP_JDK_work02.USER.User.add(..))")
    public void point(){
        
    }

    //前置通知,使用@Before注解,后使用切入点表达式
    @Before(value = "point()")
    public void before(){
        System.out.println("before....");
    }

(3)如果存在多个增强类增强同一切入点,设置优先级

在类前使用@Order()注解,在@Order()括号中填入数字,数字越小,优先级越高,数字从0开始

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值