AOP的不同实现方式

简介

AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
作用:AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。
可以将其分为两类:

静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。

AOP 领域中的特性术语:

通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
切点(PointCut): 可以插入增强处理的连接点。
切面(Aspect): 切面是通知和切点的结合。
引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

使用

我们需要先在maven中添加这样一个包 简单理解,支持切入点表达式等等

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
  1. 第一中方式 使用spring中原生接口API

写一个接口、实现类。

package aw.service;

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}

package aw.service;

public class UserServiceImp implements UserService{
    @Override
    public void add() {
        System.out.println("添加了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改了一个用户");
    }

    @Override
    public void select() {
        System.out.println("查看了一个用户");
    }
}

上面是我们创建好的实体类,现在我们需要在他执行的时候添加一些日志的输出情况。
方法执行前的日志,这里重点是我们要让这个类继承MethodBeforeAdvice类,然后覆盖这类里面多的before方法,Method method, Object[] objects, Object o这个三个参数添加切点类的方法、参数数组、切点类

package aw.service.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class LogBefore implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"方法");
    }
}

执行后的日志,这里他继承AfterReturningAdvice类, 这个覆盖方法是afterReturning他的参数比before多一个,Object returnValue这个是返回值。

package aw.service.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class LogAfter implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果是"+returnValue);
    }
}

现在开始写配置文件
首先是在spring中注册类

<bean id="logBefore" class="aw.service.log.LogBefore"/>
<bean id="logAfter" class="aw.service.log.LogAfter"/>

<bean id="userServiceImp" class="aw.service.UserServiceImp"/>

写aop的配置,这时我们需要先添加约束,我们可以先添加<aop:然后进行导入。

 xmlns:aop="http://www.springframework.org/schema/aop"
 http://www.springframework.org/schema/aop
  https://www.springframework.org/schema/aop/spring-aop.xsd">

这里重点讲解一下配置文件中的标签,
<aop:pointcut id="" expression="execution()"很明显这是一个切点类,id自己进行设置,execution表达式他是一种格式,下面是他的使用方式
整个表达式可以分为五个部分:

1、execution(): 表达式主体。

2、第一个*号:表示返回类型, *号表示所有的类型。

3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。

4、第二个*号:表示类名,*号表示所有的类。

5、*(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数

<aop:advisor advice-ref="" pointcut-ref="" 定义通知其(通知器跟切面一样,也包括通知和切点)。advice-ref是引用我们之前定义好的切面类,pointcut-ref是定义的切点类。

<aop:config>
        <aop:pointcut id="poi" expression="execution(* aw.service.UserServiceImp.*(..))"/>

        <aop:advisor advice-ref="logBefore" pointcut-ref="poi"/>
        <aop:advisor advice-ref="logAfter" pointcut-ref="poi"/>
    </aop:config>

测试类,我们在使用这种API接口形式,再接受对象的时候必须使用接口接受。

import aw.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class T1 {
    @Test
    public void test1(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理必须是接口
        UserService userService = (UserService) context.getBean("userServiceImp");
        userService.add();
    }
}


2.使用切面类
使用切面类我们就单独定义一个类,在里面写好我们需要的切面。

package aw.diy;

public class Diy {
    public void before(){
        System.out.println("执行前——————————");
    }
    public void after(){
        System.out.println("执行后------------");
    }
}

配置文件,同样是先注册切面类,<aop:config在写配置,<aop:aspect ref="diy"我们现在使用的这种就是以切面类来定义, <aop:pointcut同样是写切点类, <aop:before这个标签是执行之前的标签 方法名就是我们切面里的方法名,切点是定义的切点。

 <bean id="diy" class="aw.diy.Diy"/>

    <aop:config>
        <aop:aspect ref="diy">
            <aop:pointcut id="poi" expression="execution(* aw.service.UserServiceImp.*(..))"/>
            <aop:before method="before" pointcut-ref="poi"/>
            <aop:after method="after" pointcut-ref="poi"/>
        </aop:aspect>
    </aop:config>

测试类同样

import aw.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class T1 {
    @Test
    public void test1(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理必须是接口
        UserService userService = (UserService) context.getBean("userServiceImp");
        userService.add();
    }
}

3.注解开发
同样我们先写这个注解类,首先是在这个的上面添加@Aspect注解这就说明这个类是一个切面类。在我们写方法上面添加好相对应的注解名,注解的参数需要添加execution表达式。
@Around注解是环绕的意思、ProceedingJoinPoint 这个类对切面类进行一些操作。

package aw.service;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;


@Aspect
public class AnnotationPointCut {
    @Before("execution(* aw.service.UserServiceImp.*(..))")
    public void before(){
            System.out.println("方法执行前");
    }
    @After("execution(* aw.service.UserServiceImp.*(..))")
    public void after(){
        System.out.println("方法执行后");
    }
    @Around("execution(* aw.service.UserServiceImp.*(..))")
    public void around(ProceedingJoinPoint pj) throws Throwable {
        System.out.println("方法环绕前");
        Signature signature = pj.getSignature();//获得签名 就是那个类下的那个方法
        System.out.println("signature:"+signature);
        //执行方法
        Object proceed = pj.proceed();
        System.out.println(proceed);

        System.out.println("方法环绕后");
    }
}


配置文件,添加注册类,开启注解支持


    <bean id="an" class="aw.service.AnnotationPointCut"/>
    <!--    开启注解支持-->
    <aop:aspectj-autoproxy/>

测试类

import aw.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class T1 {
    @Test
    public void test1(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理必须是接口
        UserService userService = (UserService) context.getBean("userServiceImp");
        userService.add();
    }
}

他的输出顺序:
方法环绕前
signature:void aw.service.UserService.add()
方法执行前
添加了一个用户
方法执行后
null
方法环绕后
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值