栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

AOP面向切面编程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

AOP面向切面编程

AOP面向切面编程
    • 底层原理
    • JDK 动态代理
      • Proxy 类
      • 编写 JDK 动态代理代码
    • AOP术语
    • AOP 操作(准备工作)
    • AspectJ 注解
      • 相同的切入点抽取
      • 设置增强类优先级
    • 完全使用注解开发
    • AspectJ 配置文件(了解)

AOP的概念:

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

底层原理

AOP 底层使用动态代理

第一种 有接口情况,使用 JDK 动态代理

  • 创建接口实现类代理对象,增强类的方法


第二种 没有接口情况,使用 CGLIB 动态代理

  • 创建子类的代理对象,增强类的方法

增加新功能原始方式是写一个它的子类然后继承该功能方法,在重写的方法里增加新功能的代码,CGLIB代理便是创建一个当前类子类的代理对象来完成相关的逻辑

JDK 动态代理 Proxy 类
  • java.lang.reflect.Proxy
  • newProxyInstance 方法


方法有三个参数:

  • 第一参数,类加载器,一般调用当前类的加载器即可

    • JDKProxy.class.getClassLoader()
      
  • 第二参数,增强方法所在的类,这个类实现的接口,支持多个接口,用数组来封装,写法如下:

    • new Class[]{UserDao.class}
      
  • 第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分

    • new UserDaoProxy()
      
    • new InvocationHandler() {
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              return null;
          }
      });
      

      这里可以写实现类InvocationHandler()接口的类,也可以直接用匿名实现类来完成操作

编写 JDK 动态代理代码

(1)创建接口,定义方法

public interface UserDao {
    public int add(int a,int b);
    public String update(String str);
}

(2)创建接口实现类,实现方法

public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println("UserDao add()方法");
        return a + b - 1;
    }

    @Override
    public String update(String str) {
        System.out.println("UserDao update()方法");
        return str;
    }
}

(3)使用 Proxy 类创建接口代理对象

//代理类
public class UserDaoProxy implements InvocationHandler {

    private UserDao userDao;

    public UserDaoProxy(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("在方法" + method.getName() + "前增加操作");
        Integer invoke = (Integer) method.invoke(userDao, args);
        Object result = invoke + 1;
        System.out.println("在方法" + method.getName() + "后增加操作");
        return result;
    }
}

测试:

public class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces = {UserDao.class};
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao userDao1 = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int add = userDao1.add(2, 3);
        System.out.println(add);
    }
}

AOP术语

连接点

类里面哪些方法可以被增强,这些方法就称为连接点

切入点

实际被真正增强的方法,称为切入点

通知(增强)

实际增强的逻辑部分称为通知(增强),有以下5类:

  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 最终通知

切面

把通知应用到切入点的过程,是一个动作

AOP 操作(准备工作)

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

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

2、基于 AspectJ 实现 AOP 操作

  • 基于 xml 配置文件实现
  • 基于注解方式实现(推荐

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

4、切入点表达式

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

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

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

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

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

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

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

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

    • *号表示任意权限,返回值可以省略不写,参数列表可以用两个点表示

AspectJ 注解

1、创建类,在类里面定义方法

//被增强类
@Component
public class User {
    public void add(){
        System.out.println("User add()方法执行。。。。");
    }
}

2、创建增强类(编写增强逻辑)

//增强类
@Component
public class UserProxy {

    //前置通知
    public void before(){
        System.out.println("前置通知:before....");
    }
    
}    

3、进行通知的配置

(1)在 spring 配置文件中,开启注解扫描(需要配置context空间)


(2)使用注解创建 User 和 UserProxy 对象

  • 在需要用到的类上添加创建bean实例的注解,如@Component

(3)在增强类上面添加注解 @Aspect

//增强类
@Component
@Aspect //该注解作用是生成代理对象
public class UserProxy {

(4)在 spring 配置文件中开启生成代理对象(需要配置aop空间,和context类似)


4、配置不同类型的通知

//增强类
@Component
@Aspect //该注解作用是生成代理对象
public class UserProxy {

    //前置通知
    @Before(value = "execution(* spring5.aopanno.User.add(..))")
    public void before(){
        System.out.println("前置通知:before....");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "execution(* spring5.aopanno.User.add(..))")
    public void afterReturning(){
        System.out.println("后置通知:afterReturning....");
    }

    //最终通知
    @After(value = "execution(* spring5.aopanno.User.add(..))")
    public void after(){
        System.out.println("最终通知:after....");
    }

    //异常通知
    @AfterThrowing(value = "execution(* spring5.aopanno.User.add(..))")
    public void afterThrowing(){
        System.out.println("异常通知:afterThrowing....");
    }

    //环绕通知
    @Around(value = "execution(* spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕通知(前):around....");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕通知(后):around....");
    }
}

测试:

ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
user.add();

正常输出:


异常输出:

由上可以得出异常通知只有在出现异常的时候执行,后置通知和最终通知的区别就在于最终通知有异常也执行,而后置通知在出现异常便不会执行

相同的切入点抽取

针对上面多个通知都是使用的一个切入点表达式,所有我们可以把相同的切入点表达式抽取出来,写一个带有注解==@Pointcut==的方法,将切入点表达式写到该注解的value里面,然后在需要用到该切入点表达式的通知注解的value里写入该方法名就行

//相同切入点抽取
@Pointcut(value = "execution(* spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
@Before(value = "pointdemo()")
public void before(){
    System.out.println("前置通知:before....");
}
设置增强类优先级

有多个增强类对同一个方法进行增强,就得设置增强类优先级,方法是:

  • 在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy{}
完全使用注解开发

相当于开启注解扫描和生成代理对象

@Configuration
@ComponentScan(basePackages = {"com.atguigu"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
AspectJ 配置文件(了解)

一般我们都是使用注解方式,所有配置文件的方式了解即可

1、创建两个类,增强类和被增强类,创建方法

2、在 spring 配置文件中创建两个类对象



3、在 spring 配置文件中配置切入点


     
     
     
     
         
         
     

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/272044.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号