Java AOP(面向切面编程)

2020-09-22  本文已影响0人  丿星纟彖彳亍

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

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

1、到底什么是AOP(面向切面编程)?

无论在学习或者面试的时候,大家都会张口说spring的特性AOP和IOC(控制反转),举个例子给大家说明一下:

有A,B,C三个方法,但是在调用每一个方法之前,要求打印一个日志:某一个方法被开始调用了!

在调用每个方法之后,也要求打印日志:某个方法被调用完了!

一般人会在每一个方法的开始和结尾部分都会添加一句日志打印吧,这样做如果方法多了,就会有很多重复的代码,显得很麻烦,这时候有人会想到,为什么不把打印日志这个功能封装一下,然后让它能在指定的地方(比如执行方法前,或者执行方法后)自动的去调用呢?如果可以的话,业务功能代码中就不会掺杂这一下其他的代码,所以AOP就是做了这一类的工作,比如,日志输出,事务控制,异常的处理等。

如果把AOP当做成给我们写的“业务功能”增添一些特效,就会有这么几个问题:

2、AOP术语

关键就是:切点定义了哪些连接点会得到通知

3、AOP 编码流程

3.1 导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.6.RELEASE</version>
</dependency>

3.2 准备Target

public class UserServiceImpl implements UserService{
    private UserDAO userDAO;
    // set/get...
    @Override
    public void updateUser(User user) {
        System.out.println("update in service==========");
        userDAO.updateUser(user);
    }

    @Override
    public void insertUser(User user) {
        System.out.println("insert in service===============");
        userDAO.insertUser(user);
    }
}

3.3 准备Advice

public class MyBeforeAdvice implements MethodBeforeAdvice{
    //环绕额外功能
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        System.out.println("环绕前.....");

        Object ret = invocation.proceed();   //执行目标业务方法

        System.out.println("环绕后");
        return ret;
    }
    //后置额外功能
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        Date date = new Date();
        SimpleDateFormat sd = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
        String da = sd.format(date);
        System.out.println("于"+da+"之后 切入额外功能...");
    }
    /**
     * 前置额外功能
     * 
     * @param method 当前执行的方法
     * @param args   当前执行的方法中的参数
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("before~~~");
    }
}

异常额外功能

public class MyThrows implements ThrowsAdvice{
    //目标业务方法中抛出异常时,执行此方法。ex=抛出的异常对象
    public void afterThrowing(Exception ex){
        System.out.println(ex.getMessage()+"~~~");
    }
}

3.4 编制Weave

所谓编织,即,将Target 和 Advice 组装 形成代理。
当然组装过程由spring管理,开发者只需要做出配置,告知spring需要组装谁即可。

<!-- 声明 Target + Advice -->
<!-- 声明 Target -->
<bean id="userService" class="com.zhj.service.UserServiceImpl">
    <!-- 为userDAO属性赋值,值为id=“userDAO”的组件 -->
    <property name="userDAO" ref="userDAO"/>
</bean>

<!-- Advice -->
<bean id="myBefore" class="com.zhj.advice.MyBeforeAdvice"/>

<!-- 编织 配置 -->
<aop:config>
         <!-- ref="引入MyAdvice" -->
          <aop:aspect ref="myAdvice">
        <!-- 切入点=pointcut
            execution()表达式:描述切入位置
            组成:修饰符  返回值  包  类  方法名  参数表-->
            
        <aop:pointcut id="pc" expression="execution(* com.service.UserServiceImpl.queryUser(..))"/>
     
        <aop:advisor advice-ref="myBefore" pointcut-ref="pc"/>
    </aop:aspect>
</aop:config>

3.5 切入点表达式

3.5.1 execution

1> * com.service.UserServiceImpl.queryUser(..)
    修饰符:任意
    返回值:任意
    包:com.service
    类:UserServiceImpl
    方法:queryUser
    参数表:任意
2> * com.service.UserServiceImpl.*(..)
    修饰符:任意
    返回值:任意
    包:com.service
    类:UserServiceImpl
    方法:所有,任意
    参数表:任意
3> * com..UserServiceImpl.*(..)
    修饰符:任意
    返回值:任意
    包:com包,及其子包
    类:UserServiceImpl
    方法:所有,任意
    参数表:任意
4> * com.service.*.*(..)
    修饰符:任意
    返回值:任意
    包:com.service
    类:所有,任意
    方法:所有,任意
    参数表:任意
5> * *(..)    不建议
    修饰符:任意
    返回值:任意
    包:任意
    类:所有,任意
    方法:所有,任意
    参数表:任意
6> * com.service.UserServiceImpl.query*(..)  【技巧:批量切入】
    修饰符:任意
    返回值:任意
    包:com.service
    类:UserServiceImpl
    方法:所有,任意
    参数表:任意
  *注意:尽量精确,避免不必要的切入

3.5.2 within

描述包和类,类中所有方法都切入,示例:

`within(com.service.UserServiceImpl) 类中的所有方法

within(com…UserServiceImpl) com包和com子包下的类中的所有方法

<aop:pointcut id=“pc” expression=“within(com…UserServiceImpl)”/>

3.5.3 args

描述参数表,符合的方法都切入。

args(int,String,com.entity.User) 参数表如此的方法

<aop:pointcut id=“pc” expression=“args(int,String,com.entity.User)”/>

4、SpringBoot AOP使用

SpringBoot AOP使用

参考文档:

  1. 基础:https://www.cnblogs.com/yulinfeng/p/7719128.html
  2. 应用场景:https://www.cnblogs.com/yulinfeng/p/7764600.html
  3. 动态代理:https://www.cnblogs.com/yulinfeng/p/7811965.html
上一篇 下一篇

猜你喜欢

热点阅读