Spring AOP实战和源码解析
- Spring AOP简介
说起Spring框架,我们最先想到的就是IOC和AOP了,如果说IOC是Spring的核心,那AOP就是Spring最重要的功能之一,AOP,即Aspect Oriented Programming,面向切面编程。我们实际项目中,业务除了像数据库的增删改查等核心功能之外,还有一些业务逻辑相同的切面功能,如日志打印和性能统计等。AOP的目的是将业务中共同调用的逻辑封装起来,减少系统中的重复代码,降低模块间的耦合度,提高代码的可扩展性和可维护性。
- Spring AOP 基本术语
1)连接点(Joinpoint)
一个类或者一段代码中具有边界性质的特定点,如方法调用前后,抛出异常后或者某个类初始化之前和初始化之后等,是客观存在的边界点;
2)切点(Pointcut)
切点是某些我们感兴趣的连接点,通过切点来定位特定的连接点,可以将连接点和切点的关系看做数据库中的记录和查询条件的关系,一个查询条件可以对应多条记录,一个切点也可以对应多个连接点。在Spring中切点用Pointcut接口来描述;
3)增强(Advice)
增强是织入目标类连接点上的一段程序代码,Spring中增强除了程序代码之外还有一段和连接点相关的方位信息,如Spring提供的BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等;
4)目标对象(Target)
增强逻辑织入的目标类;
5)引介(Introduction)
引介是一种特殊的增强,表示为类添加一些属性或者方法,即使一个类没有实现某个接口,也能通过引介为这个类添加该接口的实现逻辑;
6)织入(Weaving)
将增强添加到目标类连接点上的过程,织入分为编译期织入、类装载期织入和动态代理织入,Spring AOP选择动态代理织入,在运行期为目标类添加增强生成子类的方式,AspectJ选择编译期织入和类装载期间织入,前者需要特殊的编译器,后者需要特殊的类装载器;
7)代理(Proxy)
一个类被AOP增强之后,生成一个结果类,这个类既有目标类的业务逻辑也有增强的逻辑;
8)切面(Aspect)
切面由切点和增强组成,包括横切逻辑的定义也包括特定连接点的定义。
使用Spring AOP实现一个环绕通知,在方法执行前后打印日志
- 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAnnotation {
}
- 目标方法
@Component
public class UserService {
@LogAnnotation
public String getSomething() {
return "some thing";
}
}
- 切面
@Aspect
@Component
@Slf4j
public class LogAspect {
@Around("@annotation(liuxin.kkssyy.annotation.LogAnnotation)")
public Object aroundOperateLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Signature signature = proceedingJoinPoint.getSignature();
if (!(signature instanceof MethodSignature)) {
return new Object();
}
MethodSignature methodSignature = (MethodSignature) signature;
Method targetMethod = methodSignature.getMethod();
targetMethod.setAccessible(true);
Object resVal = null;
log.info("方法执行前,打印执行的方法名,方法名:[{}]", targetMethod.getName());
try {
resVal = proceedingJoinPoint.proceed();
log.info("方法执行成功,打印执行结果,结果为:[{}]", resVal.toString());
} catch (Throwable throwable) {
log.error("方法执行失败", throwable.toString());
}
return resVal;
}
}
- 方法调用
@Component
public class Client {
@Autowired
UserService userService;
@PostConstruct
public void run() {
userService.getSomething();
}
}
- 输出结果
2020-05-18 16:58:23.680 INFO 8940 --- [ main] liuxin.kkssyy.aop.LogAspect : 方法执行前,打印执行的方法名,方法名:[getSomething]
2020-05-18 16:58:23.684 INFO 8940 --- [ main] liuxin.kkssyy.aop.LogAspect : 方法执行成功,打印执行结果,结果为:[some thing]
Spring AOP动态代理的过程为:
创建AnnotationAwareAspectJAutoProxyCreator对象;
扫描容器中的切面;
创建PointcutAdvisor对象生成代理类
在AbstractAutoProxyCreator类中实现BeanPostProcessor中的下面方法中
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//核心方法
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
核心方法为wrapIfNeccessary,其处理过程为:
- 如果已经处理过,且该bean没有被代理过,则直接返回该bean;
- 如果该bean是内部基础设置类Class 或 配置了该bean不需要代理,则直接返回bean(返回前标记该bean已被处理过);
- 获取所有适合该bean的增强Advisor如果增强不为null,则为该bean创建代理对象,并返回结果,标记该bean已经被处理过
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
AbstractAutoProxyCreator中的createProxy方法中
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
//加入增强器
proxyFactory.addAdvisors(advisors);
//设置要代理的类
proxyFactory.setTargetSource(targetSource);
//定制代理
customizeProxyFactory(proxyFactory);
//用来控制代理工厂被设置后是否还允许修改通知,缺省值为false
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
通过ProxyFactory获取Proxy,方法如下
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
可以看到进一步通过AopProxy类的getProxy获取代理对象,
Object getProxy(ClassLoader classLoader);方法有两个实现,即JdkDynamicAopProxy和CglibAopProxy,那么究选择jdk的动态代理还是cglib的动态代理呢?是在DefaultAopProxyFactory类中的createAopProxy方法中处理的
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
前面介绍过AOP的底层是通过动态代理来实现的,动态代理包括基于JDK的动态代理和CGlib的动态代理,JDK的动态代理的两个核心点是:JDK动态代理是通过继承InvocationHandler接口,通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvacationHandler h)来获取代理对象;从newProxyInstance的方法签名可以看出来,JDK动态代理对象必须实现某一个接口,否则不能通过JDK动态代理来进行增强,下面通过一个简单的例子进行介绍。有一个EmployeeService的接口,接口中定义了一个方法showName(String),打印员工的姓名,现在我们需要在打印员工姓名之前展示公司的信息。
EmployeeService接口
public interface EmployeeService {
void showName(String name);
}
EmployeeServiceImpl
public class EmployeeServiceImpl implements EmployeeService{
@Override
public void showName(String name) {
System.out.println(name);
}
}
EmployeeServiceHandler
public class EmployeeServiceHandler implements InvocationHandler {
private Object target;
public EmployeeServiceHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("the name of the worker is: ");
Object result = method.invoke(target, args);
return result;
}
}
EmployeeServiceClient
public class EmployeeServiceClient {
public static void main(String[] args) {
EmployeeService employeeService = new EmployeeServiceImpl();
EmployeeServiceHandler employeeServiceHandler = new EmployeeServiceHandler(employeeService);
EmployeeService employeeService1 = (EmployeeService) Proxy.newProxyInstance(employeeService.getClass().getClassLoader(),employeeService.getClass().getInterfaces(), employeeServiceHandler);
employeeService1.showName("Tom");
//以下为尝试用另外一种思路实现动态代理,目前还没成功
Constructor cons = employeeServiceHandler .getClass().getConstructor(constructorParams);
EmployeeService proxy = (EmployeeService ) cons.newInstance(new Object[]{employeeServiceHandler });
}
}
JDK动态代理的原理就是实现InvocationHandler接口,通过Proxy.newProxyInstance方法生成代理对象,jdk动态代理只能代理接口的原因就在这里,该方法的第二个参数需要传入接口数组。该方法如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//最重要的一行
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
CGlib的动态代理实例如下
继承MethodInterceptor
public class EmployeeServiceImplInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======插入前置通知======");
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("======插入后者通知======");
return object;
}
}
调用实例代码如下:
public class CGLibClient {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(EmployeeServiceImpl .class);
enhancer.setCallback(new EmployeeServiceImplInterceptor ());
EmployeeServiceImpl employeeServiceImpl = (EmployeeServiceImpl )enhancer.create();
employeeServiceImpl ..showName("Tom");
}
}