目录
一、spring aop简介与应用
1. Spring AOP的几个概念
- 切面(Aspect):切面就是一个关注点的模块化,如事务管理、日志管理、权限管理等;
- 连接点(Joinpoint):程序执行时的某个特定的点,在Spring中就是一个方法的执行;
- 增强(Advice):增强就是在切面的某个连接点上执行的操作,也就是事务管理、日志管理等;
- 切入点(Pointcut):切入点就是描述某一类选定的连接点,也就是指定某一类要织入通知的方法;
- 织入(Weaving):将Aspect加入到程序代码的过程,对于SpringAOP,由ProxyFactory或者ProxyFactoryBean负责织入动作。
- 目标对象(Target):就是被AOP动态代理的目标对象;
2. 增强类型
- 前置增强(@Before):在目标方法执行前实施增强。
- 后置增强(@AfterReturning,@After):AfterReturning在目标方法正常执行后实施增强,如果方法抛出异常不会执行;@After增强的方法则无论是否有异常都会执行,类似finally方法。类似于finally代码块,只要应用了无论什么情况下都会执行
- 环绕增强(@Around):在目标方法执行前后实施增强。直接使用了AOP联盟定义的接口。
- 异常抛出增强(@AfterThrowing):在目标方法抛出异常后实施增强。
- 引介增强():在目标类中添加一些新的方法和属性,连接点是类级别的而非方法级别的。
3. 切点表达式
内容项 | 示例 |
任意公共方法的执行 | execution(public * *(..)) |
任何一个以“set”开始的方法的执行 | execution(* set*(..)) |
DemoService接口的任意方法的执行 | execution(* com.demo.service.DemoService.*(..)) |
定义在service包里的任意方法的执行 | execution(* com.demo.service..(..)) |
定义在service包和所有子包里的任意类的任意方法的执行 | execution(* com.demo.service...(..)) |
定义在demo包和所有子包里的DemoDao类的任意方法的执行 | execution(* com.demo..DemoDao.*(..)) |
二、动态代理介绍
1. jdk动态代理
JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类
// 接口
public interface InterfaceBean {
String sayHello(String something);
}
// 实现
public class InterfaceBeanImpl implements InterfaceBean {
@Override
public String sayHello(String something) {
System.out.println("hello " + something);
return "OK";
}
}
// Jdk动态代理
public class JdkDynamicProxy implements InvocationHandler {
private InterfaceBean target;
public JdkDynamicProxy(InterfaceBean target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("sayHello".equals(method.getName())) {
Logger logger = LoggerFactory.getLogger(JdkDynamicProxy.class);
logger.info("jdk dynamic proxy is applyed");
}
return method.invoke(target, args);
}
}
// 测试类
public class ProxyTest {
@Test
public void testJdkProxy() {
InterfaceBean bean = (InterfaceBean) Proxy.newProxyInstance(
InterfaceBean.class.getClassLoader(),
new Class<?>[] {InterfaceBean.class},
new JdkDynamicProxy(new InterfaceBeanImpl())
);
bean.sayHello("double six six six");
}
}
上述代码的关键是Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)
方法,该方法会根据指定的参数动态创建代理对象。三个参数的意义如下:
loader
,指定代理对象的类加载器;
interfaces
,代理对象需要实现的接口,可以同时指定多个接口;
handler
,方法调用的实际处理者,代理对象的方法调用都会转发到这里(*注意1)。
newProxyInstance()
会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()
方法。
2. cglib动态代理
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,诸如private的方法也是不可以作为切面的。
// 目标类
public class ClassBean {
public String print(String something) {
return something;
}
}
// cglib代理
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Logger logger = LoggerFactory.getLogger(CglibProxy.class);
logger.info("cglib proxy is applyed");
return proxy.invokeSuper(obj, args);
}
}
// 测试类
public class ProxyTest {
@Test
public void testCglibProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ClassBean.class);
enhancer.setCallback(new CglibProxy());
ClassBean bean = (ClassBean)enhancer.create();
bean.print("double six six six");
}
}
上述代码中,我们通过CGLIB的Enhancer
来指定要代理的目标对象、实际处理代理逻辑的对象,最终通过调用create()
方法得到代理对象,对这个对象所有非final方法的调用都会转发给MethodInterceptor.intercept()
方法,在intercept()
方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;通过调用MethodProxy.invokeSuper()
方法,我们将调用转发给原始对象。
3. 总结
Jdk动态代理获取的对象,实际是Proxy子类,且实现了代理类接口;
Cglib动态代理获取的代理对象,是代理类的子类,
三、简单源码分析
1. 解析增强
ReflectiveAspectJAdvisorFactory.getAdvice()
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}
AbstractAspectJAdvice springAdvice;
switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
break;
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method " + candidateAdviceMethod);
}
......
}
2. 生成代理
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);
}
}
JdkDynamicAopProxy.getProxy()
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
3. 执行增强方法
JdkDynamicAopProxy.invoke()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
// Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
CglibAopProxy->DynamicAdvisedInterceptor.intercept()
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
参考链接