动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。
一 Cglib
Cglib是一个开源项目,它的底层是字节码处理框架ASM,提供了比jdk更为强大的动态代理。相比于jdk动态代理的优势有:
- jdk动态代理只能基于接口,代理对象只能是接口类型,而Cglib通过生成子类来实现的,代理对象既可以是实现类,又可以是接口。
- Cglib速度比jdk动态代理更快,性能更好。
Spring对Cglib做了封装,提供了spring-cglib-repack.jar包。
java
public class UserService {
public void test() {
System.out.println("执行UserService.test");
}
java
import com.xiakexing.service.UserService;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class UserServiceProxy {
public static void main(String[] args) {
UserService target = new UserService();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
// 定义代理逻辑
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] params, MethodProxy
methodProxy) throws Throwable {
System.out.println("before...");
Object result = methodProxy.invoke(target, params);
System.out.println("after...");
return result;
}
}});
// 创建代理对象
UserService userService = (UserService) enhancer.create();
userService.test();
}
}
二 JDK动态代理
大家或多或少都有了解,此处不在赘述。
三 ProxyFactory类
对上述两种代理实现,Spring进行了封装,统一为ProxyFactory,表示创建代理对象的一个工厂。
我们不用关心底层使用了cglib还是JDK动态代理,ProxyFactory会自动判断:
- 如果被代理类实现了接口,就会用jdk动态代理;
- 如果没有实现接口,就会用cglib技术。
3.1 使用Cglib
java
package com.xiakexing.aop;
import com.xiakexing.service.UserService;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
public class ProxyFactoryTest {
public static void main(String[] args) {
UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
});
UserService userService = (UserService) proxyFactory.getProxy();
userService.test();
}
}
上面示例中,没有给UserService指定接口实现, 因此使用了Cglib代理。
3.2 使用JDK代理
定义OrderInterface接口,提供实现类OrderService。
java
public interface OrderInterface {
void test();
}
java
public class OrderService implements OrderInterface {
public void test() {
System.out.println("执行OrderService.test");
}
}
java
package com.xiakexing.aop;
import com.xiakexing.service.OrderInterface;
import com.xiakexing.service.OrderService;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
public class ProxyFactoryTest {
public static void main(String[] args) {
OrderInterface target = new OrderService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
// 添加要实现的接口
proxyFactory.addInterface(OrderInterface.class);
proxyFactory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
});
OrderInterface orderService = (OrderInterface) proxyFactory.getProxy();
System.out.println(orderService.getClass().getName());
orderService.test();
}
}
当主动指定代理对象要实现OrderInterface接口时,将使用JDK动态代理。
四 ProxyFactoryBean类
我们希望能够从Spring容器中,直接获取ProxyFactory所创建的代理对象,就要使用ProxyFactoryBean。
该类实现了FactoryBean接口,用于创建Proxy对象,需要提供被代理类和代理逻辑,getObject()会返回代理类Bean对象。
java
// 返回UserService的代理对象
@Bean
public ProxyFactoryBean userServiceProxy() {
UserService userService = new UserService();
ProxyFactoryBean factoryBean = new ProxyFactoryBean();
factoryBean.setTarget(userService);
factoryBean.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
});
return factoryBean;
}
五 一些概念
5.1 Advice通知
在已有方法的前、后、异常或最终处加入的增加逻辑,包括:
- Before Advice:方法之前执行
- After returning advice:方法return后执行
- After throwing advice:方法抛异常后执行
- After finally advice:方法执行完finally之后执行,在return之后
- Around advice:这是功能最强大的Advice,可以自定义执行顺序
5.2 Pointcut切面点
需要应用Advice的方法范围,指定对哪些类的哪些方法进行增强。
5.3 Advisor接口
一个Advisor由一个Pointcut和一个Advice组成。子类PointcutAdvisor有如下方法。
如下,创建UserService的代理对象,只对名称为test的方法进行增强。
java
UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisor(new PointcutAdvisor() {
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("test");
}
};
}
@Override
public Advice getAdvice() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
};
}
@Override
public boolean isPerInstance() {
return false;
}
});
UserService userService = (UserService) proxyFactory.getProxy();
userService.test();
六 使用注解
上面示例中,我们自行指定了要代理的类,自行创建了Advisor对象。Spring中代理肯定不能如此麻烦的实现。
使用注解,将Advice、Pointcut都定义为Bean放入容器中,Spring就具备了动态生成代理对象的能力了。
java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class SimpleAspect {
@Before("execution(public void com.xiakexing.service.UserService.test())")
public void before(JoinPoint joinPoint) {
System.out.println("Before...");
}
}
OOP表示面向对象编程,AOP表示面向切面编程,都只是一种编程思想。而Spring提供了一套机制,可以让我们更加容易的实现AOP编程,这套机制我们称之为 Spring AOP。
通过注解方式来定义Pointcut和Advice,首创是AspectJ组件。Spring依赖了AspectJ,会去解析相应注解,利用动态代理机制生成代理对象。