Bootstrap

Spring自学日志02-AOP(面向切面编程)

一、概述

1.1、AOP诞生的背景

AOP的诞生和面向对象编程(OOP)的特点密不可分。

这必须要讲面向过程编程(POP),代表的语言是 C++、C 。它是以功能为核心的思考和组织编码的,着重强调的是数据的处理过程

向对象编程(OOP)则是强调整体性的概念,将数据和方法封装到一个类中。以封装对的对象为核心。对象的的内部和外部有明确的区分

把程序比喻为一间房子,内有家具如马桶、浴缸,床、桌子等。

对于面向过程的程序设计更注重的是功能的实现(即功能方法的实现),功能符合预期就好更倾向图1。现在房子的功能已经齐全可以入住了。
但房子内的家具是相互暴露的。卫生间和卧室是一体的不就等于在卫生间睡觉吗?对于面向对象的程序设计这是无法忍受的所以便采用了图2,房子中的每个房间都有各自的名称相应功能对外接口。同时也更有利于后期的拓展了,毕竟哪个房间需要添加那些功能,其范围也有了限制,也就使职责更加明确了(单一责任原则)。

OOP的出现对POP确实存在很多颠覆性的,一切皆对象的思路更接近人们对客观事物的认识,同时把数据和方法(算法)封装在一个类(对象)中,这样更有利于数据的安全,一般情况下属性和算法只单独属于某个类,从而使程序设计更简单,也更易于维护。

但POP并没有被淘汰。POPOOP对应事物的两个方面–局部(POP)整体(OOP),因此在实际应用中,两者都同样重要。

但是随着软件开发的系统越来越复杂,工程师认识到,传统的OOP程序经常表现出一些不自然的现象,核心业务中总掺杂着一些不相关联的特殊业务,如日志记录,权限验证,事务控制,性能检测,错误信息检测等等,这些特殊业务可以说和核心业务没有根本上的关联而且核心业务也不关心它们,比如在用户管理模块中,该模块本身只关心与用户相关的业务信息处理,至于其他的业务完全可以不理会,我们看一个简单例子协助理解这个问题

public interface IUserService {
    void saveUser();
}
//实现类
public class UserServiceImpl implements IUserService {
    
    //核心数据成员
    private String aaaa;
    //日志操作对象
    private String bbbb;
    //权限管理对象
    private String cccc;
    //事务控制对象
    private String dddd;
    
    @Override
    public void saveUser() {
        //权限验证(假设权限验证丢在这里)
        aaaa.xxxx(A,B);
        //事务控制
        bbbb.xxxx(A,B,C);
        //日志操作
        cccc.xxxx(B);
        //进行Dao层操作
        userDao.saveUser();
    }
}

上述代码中我们注意到一些问题,权限,日志,事务都不是用户管理的核心业务,也就是说用户管理模块除了要处理自身的核心业务外,还需要处理权限,日志,事务等待这些杂七杂八的不相干业务的外围操作,而且这些外围操作同样会在其他业务模块中出现,这样就会造成如下问题

  1. 代码耦合严重:核心业务模块可能需要兼顾处理其他不相干的业务外围操作,这些外围操作可能会混乱核心操作的代码,而且当外围模块有重大修改时也会影响到核心模块,这显然是不合理的
  2. 代码分散和冗余:同样的功能代码,在其他的模块几乎随处可见,导致代码分散并且冗余度高。
  3. 代码质量低难:由于不太相关的业务代码混杂在一起,无法专注核心业务代码,当进行类似无关业务扩展时又会直接涉及到核心业务的代码,导致拓展性低。

事实上我们知道诸如日志,权限,事务,性能监测等业务几乎涉及到了所有的核心模块,如果把这些特殊的业务代码直接到核心业务模块的代码中就会造成上述的问题,而工程师更希望的是这些模块可以实现热插拔特性而且无需把外围的代码入侵到核心模块中。这样外围业务可以看作单独的功能。召之即来挥之即去!

这种形式相当下图:

img

这种基于AOP的扩展有也被称为横向扩展

当然也有纵向扩展。什么是纵向扩展

1.2、纵向扩展

通过继承抽取通用代码的方式称为纵向拓展

形如

img

//通用父类
public class Dparent {
    public void commond(){
        //通用代码
    }
}
//A 继承 Dparent 
public class A extends Dparent {
    public void executeA(){
        //其他业务操作省略......
        commond();
    }
}
//B 继承 Dparent 
public class B extends Dparent{
    public void executeB(){
        //其他业务操作省略......
        commond();
    }
}
//C 继承 Dparent 
public class C extends Dparent{
    public void executeC(){
        //其他业务操作省略......
        commond();
    }
}

显然代码冗余也得到了解决。

二、Spring实现AOP

2.1、Spring集成AspectJ-AOP

添加依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
</dependency>

配置文件

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="com.User"></context:component-scan>
    <!-- 启动@aspectj的自动代理支持-->
    <aop:aspectj-autoproxy />
    <aop:config proxy-target-class="true" />
    <!--无参构造创对象
    类型 变量名 = new 类型();
    Hello hello =new Hello();
    id = 变量名
    class = new 的对象
    property相当于给对象中的属性设值!
    -->
    <bean id="userImpl" class="com.User.UserImpl">
        <property name="name" value="陈声铭"/>
    </bean>

<!--    &lt;!&ndash; 定义aspect类 &ndash;&gt;-->
<!--    <bean name="myAspectJ" class="com.User.MyAspect"/>-->
</beans>

定义目标类接口和实现类

//接口
public interface User {

    int addUser();

    void updateUser();

    void deleteUser();

    void findUser();

}
//实现类
@Component
public class UserImpl implements User{
    private String name;

    public UserImpl(String name) {
        System.out.println("有参构造");
        this.name = name;
    }

    public UserImpl() {
        System.out.println("无参构造");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("Set函数");
        this.name = name;
    }

    public int addUser() {
        System.out.println("add user ......");
        return 6666;
    }

    public void updateUser() {
        System.out.println("update user ......");
    }

    public void deleteUser() {
        System.out.println("delete user ......");
    }

    public void findUser() {
        System.out.println("find user ......");
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

//切面
@Component
@Aspect//该注解标记类为切面
public class MyAspect {
    /**
     * 前置通知
     */
    @Before(value = "execution(* com.User.UserImpl.addUser(..))")
    public void before() {
        System.out.println("前置通知....");
    }

    /**
     * 后置通知
     * returnVal,切点方法执行后的返回值
     */
    @AfterReturning(value = "execution(* com.User.UserImpl.addUser(..))", returning = "returnVal")
    public void AfterReturning(Object returnVal) {
        System.out.println("后置通知...." + returnVal);
    }


    /**
     * 环绕通知
     *
     * @param joinPoint 可用于执行切点的类
     * @return
     * @throws Throwable
     */
    @Around(value = "execution(* com.User.UserImpl.addUser(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前....");
        Object obj = (Object) joinPoint.proceed();
        System.out.println("环绕通知中:" + obj);
        System.out.println("环绕通知后....");
        return obj;
    }

    /**
     * 抛出通知
     *
     * @param e
     */
    @AfterThrowing(value = "execution(* com.User.UserImpl.addUser(..))", throwing = "e")
    public void afterThrowable(Throwable e) {
        System.out.println("出现异常:msg=" + e.getMessage());
    }

    /**
     * 无论什么情况下都会执行的方法
     */
    @After(value = "execution(* com.User.UserImpl.addUser(..))")
    public void after() {
        System.out.println("最终通知....");
    }
}

//测试类
public class MyTest {

    public static void main(String[] args) {
//        User user = (User) ObjectFactory.getObject("User");
//        System.out.println(user);
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/beans.xml");
        UserImpl userImpl=applicationContext.getBean("userImpl",UserImpl.class);
        userImpl.addUser();
        System.out.println(userImpl);

    }
}

结果

;