欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java日志三十五「AOP」

程序员文章站 2024-03-18 08:24:04
...

**

Spring的AOP概念

**

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个
热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑
的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高
了开发的效率。

这样看概念可能感觉有些模糊,我举个例子。在我们操作后段数据库时,往往要添加事务的操作,但是在每个业务层添加同样的事务操作的代码会导致代码冗余度太高,我们希望每次我们想进行事务操作的时候,我们有关的事务操作代码会自动执行。而我们之前学过动态代理来对方法增强。例如,我们现在相对业务层的每一个方法都进行事务的操作,那么我们可以这样做

public AccountService getAccountService(){
        AccountService as =(AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 添加事务的支持
             * @param proxy
             * @param method
             * @param args
             * @return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object rtValue = null;
                try {
                    //开启事物
                    txManager.beginTransaction();
                    //执行操作
                    rtValue = method.invoke(accountService, args);
                    //提交事务
                    txManager.commit();
                    //返回结果
                    return rtValue;
                } catch (Exception e) {
                    //回滚操作
                    txManager.rollback();
                    throw new RuntimeException(e);
                } finally {
                    //释放连接
                    txManager.release();
                }
            }
        });
        return as;
    }

这里使用了动态代理来完成需求。而AOP实现的效果与动态代理类似,让我们专心于关心具体的业务,而不需要关注其他非该接口关注的逻辑或处理。
编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。

AOP中的相关概念

Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。

Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。

Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。

Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。

Target(目标对象):织入 Advice 的目标对象.。

Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。

这些概念非常模糊难懂,当时我也理解了很久。我这里搬运了一篇博主的文章,里面的例子非常有助于理解:
链接: link.

理解了这些概念后,我们来看一下简单的实现AOP(通过XML文件配置)
业务层

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements AccountService {

    public void saveAccount() {
        System.out.println("执行了保存");
    }

    public void updateAccount(int i) {
        System.out.println("执行了更新");
    }

    public void deleteAccount() {
        System.out.println("执行了删除");
    }
}

切点:

public class Logger {
 /**
     * 用于打印日志,计划在切入点方法执行之前执行
     */
    public void printLog(){
        System.out.println("开始记录日志了");
    }
}

bean.xml文件配置

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--    配置spring的ioc,把-service对象配置进来-->
    <bean id="accountService" class="Gao.service.impl.AccountServiceImpl"></bean>
    <!-- 配置Logger类 -->
    <bean id="logger" class="Gao.utils.Logger"></bean>

    <!--配置AOP-->
    <aop:config>
        <!--配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
            <aop:before method="printLog" pointcut="execution(* Gao.service.impl.*.*(..))"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>

spring中基于XML的AOP配置步骤
1、把通知Bean也交给spring来管理
2、使用aop:config标签表明开始AOP的配置
3、使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一标识
ref属性:是指定通知类bean的Id。
4、在aop:aspect标签的内部使用对应标签来配置通知的类型
我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知
aop:before:表示配置前置通知
method属性:用于指定Logger类中哪个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强

切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
实际开发中切入点表达式的通常写法:
切到业务层实现类下的所有方法
* com.itheima.service.impl..(…)

test:

public class AOPTest {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        AccountService as = (AccountService) ac.getBean("accountService");
        as.saveAccount();
        as.deleteAccount();
        as.updateAccount(1);
    }
}

结果

开始记录日志了
执行了保存
开始记录日志了
执行了删除
开始记录日志了
执行了更新