认识Spring AOP

Spring AOP

AOP是Aspect/'æspekt/ Oriented/ɔːrɪentɪd/ Programming的缩写,意为:面向切面编程。

是什么

通过预编译方式和运行期动态代理实现程序功能的一种技术。

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

使用场景
Java是一个面向对象(OOP)的语言,但它有一些弊端,比如当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日志、权限验证、事务等功能时,只能在在每个对象里引用公共行为。这样做不便于维护,而且有大量重复代码。AOP的出现弥补了OOP的这点不足。

举个栗子1

package test.staticProxy;
 
// 接口
public interface IUserDao {
    void save();
}
 
//目标对象
class UserDao implements IUserDao{
    @Override
    public void save() {
        System.out.println("模拟:保存用户!");
    }
}
 
/**
  * 静态代理
  * 特点:
  * 2. 目标对象必须要实现接口
  * 2. 代理对象,要实现与目标对象一样的接口
 */
class UserDaoProxy implements IUserDao{
 
    // 代理对象,需要维护一个目标对象
    private IUserDao target = new UserDao();
 
    @Override
    public void save() {
        System.out.println("代理操作: 开启事务...");
        target.save();   // 执行目标对象的方法
        System.out.println("代理操作:提交事务...");
    }    
}
测试:
public static void main(String args[]){
    IUserDao proxy = new UserDaoProxy();
    proxy.save();
}
测试结果:
代理操作: 开启事务...
模拟:保存用户!
代理操作:提交事务...

以上是静态代理的例子,其中一个代理类只能服务一个对象,如果有大量需要被代理的对象,那么需要编写大量的代理类,并且被代理的对象增加方法则除了实现了增加之外代理类也要实现此方法,这样就增加了代码的维护成本。

举个栗子2

package test.dynamicProxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
 
// 接口
public interface IUserDao {
    void save();
}
 
//目标对象
class UserDao implements IUserDao{
 
    @Override
    public void save() {
        System.out.println("模拟: 保存用户!");
    }
}
 
/**
 * 动态代理:
 * 代理工厂,给多个目标对象生成代理对象!
 *
 */
class ProxyFactory {
 
    // 接收一个目标对象
    private Object target;
 
    public ProxyFactory(Object target) {
        this.target = target;
    }
 
    // 返回对目标对象(target)代理后的对象(proxy)
    public Object getProxyInstance() {
        Object proxy = Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // 目标对象使用的类加载器
            target.getClass().getInterfaces(),   // 目标对象实现的所有接口
            new InvocationHandler() {            // 执行代理对象方法时候触发
 
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
 
                    // 获取当前执行的方法的方法名
                    String methodName = method.getName();
                    // 方法返回值
                    Object result = null;
                    if ("save".equals(methodName)) {
                        System.out.println("开启事务...");
                        // 执行目标对象方法
                        result = method.invoke(target, args);
                        System.out.println("提交事务...");
                    }
                    return result;
                }
            }
        );
        return proxy;
    }
}
测试:
public static void main(String[] args){
  //创建目标对象
  IUserDao target = new UserDao();
  System.out.println("目标对象"+target.getClass());
  //代理对象
  IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
  System.out.println("代理对象"+proxy.getClass());
  //执行代理对象方法
  proxy.save();
}
测试结果:
目标对象:class test.dynamicProxy.UserDao
代理对象:class com.sun.proxy.$Proxy0
开启事务...
模拟:保存用户!
提交事务...

以上代码是基于JDK动态代理实现的,前提是目标类必须有实现的接口。

如果一个类没有实现接口,就不能使用JDK动态代理。而CGLIB代理可以弥补这个问题。

举个栗子3

CGLIB 是以动态生成的子类继承目标的方式实现,在运行期动态的在内存中构建一个子类,如下:

public class UserDao{}
 
// CGLIB 是以动态生成的子类继承目标的方式实现,程序执行时,隐藏了下面的过程
public class $Cglib_Proxy_class  extends UserDao{}

 前提是目标类不能是final修饰,因为final修改的类不能被继承。

总结:

面向切面编程的核心原理是使用动态代理模式在方法执行的前后或出现异常时加入相关逻辑。

 关键词 动态代理 方法级 动态关注点 切面关注点代码类

优秀的 Spring 框架把JDK 代理和 CGLIB 代理两种动态代理方式在底层都集成了进去,我们无需担心自己去实现动态生成代理。

通过查看 Spring 源码,我们在 DefaultAopProxyFactory 类中,找到这样一段话。

 

简单的从字面意思看出:如果有接口,则使用 JDK 代理,反之使用 CGLIB ,这刚好印证了前文所阐述的内容。Spring AOP 综合两种代理方式的使用前提有会如下结论:如果目标类没有实现接口,且 class 为 final 修饰的,则不能进行 Spring AOP 编程!

posted @ 2019-03-28 11:16  后知后觉,知耻而后勇  阅读(208)  评论(0)    收藏  举报