随笔 - 171  文章 - 0  评论 - 0  阅读 - 62466

AOP原理

AOP相关的概念

1)Aspect:切面,切入系统的一个切面。比如事务管理是一个切面,权限管理也是一个切面。

2)Join point:连接点,也就是可以进行横向切入的位置。

3)Advice:通知,切面在某个连接点执行的操作(分为:Before advice,After returning advice,After throwing advice,After (finally) advice,Around advice)。

4)Pointcut:切点,符合切点表达式的连接点,也就是真正被切入的地方。

1) JDK动态代理

主要使用到 InvocationHandler 接口和 Proxy.newProxyInstance() 方法。JDK动态代理要求被代理实现一个接口,只有接口中的方法才能够被代理。其方法是将被代理对象注入到一个中间对象,而中间对象实现InvocationHandler接口,在实现该接口时,可以在 被代理对象调用它的方法时,在调用的前后插入一些代码。而 Proxy.newProxyInstance() 能够利用中间对象来生产代理对象。插入的代码就是切面代码。所以使用JDK动态代理可以实现AOP。我们看个例子:

被代理对象实现的接口,只有接口中的方法才能够被代理:

public interface UserService {
    public void addUser(User user);
    public User getUser(int id);
}

被代理对象:

复制代码
public class UserServiceImpl implements UserService {
    public void addUser(User user) {
        System.out.println("add user");
    }
    public User getUser(int id) {
        User user = new User();
        System.out.println("getUser");
        return user;
    }
}
复制代码

代理中间类:

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyUtil implements InvocationHandler {
  //被代理的对象   
private Object target;   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    System.out.println("do method before");    Object result = method.invoke(target, args);    System.out.println("do method after");   return result;   }   ProxyUtil(Object target){    this.target = target;   }   public Object getTarget() {    return target;   }   public void setTarget(Object target) {    this.target = target;   } }
复制代码

测试:

复制代码
import java.lang.reflect.Proxy;
import User;
public class ProxyTest {
  public static void main(String[] args){
// 被代理的对象     Object proxyedObject
= new UserServiceImpl();     ProxyUtil proxyUtils = new ProxyUtil(proxyedObject);    //生成代理对象,对被代理对象的这些接口进行代理:UserServiceImpl.class.getInterfaces()   UserService proxyObject = (UserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), UserServiceImpl.class.getInterfaces(), proxyUtils);   proxyObject.getUser(1);   proxyObject.addUser(new User());   } }
复制代码

结果:

do method before

getUser

do method after

do method before

add user

do method after

该方式有一个要求,被代理的对象必须实现接口,而且只有接口中的方法才能被代理。

2)CGLIB(code generate libary)

字节码生成技术实现AOP,其实就是继承被代理对象,然后override需要被代理的方法,在覆盖该方法时,自然是可以插入我们自己的代码的。因为需要override被代理对象的方法,所以CGLIB技术实现AOP时,就必须要求需要被代理的方法不能是final方法,因为final方法不能被子类覆盖。我们使用CGLIB实现上面的例子:
复制代码
package aop;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CGProxy implements MethodInterceptor{
    private Object target;    // 被代理对象
    public CGProxy(Object target){
        this.target = target;
    }
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable {
        System.out.println("do method before");
        Object result = proxy.invokeSuper(arg0, arg2);
        System.out.println("do method after");
        return result;
    }
    public Object getProxyObject() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());    // 设置父类
        // 设置回调
     // 在调用父类方法时,回调 this.intercept()
enhancer.setCallback(this); // 创建代理对象 return enhancer.create(); } }
复制代码

测试:

复制代码
public class CGProxyTest {
    public static void main(String[] args){
        Object proxyedObject = new UserServiceImpl();    // 被代理的对象
        CGProxy cgProxy = new CGProxy(proxyedObject);
        UserService proxyObject = (UserService) cgProxy.getProxyObject();
        proxyObject.getUser(1);
        proxyObject.addUser(new User());
    }
}
复制代码

结果:

do method before

getUser

do method after

do method before

add user

do method after

它的原理是生成一个父类enhancer.setSuperclass(this.target.getClass())的子类enhancer.create(),然后对父类的方法进行拦截enhancer.setCallback(this). 对父类的方法进行覆盖,所以父类方法不能是final的。

spring实现AOP的相关源码:

复制代码
@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    @Override
    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()) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
}
复制代码

如果被代理对象实现了接口,那么就使用JDK的动态代理技术,反之则使用CGLIB来实现AOP,所以Spring默认是使用JDK的动态代理技术实现AOP的。

JdkDynamicAopProxy:
复制代码
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {    
    @Override
    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);
    }
}
复制代码

JdkDynamicAopProxy中invoke方法核心代码如下:

复制代码
// 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()) {
 // 如果chain是空的,直接调用target对象的method
// 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(); }
复制代码

顺着this.advised.getInterceptorsAndDynamicInterceptionAdvice看看拦截器链的产生:

复制代码
/**
     * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
     * for the given method, based on this configuration.
     * @param method the proxied method
     * @param targetClass the target class
     * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
     */
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
        MethodCacheKey cacheKey = new MethodCacheKey(method);
        List<Object> cached = this.methodCache.get(cacheKey);
        if (cached == null) {
            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                    this, method, targetClass);
            this.methodCache.put(cacheKey, cached);
        }
        return cached;
    }
复制代码

this.advisorChainFactory其实是DefaultAdvisorChainFactory。

复制代码
/**
 * A simple but definitive way of working out an advice chain for a Method,
 * given an {@link Advised} object. Always rebuilds each advice chain;
 * caching can be provided by subclasses.
 *
 * @author Juergen Hoeller
 * @author Rod Johnson
 * @author Adrian Colyer
 * @since 2.0.3
 */
@SuppressWarnings("serial")
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {

    @Override
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
            Advised config, Method method, Class<?> targetClass) {

        // This is somewhat tricky... We have to process introductions first,
        // but we need to preserve order in the ultimate list.
        List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
        Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

        for (Advisor advisor : config.getAdvisors()) {
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                        if (mm.isRuntime()) {
                            // Creating a new object instance in the getInterceptors() method
                            // isn't a problem as we normally cache created chains.
                            for (MethodInterceptor interceptor : interceptors) {
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        }
                        else {
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            }
            else if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    Interceptor[] interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            }
            else {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }

        return interceptorList;
    }

    /**
     * Determine whether the Advisors contain matching introductions.
     */
    private static boolean hasMatchingIntroductions(Advised config, Class<?> actualClass) {
        for (int i = 0; i < config.getAdvisors().length; i++) {
            Advisor advisor = config.getAdvisors()[i];
            if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
                if (ia.getClassFilter().matches(actualClass)) {
                    return true;
                }
            }
        }
        return false;
    }
}
复制代码

List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass)

这里的config就是ProxyFactoryBean的实例。(config传入了个AdvisedSupport实例,ProxyFactoryBean继承了AdvisedSupport)

继续查看ProxyFactoryBean的代码。

ProxyFactoryBean的getObject方法调用时,会对adviceChain进行初始化

复制代码
/**
     * Return a proxy. Invoked when clients obtain beans from this factory bean.
     * Create an instance of the AOP proxy to be returned by this factory.
     * The instance will be cached for a singleton, and create on each call to
     * {@code getObject()} for a proxy.
     * @return a fresh AOP proxy reflecting the current state of this factory
     */
    @Override
    public Object getObject() throws BeansException {
        initializeAdvisorChain();
        if (isSingleton()) {
            return getSingletonInstance();
        }
        else {
            if (this.targetName == null) {
                logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
            }
            return newPrototypeInstance();
        }
    }
复制代码

initializeAdvisorChain的内部实现:

因为ProxyFactoryBean实现了BeanFactoryAware接口,所以可以拿到当前的容器实例beanFactory。

通过调用this.beanFactory.get(beanName)方法将各个advice bean拿出来,然后加入到list中。

initializeAdvisorChain的实现如下。

复制代码
/**
     * Create the advisor (interceptor) chain. Advisors that are sourced
     * from a BeanFactory will be refreshed each time a new prototype instance
     * is added. Interceptors added programmatically through the factory API
     * are unaffected by such changes.
*/
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
        if (this.advisorChainInitialized) {
            return;
        }

        if (!ObjectUtils.isEmpty(this.interceptorNames)) {
            if (this.beanFactory == null) {
                throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
                        "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
            }

            // Globals can't be last unless we specified a targetSource using the property...
            if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
                    this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
                throw new AopConfigException("Target required after globals");
            }

            // Materialize interceptor chain from bean names.
            for (String name : this.interceptorNames) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Configuring advisor or advice '" + name + "'");
                }

                if (name.endsWith(GLOBAL_SUFFIX)) {
                    if (!(this.beanFactory instanceof ListableBeanFactory)) {
                        throw new AopConfigException(
                                "Can only use global advisors or interceptors with a ListableBeanFactory");
                    }
                    addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
                            name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
                }

                else {
                    // If we get here, we need to add a named interceptor.
                    // We must check if it's a singleton or prototype.
                    Object advice;
                    if (this.singleton || this.beanFactory.isSingleton(name)) {
                        // Add the real Advisor/Advice to the chain.
                        advice = this.beanFactory.getBean(name);
                    }
                    else {
                        // It's a prototype Advice or Advisor: replace with a prototype.
                        // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
                        advice = new PrototypePlaceholderAdvisor(name);
                    }
                    addAdvisorOnChainCreation(advice, name);
                }
            }
        }

        this.advisorChainInitialized = true;
}
复制代码

2)拦截器链是如何链式调用的

接着看JdkDynamicAopProxy中invoke方法调用拦截器链代码,ReflectiveMethodInvocation.proceed执行拦截器链的调用:

复制代码
/**
 * Spring's implementation of the AOP Alliance
 * {@link org.aopalliance.intercept.MethodInvocation} interface,
 * implementing the extended
 * {@link org.springframework.aop.ProxyMethodInvocation} interface.
 *
 * <p>Invokes the target object using reflection. Subclasses can override the
 * {@link #invokeJoinpoint()} method to change this behavior, so this is also
 * a useful base class for more specialized MethodInvocation implementations.
 *
 * <p>It is possible to clone an invocation, to invoke {@link #proceed()}
 * repeatedly (once per clone), using the {@link #invocableClone()} method.
 * It is also possible to attach custom attributes to the invocation,
 * using the {@link #setUserAttribute} / {@link #getUserAttribute} methods.
 *
 * <p><b>NOTE:</b> This class is considered internal and should not be
 * directly accessed. The sole reason for it being public is compatibility
 * with existing framework integrations (e.g. Pitchfork). For any other
 * purposes, use the {@link ProxyMethodInvocation} interface instead.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Adrian Colyer
 * @see #invokeJoinpoint
 * @see #proceed
 * @see #invocableClone
 * @see #setUserAttribute
 * @see #getUserAttribute
 */
//ReflectiveMethodInvocation实现了接口ProxyMethodInvocation,而ProxyMethodInvocation是MethodInvocation的子接口。
//所以ReflectiveMethodInvocation实现了MethodInvocation接口。
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable { @Override public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); } Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
}
复制代码

MethodInterceptor代码:

复制代码
public interface MethodInterceptor extends Interceptor {
    
    /**
     * Implement this method to perform extra treatments before and
     * after the invocation. Polite implementations would certainly
     * like to invoke {@link Joinpoint#proceed()}.
     * @param invocation the method invocation joinpoint
     * @return the result of the call to {@link Joinpoint#proceed()};
     * might be intercepted by the interceptor
     * @throws Throwable if the interceptors or the target object
     * throws an exception
     */
    Object invoke(MethodInvocation invocation) throws Throwable;

}
复制代码

spring提供的MethodBeforeAdviceInterceptor:

复制代码
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {

    private MethodBeforeAdvice advice;

    /**
     * Create a new MethodBeforeAdviceInterceptor for the given advice.
     * @param advice the MethodBeforeAdvice to wrap
     */
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    }

}
复制代码

Spring AOP的配置

Spring中AOP的配置一般有两种方法,一种是使用 aop:config 标签在xml中进行配置,一种是使用注解以及@Aspect风格的配置。

1)基于aop:config的AOP配置

下面是一个典型的事务AOP的配置:

复制代码
<tx:advice id="transactionAdvice" transaction-manager="transactionManager"?>
        <tx:attributes >
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="*" propagation="SUPPORTS" />
        </tx:attributes>
</tx:advice>
<aop:config>
        <aop:pointcut id="transactionPointcut" expression="execution(* com.zby.service..*Impl.*(..))" />
        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" />
</aop:config>
复制代码

2) 基于注解和@Aspect风格的AOP配置 

<!-- 使用annotation定义事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />

扫描切面:

<context:component-scan base-package="com.zby.aop" />

启用@AspectJ风格的注解:

<aop:aspectj-autoproxy proxy-target-class="true"/>

 proxy-target-class="true" 这个最好不要随便使用,它是指定只能使用CGLIB代理。

切面失效

因为Spring AOP是基于动态代理对象的,那么如果target中的方法不是被代理对象调用的,那么就不会织入切面代码。

失效示例:

切面:

复制代码
@Aspect
@Order(100)
public class MethodMonitorAspect {

    Logger logger = LoggerFactory.getLogger(UMPAspect.class);

    /**
     * 环绕增强切面
     *
     * @param pjp
     * @return
     */
    @Around(value = "@annotation(methodMonitor)")
    public Object intercept(ProceedingJoinPoint pjp, MethodMonitor methodMonitor) throws Throwable {
    logger.info("do method before");
    obj = pjp.proceed();
    logger.info("do method after");
    return obj;
  }
}
复制代码
复制代码
@Service("userService")
public class UserServiceImpl implements UserService{
  
public void a() { b(); }
@MethodMonitor
public void b() {
    System.out.println("do b"); } }
复制代码

虽然b()方法被调用了,但是因为不是代理对象调用的,所以切面并没有执行。这就是Spring aop的缺陷。解决方法如下:

首先:将 <aop:aspectj-autoproxy /> 改为:

<aop:aspectj-autoproxy expose-proxy="true"/>

修改失效示例:

复制代码
@Service("userService")
public class UserServiceImpl implements UserService{
   public void a() {
         ((UserService)AopContext.currentProxy()).b();
    }

    @methodMonitor
    public void b() {   
    System.out.println("do b"); } }
复制代码

结果:

do method before

do b

do method after

((UserService)AopContext.currentProxy())先获得当前的代理对象,然后在调用b() 方法就可以了。expose-proxy="true" 表示将当前代理对象暴露出去,不然 AopContext.currentProxy()返回的是null

切面顺序

项目中经常会使用多个切面,监控、权限校验、日志打印、参数校验,使用@Order注解配置顺序,越小优先级越高,越先执行。

final方法想代理怎么解决?

 使用字节码增强技术,重写类。例如:Javassist、bytebuddy、ASM工具包。

posted on   zhengbiyu  阅读(101)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示