Spring AOP基础----动态代理

337 阅读5分钟

AOP含义

AOP即面向切面编程(Aspect Oriented Program),将那些与业务无关、但是却为业务模块所共同调用的逻辑(日志、事务、异常处理等)封装起来,便于减少重复代码,降低模块间耦合度,提升系统的可扩展性和可维护性。

AOP实现

AOP实现的核心是:在目标类的基础上增加切面逻辑,生成增强后的代理类。

AOP代理可以分为静态代理动态代理

  • 静态代理在编译期就生成代理类。
  • 动态代理则是在运行期生成代理类。

动态代理又可以分为两种主要实现方式:JDK动态代理CGLIB动态代理

  • JDK动态代理是通过Java反射机制来实现,需要目标类实现一个接口
  • CGLIB动态代理通过修改字节码,来生成目标类的子类,通过继承的方式对目标类型进行增强。 Spring AOP默认采用JDK动态代理(要求目标类实现接口),当目标类未实现接口时会采用CGLIB动态代理。也可以在配置文件中指定使用CGLIB动态代理。

JDK动态代理实现示例

JDK动态代理的核心类是InvocationHandlerProxy,实现主要步骤可以分为3步:

  • 实现InvocationHandler接口,并在其中invoke方法实现对目标对象的代理;
  • 使用Proxy.newProxyInstance()创建代理对象;
  • 将代理对象转成接口类型,并调用其中方法。

(1)准备目标类和接口

创建接口:

public interface HelloService {
    void sayHello();
}

创建目标类实现接口

public class HelloServiceImpl implements HelloService {

    private Integer id = 0;

    public HelloServiceImpl() {
    }

    public HelloServiceImpl(int id) {
        this.id = id;
    }

    public void sayHello() {
        System.out.println(id + "    Hello, world!");
    }
}

(2)实现InvocationHandler接口,并实现代理方法

要求构造器传入目标对象,并在invoke()方法中应该对目标对象方法进行增强。

public class JdkProxyFactory implements InvocationHandler {

    private Object targetObject;

    public JdkProxyFactory(Object targetObject) {
        this.targetObject = targetObject;
    }

    // proxy表示当前代理对象,调用proxy的方法会造成无限循环代理,应该该调用原对象的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Jdk proxy, 该方法被代理了,这里是前序工作");
        Object result = method.invoke(targetObject, args);
        // 调用代理对象方法是错误的
        // Object result = method.invoke(proxy, args);
        System.out.println("Jdk proxy, 该方法被代理了,这里是后序工作");
        return result;
    }
}

(3)使用Proxy创建代理对象,并调用方法

JdkProxyFactory jdkProxyFactory = new JdkProxyFactory(new HelloServiceImpl(3));

/** Proxy.newProxyInstance()需要传入3个参数。生成代理对象的过程也可以直接封装在调用处理实现类中。
 * 参数1:类加载器,该参数与原对象、代理对象等都无关,如下甚至可以使用线程对象的类加载器,不过一般建议使用原对象的类加载器
 * 参数2:代理类要实现的接口数组
 * 参数3:InvocationHandler接口实现类对象
 */
HelloService helloService1 = (HelloService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
        new Class[] {HelloService.class}, jdkProxyFactory);
helloService1.sayHello();

上面示例代码中将创建代理对象的过程放在JdkProxyFactory类外部,也可以将该逻辑封装到JdkProxyFactory内部,调用方不关心创建代理对象内部逻辑。 在JdkProxyFactory中增加获取代理对象的方法:

public Object getProxyInstance() {
      return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
}

此时外部调用逻辑如下:

HelloService helloService2 = (HelloService) jdkProxyFactory.getProxyInstance();
helloService2.sayHello();

CGLIB动态代理实现

CGLIB动态代理实现核心类是MethodInterceptorEnhancer,其实现步骤也可以分为主要3步:

  • 实现MethodInterceptor接口,并在intercept()方法中实现对目标类的增强。
  • 使用Enhancer创建代理对象。
  • 将代理对象转成目标类型,并调用其中方法。

(0)引入相应库

可能需要引入Spring AOP相应maven库,这里由于直接创建了Springboot程序,省略这一步。

(1)实现MethodInterceptor接口,并实现代理方法

要求构造器传入目标对象,并在intercept()方法中对目标对象的方法进行增强。

public class CglibProxyFactory implements MethodInterceptor {

    private Object targetObject;

    public CglibProxyFactory(Object obj) {
        this.targetObject = obj;
    }

    // object是被代理对象(子类对象),method是原方法,methodProxy是代理方法
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib proxy, 该方法被代理了,这里是前序工作");
        method.invoke(targetObject, args);
        // 这是使用子类对象调用父类的方法,生成的代理对象其实是一个新的对象,没有原来对象的属性,所以这种方式是错误的
        // methodProxy.invokeSuper(object, args);
        System.out.println("Cglib proxy, 该方法被代理了,这里是后序工作");
        return null;
    }

}

(2)使用Enhancer创建代理对象,并调用方法

CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(new HelloServiceImpl(3));

// cglib动态代理是生成了目标类的一个子类,所以Enhancer需要设置父类型,同时需要设置MethodInterceptor实现类对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloServiceImpl.class);
enhancer.setCallback(cglibProxyFactory);

HelloServiceImpl helloService1 = (HelloServiceImpl) enhancer.create();
helloService1.sayHello();

同样上面示例代码中将创建代理对象的过程放在CglibProxyFactory类外部,也可以将该逻辑封装到CglibProxyFactory内部。 在CglibProxyFactory中增加获取代理对象的方法:

public Object getProxyInstance() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(targetObject.getClass());
    //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
    enhancer.setCallback(this);
    //增强后的目标类
    Object proxyObj = enhancer.create();
    // 返回代理对象
    return proxyObj;
}

外部调用逻辑:

HelloServiceImpl helloService2 = (HelloServiceImpl) cglibProxyFactory.getProxyInstance();
      helloService2.sayHello();

总结

Spring AOP实现的核心机制是动态代理,实现方式可以分为JDK动态代理和CGLIB动态代理。

  • JDK动态代理基于反射,要求目标类必须实现接口;
  • CGLIB动态代理基于继承,要求目标类不能被final修饰、可以被继承。