Spring AOP 01_初始AOP 及两种动态代理方法


一、Spring AOP简介

AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。

在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。

为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。

类与切面的关系


AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多的关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。

AOP术语

Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。
Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

二、动态代理

  1. JDK动态代理
    JDK动态代理是通过java.lang.reflect.Proxy 类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。

案例

pom.xml
<?xml version="1.0" encoding="UTF-8"?>

    4.0.0

    org.example
    Spring_AOP
    1.0-SNAPSHOT

    
        8
        8
    

    
        
            junit
            junit
            4.12
            test
        
        
            mysql
            mysql-connector-java
            8.0.26
        
        
            org.mybatis
            mybatis
            3.5.9
        
        
            org.springframework
            spring-core
            5.0.5.RELEASE
        
        
            org.springframework
            spring-beans
            5.0.5.RELEASE
        
        
            org.springframework
            spring-context
            5.0.5.RELEASE
        
        
            org.springframework
            spring-jdbc
            5.0.5.RELEASE
        
        
            org.springframework
            spring-tx
            5.0.5.RELEASE
        
        
            org.springframework
            spring-test
            5.0.5.RELEASE
            test
        

        
            javax.servlet
            javax.servlet-api
            3.1.0
            provided
        
        
            org.mybatis
            mybatis-spring
            2.0.7
        
        
            com.fasterxml.jackson.core
            jackson-databind
            2.10.3
        
        
            org.junit.jupiter
            junit-jupiter
            RELEASE
            compile
        
    

一、静态代理

1. 构造函数方法给代理对象赋值
接口类UserDao
public interface UserDao {
    public void addUser();
    public void deleteUser();
}
实现类UserDaoImpl
import sse.dao.UserDao;

public class UserDaoImpl implements UserDao {
    @Override
    public void addUser() {
        System.out.println("add user!");
    }

    @Override
    public void deleteUser() {
        System.out.println("delete user!");
    }
}
代理类UserDaoProxy
import sse.dao.UserDao;
import sse.dao.impl.UserDaoImpl;

//模拟日志
public class UserDaoProxy implements UserDao {

    private  UserDao target;

    public UserDaoProxy(UserDao target) {  //构造函数方法给代理对象赋值
        this.target = target;
    }

    @Override
    public void addUser() {
        System.out.println("开始添加用户");
        target.addUser();
        System.out.println("完成添加用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("开始删除用户");
        target.deleteUser();
        System.out.println("完成删除用户");
    }
}
测试

2. set方法
接口类BookDao
public interface BookDao {
    void addBook();
}
实现接口的类BookDaoImpl
import sse.dao.BookDao;

public class BookDaoImpl implements BookDao {
    @Override
    public void addBook() {
        System.out.println("添加书籍");
    }
}
静态代理BookDaoProxy
import sse.dao.BookDao;

public class BookDaoProxy implements BookDao {

    private BookDao target;

    public void setTarget(BookDao target) { //set方法给代理对象赋值
        this.target = target;
    }

    @Override
    public void addBook() {
        System.out.println("开始添加书籍");
        target.addBook();
        System.out.println("完成添加书籍");
    }
}
测试

二、jdk动态代理

动态代理类JdkProxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//通用代理类
public class JdkProxy implements InvocationHandler {
    private  Object target;//通用代理类,既可以代理UserDao,也可以代理BookDao

    public JdkProxy(Object target) {
        this.target = target;
    }

    /*
*   invoke方法在反射机制中用到,它可以根据方法名动态地调用不同的方法,有可能调用addUser(),也有可能调用addBook(),我们把它们组合到invoke()方法里面
*    在invoke方法里我们通过参数调用实际的方法(具体的方法是通过参数传进来的)
*    通过method.invoke()传进来的有可能是addUser(),也可能是addBook(),方法里面有两个参数,一个是调用的target,一个是其参数
* * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println(method.getName()+"被调用前");
        Object ret = method.invoke(target,args);
        System.out.println(method.getName()+"被调用后");

        return ret;

    }
}
  • 测试1

  • 测试2