12-Spring5 基于注解方式实现AOP *** 作

12-Spring5 基于注解方式实现AOP *** 作,第1张

12-Spring5 基于注解方式实现AOP *** 作 1.什么是 AOP?

(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得
业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
(3)使用登录例子说明 AOP

2.AOP 底层使用动态代理

(1)有两种情况动态代理
第一种 有接口情况,使用 JDK 动态代理,创建接口实现类代理对象,增强类的方法.

使用 Proxy 类里面的方法创建代理对象

方法有三个参数:

第一参数,类加载器
第二参数,增强方法所在类的接口,支持多个接口
第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分

UserDao

package com.limi.dao;

public interface UserDao {
    void say();
}

UserDaoImp

package com.limi.dao;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImp implements UserDao{

    @Override
    public void say(){
        System.out.println("UserDaoImp say....");
    }
}

UserDaoProxy, 用于编写增强逻辑

package com.limi.dao;

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

//创建代理对象代码
public class UserDaoProxy implements InvocationHandler {

    private Object obj;

    //把需要创建代理的对象传递进来
    public UserDaoProxy(Object obj){
        this.obj = obj;
    }

    //增强的逻辑
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

        //方法之前执行
        System.out.println("方法之前执行...");

        //被增强方法的执行
        Object res = method.invoke(obj, objects);

        //方法之后执行
        System.out.println("方法之后执行...");
        return res;
    }
}

测试类MyTest

package com.limi.test;
import com.limi.dao.UserDao;
import com.limi.dao.UserDaoImp;
import com.limi.dao.UserDaoProxy;
import org.junit.Test;

import java.lang.reflect.Proxy;

public class MyTest {

    @Test
    public void test1(){

        //需要增强方法的接口
        Class interfaces[] = {UserDao.class};

        //需要增强方法的对象
        UserDaoImp userDaoImp = new UserDaoImp();

        //获取方法被增强的对象
        UserDao userDao = (UserDao)Proxy.newProxyInstance(MyTest.class.getClassLoader(), interfaces, new UserDaoProxy(userDaoImp));

        //执行被增强的方法
        userDao.say();
    }
}


测试结果

第二种 没有接口情况,使用 CGLIB 动态代理,创建子类的代理对象,增强类的方法.

3.AOP术语

1.连接点
类里面哪些可以加入增强逻辑的方法, 这些方法称为连接点
2.切入点
实际被你增强了的方法
3.通知
实际增强的逻辑部分称为通知, 通知有多种类型:
前置通知
后置通知
环绕通知
异常通知
最终通知
4.切面
是一个动作, 把通知应用到切入点的过程

4.AOP *** 作

1、Spring 框架一般都是基于 AspectJ 实现 AOP *** 作,
AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP *** 作.

2、基于 AspectJ 实现 AOP *** 作
有两种方式:
(1)基于 xml 配置文件实现
(2)基于注解方式实现

3、在项目工程里面引入 AOP 相关依赖

4、切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )

举例 1:对 com.limi.dao.BookDao 类里面的 add 进行增强 execution(* com.limi.dao.BookDao.add(…))

举例 2:对 com.limi.dao.BookDao 类里面的所有的方法进行增强 execution(* com.limi.dao.BookDao.* (…))

举例 3:对 com.limi.dao 包里面所有类,类里面所有方法进行增强 execution(* com.limi.dao.. (…))

5.AOP代码实现

1.基于完全注解方式

SpringConfig

package com.limi.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration//配置类,代替xml配置文件
@ComponentScan(basePackages = {"com.limi"})//
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启 Aspect 生成代理对象
public class SpringConfig{
}

UserDao

package com.limi.dao;

import org.springframework.stereotype.Component;

//需要增强的类
@Component
public class UserDao {
    public void say(){
        System.out.println("UserDao say...");
    }
}

UserDaoProxy

package com.limi.dao;

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

//用于加入增强逻辑的类
@Component
@Aspect  //生成代理对象
public class UserDaoProxy {
    //前置通知
    //@Before 注解表示作为前置通知
    @Before(value = "execution(* com.limi.dao.UserDao.say(..))")
    public void before(){
        System.out.println("before...");
    }

    //后置通知(返回通知), 当有异常出现不执行
    @AfterReturning(value = "execution(* com.limi.dao.UserDao.say(..))")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }

    //最终通知
    @After(value = "execution(* com.limi.dao.UserDao.say(..))")
    public void after(){
        System.out.println("after...");
    }

    //异常通知
    @AfterThrowing(value = "execution(* com.limi.dao.UserDao.say(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing...");
    }

    //环绕通知
    @Around(value = "execution(* com.limi.dao.UserDao.say(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws
            Throwable {
        System.out.println("环绕之前.........");
       //被增强的方法执行, 这里也就是执行UserDao里面的say()方法
        //如果使用了环绕通知不执行 proceedingJoinPoint.proceed(); 则被增强的原方法内容不会执行, 只执行其他增强逻辑
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

测试类MyTest

package com.limi.test;
import com.limi.config.SpringConfig;
import com.limi.dao.UserDao;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {

    @Test
    public void test1(){
        //1.加载配置类
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

        //2.获取配置的对象, 参数1:bean的id值, 参数2: 类名.class
        UserDao userService = context.getBean("userDao", UserDao.class);

        //3.使用对象
        userService.say();

    }
}


测试结果

①注意:@AfterReturning标注的方法当被增强的方法有异常出现时不执行, 执行的是@AfterThrowing标注的方法
UserDao的say()方法内加int a = 10/0; 测试异常情况

测试结果, 可以看到@AfterReturning标注的方法未执行, 执行的是@AfterThrowing标注的方法

②小技巧:使用@Pointcut提取公共切入点
UserDaoProxy

package com.limi.dao;

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

//用于加入增强逻辑的类
@Component
@Aspect  //生成代理对象
public class UserDaoProxy {

    //提取公共切入点,可减少代码量
    @Pointcut(value = "execution(* com.limi.dao.UserDao.say(..))")
    public void pointCut(){
    }

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

    //后置通知(返回通知), 当有异常出现不执行
    @AfterReturning(value = "pointCut()")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }

    //最终通知
    @After(value = "pointCut()")
    public void after(){
        System.out.println("after...");
    }

    //异常通知
    @AfterThrowing(value = "pointCut()")
    public void afterThrowing(){
        System.out.println("afterThrowing...");
    }

    //环绕通知
    @Around(value = "pointCut()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws
            Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行, 这里也就是执行UserDao里面的say()方法
        //如果使用了环绕通知不执行 proceedingJoinPoint.proceed(); 则被增强的原方法内容不会执行, 只执行其他增强逻辑
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

测试结果

③多个增强类对同一个方法进行增强
(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高, 同类型的方法, 优先级高的类的方法先执行.

第一个增强类:UserDaoProxy

package com.limi.dao;

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

//用于加入增强逻辑的类
@Component
@Aspect  //生成代理对象
@Order(1)
public class UserDaoProxy {

    //提取公共切入点,可减少代码量
    @Pointcut(value = "execution(* com.limi.dao.UserDao.say(..))")
    public void pointCut(){
    }

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

    //后置通知(返回通知), 当有异常出现不执行
    @AfterReturning(value = "pointCut()")
    public void afterReturning(){
        System.out.println("afterReturning...");
    }

    //最终通知
    @After(value = "pointCut()")
    public void after(){
        System.out.println("after...");
    }

    //异常通知
    @AfterThrowing(value = "pointCut()")
    public void afterThrowing(){
        System.out.println("afterThrowing...");
    }

    //环绕通知
    @Around(value = "pointCut()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws
            Throwable {
        System.out.println("环绕之前.........");
         //被增强的方法执行, 这里也就是执行UserDao里面的say()方法
        //如果使用了环绕通知不执行 proceedingJoinPoint.proceed(); 则被增强的原方法内容不会执行, 只执行其他增强逻辑
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}

第二个增强类:UserDaoProxy2

package com.limi.dao;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

//用于加入增强逻辑的类
@Component
@Aspect  //生成代理对象
@Order(2)
public class UserDaoProxy2 {
    //提取公共切入点,可减少代码量
    @Pointcut(value = "execution(* com.limi.dao.UserDao.say(..))")
    public void pointCut(){
    }

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

测试结果, 可以看到同样是前置通知before,由于UserDaoProxy2的优先级更低, 所以后执行.

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5437798.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存