Java 代理模式和 AOP

Posted by icoding168 on 2020-03-25 19:25:31

分类: Java   设计模式  

代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,扩展目标对象的功能。

在 Java 中代理模式有静态代理和动态代理两种实现方式:

  • 静态代理:代理类在程序编译时生成
  • 动态代理:代理类在程序运行时生成

无论是静态代理还是动态代理,都可以基于 JDK 自带的类库实现,也可以基于第三方类库实现。

基于 JDK 实现的静态代理

public interface IUserDao {
    public void save();
}
public class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("保存数据");
    }
}
public class UserDaoProxy implements IUserDao{

    private IUserDao target;
    public UserDaoProxy(IUserDao target) {
        this.target = target;
    }
    
    @Override
    public void save() {
        System.out.println("开启事务");
        target.save();
        System.out.println("提交事务");
    }
}
import org.junit.Test;

public class StaticUserProxy {
    @Test
    public void testStaticProxy(){
        IUserDao target = new UserDao();
   
        UserDaoProxy proxy = new UserDaoProxy(target);
        proxy.save();
    }
}

基于 JDK 实现的动态代理

public interface IUserDao {
    public void save();
}
public class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("保存数据");
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

    private Object target;

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

    // 为目标对象生成代理对象
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开启事务");

                        // 执行目标对象方法
                        Object returnValue = method.invoke(target, args);

                        System.out.println("提交事务");
                        return null;
                    }
                });
    }
}
import org.junit.Test;

public class TestProxy {

    @Test
    public void testDynamicProxy (){
        IUserDao target = new UserDao();
        System.out.println(target.getClass());  
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println(proxy.getClass());  
        proxy.save();  
    }
}

基于 JDK 实现的静态代理其实跟平常写代码没什么不同,无非就是在目标类的基础上再加一层封装,将额外的代码逻辑加上去。JDK 静态代理无法自动化批量修改目标类,需要更多的人工介入。

基于 JDK 实现的动态代理是运用了 Java 的反射,对目标类的字节码进行修改后生成一个新的类。JDK 动态代理可以运用反射来解析方法名或注解,从而能够自动化批量修改目标类,更智能更便利。可以说, JDK 静态代理相当于静态网页,而 JDK 动态代理相当于动态网页,哪一个功能更强大我想大家都很清楚。

AOP

面向切面编程(Aspect-oriented programming,AOP)是计算机科学中的一种程序设计思想,是面向对象编程(OOP)的延伸。AOP 可以让程序员在不影响原有功能的前提下扩展功能,也可以将一些功能代码从业务逻辑代码中分离出来。也就是说,AOP 可以提高代码的模块化程度,降低代码之间的耦合度。AOP 的应用场景有日志记录,性能统计,安全控制,事务处理,异常处理等等。

AOP 在 Java 中的实现方式有 JDK 静态代理、JDK 动态代理、AspectJ 静态代理、Cglib 动态代理等。AspectJ 意思就是 Java的 Aspect,它是一个代码编译器,在 Java 编译器的基础上增加了一些它自己的关键字识别和编译方法。Cglib 是一个可以修改字节码的 Java 类库,JDK 动态代理必须在目标类实现了接口的情况下才能进行动态代理,Cglib 则没有这个限制。

Spring AOP

Spring AOP 采用了 JDK 动态代理和 Cglib 动态代理两种方式实现 AOP,在初始化 IoC 容器的过程中对业务方法进行增强,具体使用哪种方式,Spring 会对做如下判断:

  • 如果目标对象实现了接口,默认情况下会采用 JDK 动态代理实现 AOP
  • 如果目标对象实现了接口,程序员可以配置强制使用 Cglib 实现 AOP
  • 如果目标对象没有实现了接口,程序员也没有配置使用 Cglib,Spring 会自动在 JDK 动态代理和 Cglib 之间转换

Spring AOP 在基于注解配置的情况下,需要依赖于 AspectJ 提供的标准注解,而基于 XML 配置 的情况下则不需要依赖 AspectJ。因此 Spring AOP 只是复用了AspectJ 的注解,并没有其他依赖 AspectJ 的地方。