Spring Core 官方文档阅读笔记(十一)

  1. PointCut
    org.springframework.aop.Pointcut是主接口,具体定义如下:
public interface Pointcut {

   /**
    * Return the ClassFilter for this pointcut.
    * @return the ClassFilter (never {@code null})
    */
   ClassFilter getClassFilter();

   /**
    * Return the MethodMatcher for this pointcut.
    * @return the MethodMatcher (never {@code null})
    */
   MethodMatcher getMethodMatcher();

   /**
    * Canonical Pointcut instance that always matches.
    */
   Pointcut TRUE = TruePointcut.INSTANCE;

}

其中ClassFilter用于将切入点限制为给定的目标类。

public interface ClassFilter {

   /**
    * Should the pointcut apply to the given interface or target class?
    * @param clazz the candidate target class
    * @return whether the advice should apply to the given target class
    */
   boolean matches(Class<?> clazz);


   /**
    * Canonical instance of a ClassFilter that matches all classes.
    */
   ClassFilter TRUE = TrueClassFilter.INSTANCE;

}

而MethodMatcher则用于检查目标方法是否符合Advice的条件。

public interface MethodMatcher {

   /**
    * Perform static checking whether the given method matches. If this
    * returns {@code false} or if the {@link #isRuntime()} method
    * returns {@code false}, no runtime check (i.e. no.
    * {@link #matches(java.lang.reflect.Method, Class, Object[])} call) will be made.
    * @param method the candidate method
    * @param targetClass the target class (may be {@code null}, in which case
    * the candidate class must be taken to be the method's declaring class)
    * @return whether or not this method matches statically
    */
   boolean matches(Method method, Class<?> targetClass);

   /**
    * Is this MethodMatcher dynamic, that is, must a final call be made on the
    * {@link #matches(java.lang.reflect.Method, Class, Object[])} method at
    * runtime even if the 2-arg matches method returns {@code true}?
    * <p>Can be invoked when an AOP proxy is created, and need not be invoked
    * again before each method invocation,
    * @return whether or not a runtime match via the 3-arg
    * {@link #matches(java.lang.reflect.Method, Class, Object[])} method
    * is required if static matching passed
    */
   boolean isRuntime();

   /**
    * Check whether there a runtime (dynamic) match for this method,
    * which must have matched statically.
    * <p>This method is invoked only if the 2-arg matches method returns
    * {@code true} for the given method and target class, and if the
    * {@link #isRuntime()} method returns {@code true}. Invoked
    * immediately before potential running of the advice, after any
    * advice earlier in the advice chain has run.
    * @param method the candidate method
    * @param targetClass the target class (may be {@code null}, in which case
    * the candidate class must be taken to be the method's declaring class)
    * @param args arguments to the method
    * @return whether there's a runtime match
    * @see MethodMatcher#matches(Method, Class)
    */
   boolean matches(Method method, Class<?> targetClass, Object... args);


   /**
    * Canonical instance that matches all methods.
    */
   MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

    }

MethodMatcher的校验方法分为静态和动态两种。如上代码所示,matches(Method method, Class targetClass)是静态校验,这个方法忽略了传入的参数,而matches(Method method, Class targetClass, Object... args)则是动态校验。
当isRuntime()返回false的时候,使用的是静态校验,当其返回true的时候,使用的是动态校验。
当使用动态校验的时候,要先调用matches(Method method, Class targetClass),如果返回true,才可以继续调用matches(Method method, Class targetClass, Object... args)。

  1. ComposablePointcut
    通常,每一个Advisor里都只有一个pointCut,当我们需要在一个Advisor中定义一个以上的pointCut时,可以考虑使用ComposablePointcut。如下:
/**
 * @Author: kuromaru
 * @Date: Created in 13:41 2019/4/25
 * @Description:
 * @modified:
 */
public class MyBeanOne {

    public void sayHello() {
        System.out.println("Bean-One says Hello!");
    }

    public void sayBye() {
        System.out.println("Bean-One says Bye!");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 13:52 2019/4/25
 * @Description:
 * @modified:
 */
public class ByeMethodMatcher extends StaticMethodMatcher {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().contains("sayBye");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 13:52 2019/4/25
 * @Description:
 * @modified:
 */
public class HelloMethodMatcher extends StaticMethodMatcher {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().contains("sayHello");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 13:47 2019/4/25
 * @Description:
 * @modified:
 */
public class MyAdvise implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Have attention");
        Object ret = invocation.proceed();
        System.out.println("Thanks");
        return ret;
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 13:50 2019/4/25
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {
        MyBeanOne myBeanOne = new MyBeanOne();

        MyBeanOne proxy ;

        ComposablePointcut composablePointcut = new ComposablePointcut(ClassFilter.TRUE, new HelloMethodMatcher());
        composablePointcut.union(new ByeMethodMatcher());
        Advice advice = new MyAdvise();
        Advisor advisor = new DefaultPointcutAdvisor(composablePointcut, advice);

        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(myBeanOne);
        factory.addAdvisor(advisor);
        proxy = (MyBeanOne) factory.getProxy();

        proxy.sayBye();
        proxy.sayHello();

        System.out.println("==========================================================");

        composablePointcut = new ComposablePointcut(ClassFilter.TRUE, new HelloMethodMatcher());
        composablePointcut.intersection(new ByeMethodMatcher());
        advisor = new DefaultPointcutAdvisor(composablePointcut, advice);
        factory = new ProxyFactory();
        factory.setTarget(myBeanOne);
        factory.addAdvisor(advisor);
        proxy = (MyBeanOne) factory.getProxy();

        proxy.sayBye();
        proxy.sayHello();
        proxy.sayHellosayBye();
    }
}

执行结果:

Have attention
Bean-One says Bye!
Thanks
Have attention
Bean-One says Hello!
Thanks
==========================================================
Bean-One says Bye!
Bean-One says Hello!
Have attention
F**k!
Thanks

  1. AspectJExpressionPointcut
    通过Aspect表达式来设置pointcut,如下:
/**
 * @Author: kuromaru
 * @Date: Created in 13:41 2019/4/25
 * @Description:
 * @modified:
 */
public class MyBeanTwo {

    public void sayHello() {
        System.out.println("Bean-Two says Hello!");
    }

    public void sayBye() {
        System.out.println("Bean-Two says Bye!");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 13:47 2019/4/25
 * @Description:
 * @modified:
 */
public class MyAdvise implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Have attention");
        Object ret = invocation.proceed();
        System.out.println("Thanks");
        return ret;
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 14:23 2019/4/25
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        MyBeanTwo myBeanTwo = new MyBeanTwo();

        AspectJExpressionPointcut aspectJExpressionPointcut = new AspectJExpressionPointcut();
        aspectJExpressionPointcut.setExpression("execution(void configure.aop_api.*.*(..) )");

        Advice advice = new MyAdvise();
        Advisor advisor = new DefaultPointcutAdvisor(aspectJExpressionPointcut, advice);

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisor(advisor);
        proxyFactory.setTarget(myBeanTwo);

        MyBeanTwo proxy = (MyBeanTwo) proxyFactory.getProxy();
        proxy.sayBye();
    }
}

输出结果:

Have attention
Bean-Two says Bye!
Thanks

Process finished with exit code 0

  1. JdkRegexpMethodPointcut
    通过正则表达式来设置pointcut,如下:
/**
 * @Author: kuromaru
 * @Date: Created in 13:41 2019/4/25
 * @Description:
 * @modified:
 */
public class MyBeanOne {

    public void sayHello() {
        System.out.println("Bean-One says Hello!");
    }

    public void sayBye() {
        System.out.println("Bean-One says Bye!");
    }

    public void sayHellosayBye() {
        System.out.println("F**k!");
    }

    public void sleep() {
        System.out.println("sleeping~~");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 13:47 2019/4/25
 * @Description:
 * @modified:
 */
public class MyAdvise implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Have attention");
        Object ret = invocation.proceed();
        System.out.println("Thanks");
        return ret;
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 14:38 2019/4/25
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        MyBeanOne myBeanOne = new MyBeanOne();

        JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut();
        jdkRegexpMethodPointcut.setPattern(".*say.*");

        Advice advice = new MyAdvise();
        Advisor advisor = new DefaultPointcutAdvisor(jdkRegexpMethodPointcut, advice);

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(myBeanOne);
        proxyFactory.addAdvisor(advisor);

        MyBeanOne proxy = (MyBeanOne) proxyFactory.getProxy();
        proxy.sayHellosayBye();
        proxy.sleep();
    }
}

输出结果:

Have attention
F**k!
Thanks
sleeping~~

Process finished with exit code 0

  1. RegexpMethodPointcutAdvisor
    简化了JdkRegexpMethodPointcut,如下:
/**
 * @Author: kuromaru
 * @Date: Created in 13:47 2019/4/25
 * @Description:
 * @modified:
 */
public class MyAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("HA~HA~HA~HA~");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 14:47 2019/4/25
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        MyBeanOne myBeanOne = new MyBeanOne();

        Advice advice = new MyAdvice();
        RegexpMethodPointcutAdvisor regexpMethodPointcutAdvisor = new RegexpMethodPointcutAdvisor();
        regexpMethodPointcutAdvisor.setAdvice(advice);
        regexpMethodPointcutAdvisor.setPattern(".*say.*");

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(myBeanOne);
        proxyFactory.addAdvisor(regexpMethodPointcutAdvisor);

        MyBeanOne proxy = (MyBeanOne) proxyFactory.getProxy();
        proxy.sayHellosayBye();
        proxy.sleep();
    }
}

输出结果:

F**k!
HAHAHAHA
sleeping~~

Process finished with exit code 0

  1. ControlFlowPointcut
    可以通过ControlFlowPointcut来做流程控制,如下:
/**
 * @Author: kuromaru
 * @Date: Created in 13:47 2019/4/25
 * @Description:
 * @modified:
 */
public class MyAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("HA~HA~HA~HA~");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 13:41 2019/4/25
 * @Description:
 * @modified:
 */
public class MyBeanTwo {

    public void sayHello() {
        System.out.println("Bean-Two says Hello!");
    }

    public void sayBye() {
        System.out.println("Bean-Two says Bye!");
    }

    public void sleep(MyBeanOne myBeanOne) {
        myBeanOne.sleep();
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 13:41 2019/4/25
 * @Description:
 * @modified:
 */
public class MyBeanOne {

    public void sayHello() {
        System.out.println("Bean-One says Hello!");
    }

    public void sayBye() {
        System.out.println("Bean-One says Bye!");
    }

    public void sayHellosayBye() {
        System.out.println("F**k!");
    }

    public void sleep() {
        System.out.println("sleeping~~");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 14:56 2019/4/25
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        MyBeanOne myBeanOne = new MyBeanOne();

        ControlFlowPointcut controlFlowPointcut = new ControlFlowPointcut(MyBeanTwo.class, "sleep");
        Advice advice = new MyAdvice();
        Advisor advisor = new DefaultPointcutAdvisor(controlFlowPointcut, advice);

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(myBeanOne);
        proxyFactory.addAdvisor(advisor);

        MyBeanOne proxy = (MyBeanOne) proxyFactory.getProxy();
        System.out.println("===================MyBeanOne========================");
        proxy.sleep();

        System.out.println("===================MyBeanTwo========================");
        MyBeanTwo myBeanTwo = new MyBeanTwo();
        myBeanTwo.sleep(proxy);

    }
}

输出结果:

=MyBeanOne======
sleeping~~
=MyBeanTwo======
sleeping~~
HAHAHAHA

Process finished with exit code 0

  1. StaticMethodMatcherPointcut
    静态pointcut,所谓静态,就是说spring会针对目标上的每一个方法调用一次MethodMatcher的mathces方法,其返回值被缓冲起来以便日后使用,这样,对每一个方法的适用性测试只会进行一次,相对动态的效率比较高,推荐使用,如下:
/**
 * @Author: kuromaru
 * @Date: Created in 13:47 2019/4/25
 * @Description:
 * @modified:
 */
public class MyAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("HA~HA~HA~HA~");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 16:10 2019/4/25
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        StaticMethodMatcherPointcut staticMethodMatcherPointcut = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                return method.getName().contains("say");
            }

            @Override
            public ClassFilter getClassFilter() {
                return clazz -> clazz == MyBeanTwo.class;
            }
        };

        Advice advice = new MyAdvice();
        Advisor advisor = new DefaultPointcutAdvisor(staticMethodMatcherPointcut, advice);

        MyBeanOne myBeanOne = new MyBeanOne();
        MyBeanTwo myBeanTwo = new MyBeanTwo();

        ProxyFactory proxyFactoryOne = new ProxyFactory();
        proxyFactoryOne.setTarget(myBeanOne);
        proxyFactoryOne.addAdvisor(advisor);

        ProxyFactory proxyFactoryTwo = new ProxyFactory();
        proxyFactoryTwo.setTarget(myBeanTwo);
        proxyFactoryTwo.addAdvisor(advisor);

        MyBeanOne proxyMyBeanOne = (MyBeanOne) proxyFactoryOne.getProxy();
        proxyMyBeanOne.sayHello();
        proxyMyBeanOne.sleep();

        System.out.println("===================================================");
        MyBeanTwo proxyMyBeanTwo = (MyBeanTwo) proxyFactoryTwo.getProxy();
        proxyMyBeanTwo.sayHello();
        proxyMyBeanTwo.sleep(proxyMyBeanOne);
    }
}

输出结果:

Bean-One says Hello!
sleeping~~
===================================================
Bean-Two says Hello!
HAHAHAHA
sleeping~~

Process finished with exit code 0

  1. Advice--环绕通知
    实现环绕通知需要实现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;

}

其中,invoke方法里即是增强处理,如下:

public class DebugInterceptor implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Before: invocation=[" + invocation + "]");
        Object rval = invocation.proceed();
        System.out.println("Invocation returned");
        return rval;
    }
}

需要注意的是,要将invocation.proceed()方法的返回值做为invoke的返回值。

  1. Advice--前置通知
    前置通知需要实现MethodBeforeAdvice,接口定义如下:
public interface MethodBeforeAdvice extends BeforeAdvice {

   /**
    * Callback before a given method is invoked.
    * @param method method being invoked
    * @param args arguments to the method
    * @param target target of the method invocation. May be {@code null}.
    * @throws Throwable if this object wishes to abort the call.
    * Any exception thrown will be returned to the caller if it's
    * allowed by the method signature. Otherwise the exception
    * will be wrapped as a runtime exception.
    */
   void before(Method method, Object[] args, Object target) throws Throwable;

}

before方法中即为前置通知的处理逻辑,可以看到,该方法返回类型为void。在通知之前,可以在连接点执行之前插入自定义行为,但不能更改返回值。如果前通知抛出异常,它将中止拦截器链的进一步执行。异常将向上传播。如果异常是非检查类异常或在被调用方法上已经声明了该异常,它将直接传递给客户端。否则,它将被包装在AOP代理非检查类异常中。
啥意思呢?我们写代码跑一下看看,如下:

/**
 * @Author: kuromaru
 * @Date: Created in 14:26 2019/4/26
 * @Description:
 * @modified:
 */
public class BeforeBean {

    public void sayHello() {
        System.out.println("Hello~");
    }

    public void sayBye() {
        System.out.println("Bye~");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 14:29 2019/4/26
 * @Description:
 * @modified:
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
//        throw new RuntimeException();
        Class.forName("etstste");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 14:30 2019/4/26
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        BeforeBean beforeBean = new BeforeBean();

        Advice advice = new MyBeforeAdvice();

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(beforeBean);
        proxyFactory.addAdvice(advice);

        BeforeBean proxy = (BeforeBean) proxyFactory.getProxy();
        proxy.sayHello();
        proxy.sayBye();
    }
}

输出结果:

Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at configure.aop_api.beforeAdvice.BeforeBean$$EnhancerBySpringCGLIB$$806fd3d9.sayHello()
at configure.aop_api.beforeAdvice.Client.main(Client.java:25)
Caused by: java.lang.ClassNotFoundException: etstste
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at configure.aop_api.beforeAdvice.MyBeforeAdvice.before(MyBeforeAdvice.java:17)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:51)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)
... 2 more

Process finished with exit code 1

可以看到"at configure.aop_api.beforeAdvice.BeforeBean$$EnhancerBySpringCGLIB$$806fd3d9.sayHello()"这么一句,说明异常是从代理类中抛出来的。
我们再把MyBeforeAdvice改一下:

/**
 * @Author: kuromaru
 * @Date: Created in 14:29 2019/4/26
 * @Description:
 * @modified:
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        throw new RuntimeException();
//        Class.forName("etstste");
    }
}

输出结果:

Exception in thread "main" java.lang.RuntimeException
at configure.aop_api.beforeAdvice.MyBeforeAdvice.before(MyBeforeAdvice.java:16)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:51)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)
at configure.aop_api.beforeAdvice.BeforeBean$$EnhancerBySpringCGLIB$$806fd3d9.sayHello()
at configure.aop_api.beforeAdvice.Client.main(Client.java:25)

Process finished with exit code 1

异常就从MyBeforeAdvice抛出来了。

  1. Advice--异常通知
    异常通知需要实现ThrowsAdvice接口,接口定义如下:
public interface ThrowsAdvice extends AfterAdvice {

}

啥玩意都没有!这个接口只是起到一个标识作用。如果要实现异常通知的逻辑,根据接口说明文档,需要实现如下方法:

void afterThrowing([Method, args, target], ThrowableSubclass)

其中前三个参数是可选的。如:

public void afterThrowing(Exception ex)
public void afterThrowing(RemoteException rex)
public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
public void afterThrowing(Method method, Object[] args, Object target, ServletException ex

注意,如果抛出通知方法本身抛出异常,它将覆盖原始异常(即更改抛出给用户的异常)。覆盖的异常通常是RuntimeException;这与任何方法签名都兼容。但是,如果抛出通知方法抛出检查的异常,则必须匹配目标方法的声明异常,因此在某种程度上与特定的目标方法签名相耦合。
不要抛出与目标方法的签名不兼容的未声明的检查异常!
一脸懵逼。。。还是用代码来说明,首先看一下被覆盖的场景,如下:

/**
 * @Author: kuromaru
 * @Date: Created in 15:08 2019/4/26
 * @Description:
 * @modified:
 */
public class MyThrowBean {

    public void sayHello() {
        throw new NullPointerException();
    }

    public void sayBye() throws ClassNotFoundException {
        Class.forName("testset");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 15:10 2019/4/26
 * @Description:
 * @modified:
 */
public class MyThrowAdvice implements ThrowsAdvice {

    public void afterThrowing(Exception e) {
        throw new ArrayIndexOutOfBoundsException();
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 15:16 2019/4/26
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        MyThrowBean myThrowBean = new MyThrowBean();

        Advice advice = new MyThrowAdvice();

        ProxyFactory factory = new ProxyFactory();
        factory.addAdvice(advice);
        factory.setTarget(myThrowBean);

        MyThrowBean proxy = (MyThrowBean) factory.getProxy();
        proxy.sayHello();
    }
}

输出结果:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
at configure.aop_api.throwAdvice.MyThrowAdvice.afterThrowing(MyThrowAdvice.java:14)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invokeHandlerMethod(ThrowsAdviceInterceptor.java:145)
at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:130)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)
at configure.aop_api.throwAdvice.MyThrowBean$$EnhancerBySpringCGLIB$$f310f035.sayHello()
at configure.aop_api.throwAdvice.Client.main(Client.java:25)

Process finished with exit code 1

可以看到抛出的是数组越界异常,覆盖了MyThrowBean中的空指针异常。
再来看看非检查异常,如下:

/**
 * @Author: kuromaru
 * @Date: Created in 15:10 2019/4/26
 * @Description:
 * @modified:
 */
public class MyThrowAdvice implements ThrowsAdvice {

    public void afterThrowing(Exception e) throws ClassNotFoundException {
//        throw new ArrayIndexOutOfBoundsException();
//        InputStream is = new FileInputStream("sdflskfjljk");
        Class.forName("12114");
    }
}

输出结果:

Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at configure.aop_api.throwAdvice.MyThrowBean$$EnhancerBySpringCGLIB$$f310f035.sayHello()
at configure.aop_api.throwAdvice.Client.main(Client.java:25)
Caused by: java.lang.ClassNotFoundException: 12114
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at configure.aop_api.throwAdvice.MyThrowAdvice.afterThrowing(MyThrowAdvice.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invokeHandlerMethod(ThrowsAdviceInterceptor.java:145)
at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:130)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)
... 2 more

Process finished with exit code 1

额。。。和前置通知一样,检查型异常是由代理类抛出的。而且,就结果而言,也可以认为是覆盖了原目标类的异常。。。后面尝试在异常通知处理中抛出IOException,也是一样的结果:

Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at configure.aop_api.throwAdvice.MyThrowBean$$EnhancerBySpringCGLIB$$f310f035.sayHello()
at configure.aop_api.throwAdvice.Client.main(Client.java:25)
Caused by: java.io.FileNotFoundException: sdflskfjljk (系统找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.(FileInputStream.java:138)
at java.io.FileInputStream.(FileInputStream.java:93)
at configure.aop_api.throwAdvice.MyThrowAdvice.afterThrowing(MyThrowAdvice.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invokeHandlerMethod(ThrowsAdviceInterceptor.java:145)
at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:130)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:671)
... 2 more

Process finished with exit code 1

不太明白为什么大牛们要求不要抛出与目标方法的签名不兼容的未声明的检查异常。

  1. Advice--后置通知
    后置通知,即在方法返回后的通知类型,需要实现AfterReturningAdvice接口,定义如下:
public interface AfterReturningAdvice extends AfterAdvice {

   /**
    * Callback after a given method successfully returned.
    * @param returnValue the value returned by the method, if any
    * @param method method being invoked
    * @param args arguments to the method
    * @param target target of the method invocation. May be {@code null}.
    * @throws Throwable if this object wishes to abort the call.
    * Any exception thrown will be returned to the caller if it's
    * allowed by the method signature. Otherwise the exception
    * will be wrapped as a runtime exception.
    */
   void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;

}

与前置通知类似,直接看代码:

/**
 * @Author: kuromaru
 * @Date: Created in 16:04 2019/4/26
 * @Description:
 * @modified:
 */
public class MyAfterReturnBean {

    public void sayHello() {
        System.out.println("Hello~");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 16:05 2019/4/26
 * @Description:
 * @modified:
 */
public class MyAfterReturnAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("HA~HA~HA~HA~");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 16:06 2019/4/26
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        MyAfterReturnBean myAfterReturnBean = new MyAfterReturnBean();

        Advice advice = new MyAfterReturnAdvice();

        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(myAfterReturnBean);
        factory.addAdvice(advice);

        MyAfterReturnBean proxy = (MyAfterReturnBean) factory.getProxy();
        proxy.sayHello();
    }
}

输出结果:

Hello~
HAHAHAHA

Process finished with exit code 0

同样的,当通知类中抛出检查型异常是,是由代理类抛出的,非检查性异常是由通知类直接抛出的。

  1. Advice--Introduction
    之前在整理AOP的时候,就遇到过Introduction,其目的为在一个类中引入新的方法。
    一个Introduction需要IntroductionAdvisor和IntroductionIntercepter。具体定义如下:
public interface IntroductionAdvisor extends Advisor, IntroductionInfo {

   /**
    * Return the filter determining which target classes this introduction
    * should apply to.
    * <p>This represents the class part of a pointcut. Note that method
    * matching doesn't make sense to introductions.
    * @return the class filter
    */
   ClassFilter getClassFilter();

   /**
    * Can the advised interfaces be implemented by the introduction advice?
    * Invoked before adding an IntroductionAdvisor.
    * @throws IllegalArgumentException if the advised interfaces can't be
    * implemented by the introduction advice
    */
   void validateInterfaces() throws IllegalArgumentException;

}
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {

}
public interface DynamicIntroductionAdvice extends Advice {

   /**
    * Does this introduction advice implement the given interface?
    * @param intf the interface to check
    * @return whether the advice implements the specified interface
    */
   boolean implementsInterface(Class<?> intf);

}

spring已经封装了三个实现类DefaultIntroductionAdvisor、DelegatingIntroductionInterceptor和DelegatePerTargetObjectIntroductionInterceptor,第一个对应IntroductionAdvisor,后两个对应IntroductionInteceptor。而DelegatingIntroductionInterceptor和DelegatePerTargetObjectIntroductionInterceptor的区别在于是否生成同一个委托类,官方解释如下:

* <p>This differs from {@link DelegatingIntroductionInterceptor} in that a single
* instance of this class can be used to advise multiple target objects, and each target
* object will have its <i>own</i> delegate (whereas DelegatingIntroductionInterceptor
* shares the same delegate, and hence the same state across all targets).

即DelegatePerTargetObjectIntroductionInterceptor的单个实例可以应用于多个目标类,并且每个目标对象都有自己的委托,而DelegatingIntroductionInterceptor共享同一个委托,所有的目标状态都是相同的。后面会用代码说明两个类的区别。

先来看下DelegatingIntroductionInterceptor,代码如下(参考https://www.xuebuyuan.com/1709254.html):

/**
 * @Author: kuromaru
 * @Date: Created in 13:17 2019/4/28
 * @Description:
 * @modified:
 */
public class MyInfo {

    private String name;

    private String age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}
public interface ModifyMonitor {

    public boolean isModified();
}
/**
 * @Author: kuromaru
 * @Date: Created in 10:37 2019/4/28
 * @Description:
 * @modified:
 */
public class MyInfoModifyMonitor extends DelegatingIntroductionInterceptor implements ModifyMonitor {

    private static final long serialVersionUID = 951398037861840137L;

    private boolean isModified;

    private Map<Method, Method> methodCache = new ConcurrentHashMap<>();

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        if (!isModified) {
            if (mi.getArguments().length == 1 && mi.getMethod().getName().startsWith("set")) {
                Method getter = getGetter(mi.getMethod());
                if (getter != null) {
                    Object newVal = mi.getArguments()[0];
                    Object oldVal = getter.invoke(mi.getThis(), null);
                    if (newVal == null && oldVal != null) {
                        isModified = true;
                    } else if (newVal == null && oldVal == null) {
                        isModified = false;
                    } else {
                        isModified = !newVal.equals(oldVal);
                    }
                }
            }
        }
        return super.invoke(mi);
    }

    private Method getGetter(Method setterMethod) {

        Method getter = methodCache.get(setterMethod);

        if (getter == null) {
            try {
                getter = setterMethod.getDeclaringClass().getMethod(setterMethod.getName().replaceFirst("set", "get"));
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }

        methodCache.put(setterMethod, getter);
        return getter;
    }

    @Override
    public boolean isModified() {
        return isModified;
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 11:24 2019/4/28
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        MyInfo myInfo = new MyInfo();
        myInfo.setName("WJ");
        myInfo.setAge("30");

        Advisor advisor = new DefaultIntroductionAdvisor(new MyInfoModifyMonitor());

        ProxyFactory pfb = new ProxyFactory();
        pfb.setProxyTargetClass(true);
        pfb.addAdvisor(advisor);
        pfb.setTarget(myInfo);

        MyInfo proxy = (MyInfo) pfb.getProxy();
        ModifyMonitor myModifyMonitor = (ModifyMonitor) proxy;

        System.out.println(myModifyMonitor.isModified());
        proxy.setAge("31");
        System.out.println(myModifyMonitor.isModified());
        proxy.setAge("32");
        System.out.println(myModifyMonitor.isModified());
    }
}

输出结果:

false
true
true

Process finished with exit code 0

DelegatePerTargetObjectIntroductionInterceptor的使用方式略有不同,如下:

public interface GolangStudy {

    void studyGolang();
}
/**
 * @Author: kuromaru
 * @Date: Created in 14:24 2019/4/28
 * @Description:
 * @modified:
 */
public class GolangStudyImpl implements GolangStudy {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void studyGolang() {
        System.out.println(name + " want to study golang");
    }
}
public interface JavaStudy {

    void studyJava();
}
/**
 * @Author: kuromaru
 * @Date: Created in 15:08 2019/4/28
 * @Description:
 * @modified:
 */
public class JavaStudyImpl implements JavaStudy {
    @Override
    public void studyJava() {
        System.out.println("also wanna study java");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 14:49 2019/4/28
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        GolangStudyImpl golangStudy = new GolangStudyImpl();
        golangStudy.setName("WJ");

        Advisor advisor = new DefaultIntroductionAdvisor(new DelegatePerTargetObjectIntroductionInterceptor(JavaStudyImpl.class, JavaStudy.class));

        ProxyFactory factory = new ProxyFactory();
        factory.setProxyTargetClass(true);
        factory.addAdvisor(advisor);
        factory.setTarget(golangStudy);

        GolangStudyImpl proxy = (GolangStudyImpl) factory.getProxy();
        proxy.studyGolang();

        JavaStudy javaStudy = (JavaStudy) proxy;
        javaStudy.studyJava();
    }
}

输出结果:

WJ want to study golang
also wanna study java

Process finished with exit code 0

那这两玩意有啥区别呢?我们看下invoke的源码

  • DelegatingIntroductionInterceptor
/**
 * Subclasses may need to override this if they want to perform custom
 * behaviour in around advice. However, subclasses should invoke this
 * method, which handles introduced interfaces and forwarding to the target.
 */
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   if (isMethodOnIntroducedInterface(mi)) {
      // Using the following method rather than direct reflection, we
      // get correct handling of InvocationTargetException
      // if the introduced method throws an exception.
      Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());

      // Massage return value if possible: if the delegate returned itself,
      // we really want to return the proxy.
      if (retVal == this.delegate && mi instanceof ProxyMethodInvocation) {
         Object proxy = ((ProxyMethodInvocation) mi).getProxy();
         if (mi.getMethod().getReturnType().isInstance(proxy)) {
            retVal = proxy;
         }
      }
      return retVal;
   }

   return doProceed(mi);
}
  • DelegatePerTargetObjectIntroductionInterceptor
private final Map<Object, Object> delegateMap = new WeakHashMap<Object, Object>();

...

/**
 * Subclasses may need to override this if they want to perform custom
 * behaviour in around advice. However, subclasses should invoke this
 * method, which handles introduced interfaces and forwarding to the target.
 */
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
   if (isMethodOnIntroducedInterface(mi)) {
      Object delegate = getIntroductionDelegateFor(mi.getThis());

      // Using the following method rather than direct reflection,
      // we get correct handling of InvocationTargetException
      // if the introduced method throws an exception.
      Object retVal = AopUtils.invokeJoinpointUsingReflection(delegate, mi.getMethod(), mi.getArguments());

      // Massage return value if possible: if the delegate returned itself,
      // we really want to return the proxy.
      if (retVal == delegate && mi instanceof ProxyMethodInvocation) {
         retVal = ((ProxyMethodInvocation) mi).getProxy();
      }
      return retVal;
   }

   return doProceed(mi);
}

...

private Object getIntroductionDelegateFor(Object targetObject) {
   synchronized (this.delegateMap) {
      if (this.delegateMap.containsKey(targetObject)) {
         return this.delegateMap.get(targetObject);
      }
      else {
         Object delegate = createNewDelegate();
         this.delegateMap.put(targetObject, delegate);
         return delegate;
      }
   }
}

注意这两个方法内调用AopUtils.invokeJoinpointUsingReflection时的参数,前者是this.delegate,而后者是通过getIntroductionDelegateFor方法获取到的delegate,再看getIntroductionDelegateFor的实现逻辑,以targetObject为key,delegate为value,保存在了一个WeakHashMap(在GC触发时,只有弱引用的对象会被回收)中。所以,前者是共用一个委托,而后者会对不同的目标类生成不同的委托。

  1. ProxyFactoryBean
    ProxyFactoryBean是FactoryBean的一个实现类,根据类名,我们可以认为它是用于自定义ProxyFactory的实例化过程。
    其中,有一些比较关键的属性,如下:
  • proxyTargetClass
    需设置boolean类型的值,true或者false。如果设置为true,即代表代理目标类,而不是目标类的接口,相应的,使用的是CGLIB代理。

  • optimize
    控制是否对通过CGLIB创建的代理进行积极优化,除非完全了解如果对AOP代理进行优化,否则不要随意使用此配置。该配置进队CGLIB起作用,对jdk代理无效。

  • frozen
    如果该项被配置为frozen,则不允许修改代理的配置。此项既适用于轻微优化,也可以防止在代理创建后修改代理的操作。默认是false。

  • exposeProxy
    用于控制是否在TheadLocal中暴露当前代理,以便目标类可以访问代理。如果目标类需要获取代理,并且exposeProxy被设置为true,则可以通过AopContext.currentProxy()方法获取到当前代理。

  • proxyInterfaces
    传入一个包含接口名字的字符串数组,用于指定需要被代理的接口。如果不配置,则使用CGLIB代理。

  • interceptorNames
    传入一个包含Advisor、interceptor或者其他advice名字的字符串数组。根据传入的拦截器顺序执行相应的拦截器处理,也就是说,数组下标小的优先执行。
    数组中的名字是当前工厂中的bean名称,包括父类工厂中的bean。但是不能包含bean引用,因为这样会导致ProxyFactoryBean忽略Advice的singleton配置。
    可以使用" * "号来包含所有符合正则表达式的bean。

  • singleton
    用于控制getObject()方法是否返回一个单例。默认是true。

  1. JDK和CGLIB
    如果要代理的类没有实现任何接口,则创建一个基于CGLIB的代理。ProxyFactoryBean有一个proxyTargetClass的属性,设置为true时,创建CGLIB代理,但是,即使是false,也会创建一个CGLIB代理。
    如果一个类实现了一个或多个接口,则创建的代理的类型取决于ProxyFactoryBean的配置。
    如果ProxyFactoryBean的proxyTargetClass设置为true,那么,即使ProxyFactoryBean的proxyInterfaces属性设置为一个或多个接口名称,ProxyTargetClass属性设置为true也会导致基于CGLIB的代理生效。
    如果ProxyFactoryBean的proxyInterfaces属性设置为一个或多个接口名称,则创建一个基于JDK的代理。如果目标类实现的接口比proxyInterfaces属性里指定的接口多,那么多出来的这部分接口不是由返回的代理实现的。
    如果没有设置proxyInterfaces属性,但是目标类确实实现了一个或多个接口,则ProxyFactoryBean会创建一个JDK代理。

  2. 管理AOP代理
    可以通过把获取到的proxy对象强转成Advised来管理AOP代理。如下:

/**
 * @Author: kuromaru
 * @Date: Created in 16:06 2019/4/26
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {

        MyAfterReturnBean myAfterReturnBean = new MyAfterReturnBean();

        Advice advice = new MyAfterReturnAdvice();

        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(myAfterReturnBean);
        factory.addAdvice(advice);

        MyAfterReturnBean proxy = (MyAfterReturnBean) factory.getProxy();
        proxy.sayHello();

        Advised advised = (Advised) proxy;
        Advisor[] advisors = advised.getAdvisors();
        System.out.println(advisors.length);
    }
}

输出结果:

Hello~
HAHAHAHA
1

Process finished with exit code 0

Advised接口定义了如下方法:

public interface Advised extends TargetClassAware {

   /**
    * Return whether the Advised configuration is frozen,
    * in which case no advice changes can be made.
    */
    // 设为true,防止其他人修改代理的配置
   boolean isFrozen();

   /**
    * Are we proxying the full target class instead of specified interfaces?
    */
   boolean isProxyTargetClass();

   /**
    * Return the interfaces proxied by the AOP proxy.
    * <p>Will not include the target class, which may also be proxied.
    */
   Class<?>[] getProxiedInterfaces();

   /**
    * Determine whether the given interface is proxied.
    * @param intf the interface to check
    */
   boolean isInterfaceProxied(Class<?> intf);

   /**
    * Change the {@code TargetSource} used by this {@code Advised} object.
    * <p>Only works if the configuration isn't {@linkplain #isFrozen frozen}.
    * @param targetSource new TargetSource to use
    */
   void setTargetSource(TargetSource targetSource);

   /**
    * Return the {@code TargetSource} used by this {@code Advised} object.
    */
   TargetSource getTargetSource();

   /**
    * Set whether the proxy should be exposed by the AOP framework as a
    * {@link ThreadLocal} for retrieval via the {@link AopContext} class.
    * <p>It can be necessary to expose the proxy if an advised object needs
    * to invoke a method on itself with advice applied. Otherwise, if an
    * advised object invokes a method on {@code this}, no advice will be applied.
    * <p>Default is {@code false}, for optimal performance.
    */
   void setExposeProxy(boolean exposeProxy);

   /**
    * Return whether the factory should expose the proxy as a {@link ThreadLocal}.
    * <p>It can be necessary to expose the proxy if an advised object needs
    * to invoke a method on itself with advice applied. Otherwise, if an
    * advised object invokes a method on {@code this}, no advice will be applied.
    * <p>Getting the proxy is analogous to an EJB calling {@code getEJBObject()}.
    * @see AopContext
    */
   boolean isExposeProxy();

   /**
    * Set whether this proxy configuration is pre-filtered so that it only
    * contains applicable advisors (matching this proxy's target class).
    * <p>Default is "false". Set this to "true" if the advisors have been
    * pre-filtered already, meaning that the ClassFilter check can be skipped
    * when building the actual advisor chain for proxy invocations.
    * @see org.springframework.aop.ClassFilter
    */
   void setPreFiltered(boolean preFiltered);

   /**
    * Return whether this proxy configuration is pre-filtered so that it only
    * contains applicable advisors (matching this proxy's target class).
    */
   boolean isPreFiltered();

   /**
    * Return the advisors applying to this proxy.
    * @return a list of Advisors applying to this proxy (never {@code null})
    */
   Advisor[] getAdvisors();

   /**
    * Add an advisor at the end of the advisor chain.
    * <p>The Advisor may be an {@link org.springframework.aop.IntroductionAdvisor},
    * in which new interfaces will be available when a proxy is next obtained
    * from the relevant factory.
    * @param advisor the advisor to add to the end of the chain
    * @throws AopConfigException in case of invalid advice
    */
   void addAdvisor(Advisor advisor) throws AopConfigException;

   /**
    * Add an Advisor at the specified position in the chain.
    * @param advisor the advisor to add at the specified position in the chain
    * @param pos position in chain (0 is head). Must be valid.
    * @throws AopConfigException in case of invalid advice
    */
   void addAdvisor(int pos, Advisor advisor) throws AopConfigException;

   /**
    * Remove the given advisor.
    * @param advisor the advisor to remove
    * @return {@code true} if the advisor was removed; {@code false}
    * if the advisor was not found and hence could not be removed
    */
   boolean removeAdvisor(Advisor advisor);

   /**
    * Remove the advisor at the given index.
    * @param index index of advisor to remove
    * @throws AopConfigException if the index is invalid
    */
   void removeAdvisor(int index) throws AopConfigException;

   /**
    * Return the index (from 0) of the given advisor,
    * or -1 if no such advisor applies to this proxy.
    * <p>The return value of this method can be used to index into the advisors array.
    * @param advisor the advisor to search for
    * @return index from 0 of this advisor, or -1 if there's no such advisor
    */
   int indexOf(Advisor advisor);

   /**
    * Replace the given advisor.
    * <p><b>Note:</b> If the advisor is an {@link org.springframework.aop.IntroductionAdvisor}
    * and the replacement is not or implements different interfaces, the proxy will need
    * to be re-obtained or the old interfaces won't be supported and the new interface
    * won't be implemented.
    * @param a the advisor to replace
    * @param b the advisor to replace it with
    * @return whether it was replaced. If the advisor wasn't found in the
    * list of advisors, this method returns {@code false} and does nothing.
    * @throws AopConfigException in case of invalid advice
    */
   boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;

   /**
    * Add the given AOP Alliance advice to the tail of the advice (interceptor) chain.
    * <p>This will be wrapped in a DefaultPointcutAdvisor with a pointcut that always
    * applies, and returned from the {@code getAdvisors()} method in this wrapped form.
    * <p>Note that the given advice will apply to all invocations on the proxy,
    * even to the {@code toString()} method! Use appropriate advice implementations
    * or specify appropriate pointcuts to apply to a narrower set of methods.
    * @param advice advice to add to the tail of the chain
    * @throws AopConfigException in case of invalid advice
    * @see #addAdvice(int, Advice)
    * @see org.springframework.aop.support.DefaultPointcutAdvisor
    */
   void addAdvice(Advice advice) throws AopConfigException;

   /**
    * Add the given AOP Alliance Advice at the specified position in the advice chain.
    * <p>This will be wrapped in a {@link org.springframework.aop.support.DefaultPointcutAdvisor}
    * with a pointcut that always applies, and returned from the {@link #getAdvisors()}
    * method in this wrapped form.
    * <p>Note: The given advice will apply to all invocations on the proxy,
    * even to the {@code toString()} method! Use appropriate advice implementations
    * or specify appropriate pointcuts to apply to a narrower set of methods.
    * @param pos index from 0 (head)
    * @param advice advice to add at the specified position in the advice chain
    * @throws AopConfigException in case of invalid advice
    */
   void addAdvice(int pos, Advice advice) throws AopConfigException;

   /**
    * Remove the Advisor containing the given advice.
    * @param advice the advice to remove
    * @return {@code true} of the advice was found and removed;
    * {@code false} if there was no such advice
    */
   boolean removeAdvice(Advice advice);

   /**
    * Return the index (from 0) of the given AOP Alliance Advice,
    * or -1 if no such advice is an advice for this proxy.
    * <p>The return value of this method can be used to index into
    * the advisors array.
    * @param advice AOP Alliance advice to search for
    * @return index from 0 of this advice, or -1 if there's no such advice
    */
   int indexOf(Advice advice);

   /**
    * As {@code toString()} will normally be delegated to the target,
    * this returns the equivalent for the AOP proxy.
    * @return a string description of the proxy configuration
    */
   String toProxyConfigString();

}
  1. Auto-Proxy
  • BeanNameAutoProxyCreator
    BeanNameAutoProxyCreator是一个BeanPostProcessor,作为BeanPostProcessor来讲,本身是对Bean的初始化前后添加一下个性化的处理。

用法如下:

/**
 * @Author: kuromaru
 * @Date: Created in 15:59 2019/4/29
 * @Description:
 * @modified:
 */
public class MyAction {

    public void doSomething() {
        System.out.println("do something");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 15:49 2019/4/29
 * @Description:
 * @modified:
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("this is before advice");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 16:00 2019/4/29
 * @Description:
 * @modified:
 */
@Configuration
public class MyConfig {

    @Bean("myAction")
    public MyAction myAction() {
        return new MyAction();
    }

    @Bean("myBeforeAdvice")
    public MyBeforeAdvice myBeforeAdvice() {
        return new MyBeforeAdvice();
    }

    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        beanNameAutoProxyCreator.setBeanNames("myAction");
        beanNameAutoProxyCreator.setInterceptorNames("myBeforeAdvice");
        return beanNameAutoProxyCreator;
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 15:59 2019/4/29
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
        MyAction myAction = (MyAction) acac.getBean("myAction");
        myAction.doSomething();
    }
}

输出结果:

四月 29, 2019 4:06:11 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 29 16:06:11 CST 2019]; root of context hierarchy
四月 29, 2019 4:06:12 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'myConfig' of type [configure.aop_api.autoProxy.beanNameAutoProxyCreator.MyConfig$$EnhancerBySpringCGLIB$$b075506b] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
this is before advice
do something

Process finished with exit code 0

  • DefaultAdvisorAutoProxyCreator
    DefaultAdvisorAutoProxyCreator提供了更简单更通用的自动代理创建功能。它会自动去判断你的Advisor中的配置,注意,一定要是Advisor,而不是inteceptor或者advice,所有适用于pointcut的地方,都会自动的被代理。
    如下:
/**
 * @Author: kuromaru
 * @Date: Created in 15:59 2019/4/29
 * @Description:
 * @modified:
 */
public class MyAction {

    public void doSomething() {
        System.out.println("do something");
    }

    public void bye() {
        System.out.println("Bye");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 15:49 2019/4/29
 * @Description:
 * @modified:
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("this is before advice");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 16:53 2019/4/29
 * @Description:
 * @modified:
 */
@Configuration
public class MyConfig {

    @Bean("myAction")
    public MyAction myAction() {
        return new MyAction();
    }

    @Bean
    public MyBeforeAdvice myBeforeAdvice() {
        return new MyBeforeAdvice();
    }

    @Bean
    public Advisor myAdvisor() {
        StaticMethodMatcherPointcut staticMethodMatcherPointcut = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {

                return method.getName().equalsIgnoreCase("bye") && targetClass == MyAction.class;
            }
        };
        return new DefaultPointcutAdvisor(staticMethodMatcherPointcut, myBeforeAdvice());
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 15:59 2019/4/29
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
        MyAction myAction = (MyAction) acac.getBean("myAction");
        myAction.doSomething();
        myAction.bye();
    }
}

输出结果:

四月 29, 2019 5:14:55 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 29 17:14:55 CST 2019]; root of context hierarchy
四月 29, 2019 5:14:55 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'myConfig' of type [configure.aop_api.autoProxy.defaultAdvisorAutoProxyCreator.MyConfig$$EnhancerBySpringCGLIB$$1f2392b8] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
do something
this is before advice
Bye

Process finished with exit code 0

可以看到,DefaultAdvisorAutoProxyCreator并没有设置任何beanNames之类的属性,但是它一样代理了MyAction。
如果我们把MyConfig中配置Advisor的代码注释掉,运行结果如下:

四月 29, 2019 5:17:11 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Mon Apr 29 17:17:11 CST 2019]; root of context hierarchy
四月 29, 2019 5:17:12 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization
信息: Bean 'myConfig' of type [configure.aop_api.autoProxy.defaultAdvisorAutoProxyCreator.MyConfig$$EnhancerBySpringCGLIB$$7dd99197] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
do something
Bye

Process finished with exit code 0

  1. TargetSource
    TargetSource用于返回实现了连接点的目标对象。一般来说,很少会用到,但是它提供了支持池、热插拔和其他复杂目标的强大方法。
  • Hot-swappable Target Sources
    可以切换AOP代理的目标,并且让调用方保留对它的引用。代码如下:
/**
 * @Author: kuromaru
 * @Date: Created in 9:48 2019/4/30
 * @Description:
 * @modified:
 */
public class MyTarget {

    private int times;

    public void setTimes(int times) {
        this.times = times;
    }

    public void process() {
        System.out.println("I'am doing something important, maybe after " + times + " minutes , I can finish it");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 10:07 2019/4/30
 * @Description:
 * @modified:
 */
public class MyAfterReturnAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        int i = 1;
        while (i <= 5) {
            System.out.println( i + "minute pass...");
            i++;
        }
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 10:10 2019/4/30
 * @Description:
 * @modified:
 */
@Configuration
public class MyConfig {

    @Bean("myTarget")
    public MyTarget myTarget() {
        MyTarget myTarget = new MyTarget();
        myTarget.setTimes(1);
        return myTarget;
    }

    @Bean("myTarget2")
    public MyTarget myTarget2() {
        MyTarget myTarget = new MyTarget();
        myTarget.setTimes(2);
        return myTarget;
    }

    @Bean("mySwap")
    public HotSwappableTargetSource hotSwappableTargetSource() {
        return new HotSwappableTargetSource(myTarget());
    }

    @Bean
    public MyAfterReturnAdvice myAfterReturnAdvice() {
        return new MyAfterReturnAdvice();
    }

    @Bean("myProxy")
    public ProxyFactoryBean proxyFactoryBean() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.addAdvice(myAfterReturnAdvice());
        proxyFactoryBean.setTargetSource(hotSwappableTargetSource());
        return proxyFactoryBean;
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 10:09 2019/4/30
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
        HotSwappableTargetSource hotSwappableTargetSource = (HotSwappableTargetSource) acac.getBean("mySwap");
        MyTarget proxy = (MyTarget) acac.getBean("myProxy");

        MyTarget myTarget = (MyTarget) acac.getBean("myTarget");
        proxy.process();
        myTarget.process();

        System.out.println("======================================================");
        MyTarget myTarget2 = (MyTarget) acac.getBean("myTarget2");
        Object old = hotSwappableTargetSource.swap(myTarget2);
        System.out.println(myTarget == old);
        proxy.process();
        System.out.println(myTarget2 == old);
    }
}

输出结果:

四月 30, 2019 2:07:58 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Tue Apr 30 14:07:58 CST 2019]; root of context hierarchy
I'am doing something important, maybe after 1 minutes , I can finish it
1minute pass...
2minute pass...
3minute pass...
4minute pass...
5minute pass...
I'am doing something important, maybe after 1 minutes , I can finish it
======================================================
true
I'am doing something important, maybe after 2 minutes , I can finish it
1minute pass...
2minute pass...
3minute pass...
4minute pass...
5minute pass...
false

Process finished with exit code 0

从结果可以看到,在swap之后,proxy代理类的target对象变成了myTarget2。需要注意的是,切换前和切换后的target必须是同一类型的,否则会报强转错误。

另外,swap方法的返回值是之前的target类,需要的时候,可以保存起来。

上面的例子中,代理的是两个完全不相关的类,如果是接口的不同实现类呢?

public interface MyInterface {

    void process();
}
/**
 * @Author: kuromaru
 * @Date: Created in 9:48 2019/4/30
 * @Description:
 * @modified:
 */
public class MyTarget implements MyInterface {

    private int times;

    public void setTimes(int times) {
        this.times = times;
    }

    @Override
    public void process() {
        System.out.println("I'am doing something important, maybe after " + times + " minutes , I can finish it");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 9:48 2019/4/30
 * @Description:
 * @modified:
 */
public class MyTarget2 implements MyInterface {

    @Override
    public void process() {
        System.out.println("Let me try it");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 10:10 2019/4/30
 * @Description:
 * @modified:
 */
@Configuration
public class MyConfig {

    @Bean("myTarget")
    public MyTarget myTarget() {
        MyTarget myTarget = new MyTarget();
        myTarget.setTimes(1);
        return myTarget;
    }

    @Bean("myTarget2")
    public MyTarget2 myTarget2() {
        MyTarget2 myTarget2 = new MyTarget2();
        return myTarget2;
    }

    @Bean("mySwap")
    public HotSwappableTargetSource hotSwappableTargetSource() {
        return new HotSwappableTargetSource(myTarget());
    }

    @Bean
    public MyAfterReturnAdvice myAfterReturnAdvice() {
        return new MyAfterReturnAdvice();
    }

    @Bean("myProxy")
    public ProxyFactoryBean proxyFactoryBean() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.addInterface(MyInterface.class);
        proxyFactoryBean.addAdvice(myAfterReturnAdvice());
        proxyFactoryBean.setTargetSource(hotSwappableTargetSource());
        return proxyFactoryBean;
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 10:07 2019/4/30
 * @Description:
 * @modified:
 */
public class MyAfterReturnAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        int i = 1;
        while (i <= 5) {
            System.out.println( i + "minute pass...");
            i++;
        }
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 10:09 2019/4/30
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
        HotSwappableTargetSource hotSwappableTargetSource = (HotSwappableTargetSource) acac.getBean("mySwap");
        MyInterface proxy = (MyInterface) acac.getBean("myProxy");

        MyTarget myTarget = (MyTarget) acac.getBean("myTarget");
        proxy.process();
        myTarget.process();

        System.out.println("======================================================");
        MyTarget2 myTarget2 = (MyTarget2) acac.getBean("myTarget2");
        Object old = hotSwappableTargetSource.swap(myTarget2);
        System.out.println(myTarget == old);
        proxy.process();
        System.out.println(myTarget2 == old);
    }
}

输出结果:

四月 30, 2019 2:32:49 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5387f9e0: startup date [Tue Apr 30 14:32:49 CST 2019]; root of context hierarchy
I'am doing something important, maybe after 1 minutes , I can finish it
1minute pass...
2minute pass...
3minute pass...
4minute pass...
5minute pass...
I'am doing something important, maybe after 1 minutes , I can finish it
======================================================
true
Let me try it
1minute pass...
2minute pass...
3minute pass...
4minute pass...
5minute pass...
false

Process finished with exit code 0

成功了,也是被替换了target类。
注意main方法中获取代理的那句代码

MyInterface proxy = (MyInterface) acac.getBean("myProxy");

不能强转成MyTarget,会报错。因为使用的是JDK代理,代理的其实是接口。

  • Pooling Target Sources
    可以通过CommonsPool2TargetSource来实现池。代码如下:
/**
 * @Author: kuromaru
 * @Date: Created in 9:48 2019/4/30
 * @Description:
 * @modified:
 */
public class MyTarget {

    private int times;

    public void setTimes(int times) {
        this.times = times;
    }

    public void process() {
        System.out.println("I'am doing something important, maybe after " + times + " minutes , I can finish it");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 16:43 2019/4/30
 * @Description:
 * @modified:
 */
public class MyAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("before before before before");
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 16:36 2019/4/30
 * @Description:
 * @modified:
 */
@Configuration
public class MyConfig {

    @Bean("myTarget")
    @Scope("prototype")
    public MyTarget myTarget() {
        return new MyTarget();
    }

    @Bean("myPool")
    public CommonsPool2TargetSource commonsPool2TargetSource() {
        CommonsPool2TargetSource commonsPool2TargetSource = new CommonsPool2TargetSource();
        commonsPool2TargetSource.setTargetBeanName("myTarget");
        commonsPool2TargetSource.setMaxSize(10);
        return commonsPool2TargetSource;
    }

    @Bean("myAdvice")
    public MyAdvice myAdvice() {
        return new MyAdvice();
    }

    @Bean("myProxy")
    public ProxyFactoryBean proxyFactoryBean() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTargetSource(commonsPool2TargetSource());
        proxyFactoryBean.setInterceptorNames("myAdvice");
        return proxyFactoryBean;
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 16:44 2019/4/30
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
        CommonsPool2TargetSource commonsPool2TargetSource = (CommonsPool2TargetSource) acac.getBean("myPool");
        MyTarget myTarget = (MyTarget) acac.getBean("myProxy");
        myTarget.process();
        System.out.println(commonsPool2TargetSource.getMaxSize());

        MyTarget fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
        fromPool.process();
        System.out.println(commonsPool2TargetSource.getActiveCount());
        fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
        fromPool.process();
        System.out.println(commonsPool2TargetSource.getActiveCount());
        }
}

五月 05, 2019 9:47:55 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2cdf8d8a: startup date [Sun May 05 09:47:55 CST 2019]; root of context hierarchy
before before before before
I'am doing something important, maybe after 0 minutes , I can finish it
10
I'am doing something important, maybe after 0 minutes , I can finish it
1
I'am doing something important, maybe after 0 minutes , I can finish it
2

Process finished with exit code 0

根绝输出结果,可以看到,当使用commonsPool2TargetSource.getTarget()来获取目标对象时,获取到的是目标对象本身,而不是代理,并且每次getTarget()操作,都会使activeCount增加1。
那么,当获取的个数超过了池的maxCount咋办呢?看代码:

/**
 * @Author: kuromaru
 * @Date: Created in 16:44 2019/4/30
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
        CommonsPool2TargetSource commonsPool2TargetSource = (CommonsPool2TargetSource) acac.getBean("myPool");
        MyTarget myTarget = (MyTarget) acac.getBean("myProxy");
        myTarget.process();
        System.out.println(commonsPool2TargetSource.getMaxSize());

        MyTarget fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
        fromPool.process();
        System.out.println(commonsPool2TargetSource.getActiveCount());
        for (int i = 2; i <= 10; i++) {
            fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
            fromPool.process();
        }
        System.out.println("=============================================");
        fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
        fromPool.process();
        System.out.println(commonsPool2TargetSource.getActiveCount());
        }
}

输出结果:

五月 05, 2019 9:52:04 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2cdf8d8a: startup date [Sun May 05 09:52:03 CST 2019]; root of context hierarchy
before before before before
I'am doing something important, maybe after 0 minutes , I can finish it
10
I'am doing something important, maybe after 0 minutes , I can finish it
1
I'am doing something important, maybe after 0 minutes , I can finish it
I'am doing something important, maybe after 0 minutes , I can finish it
I'am doing something important, maybe after 0 minutes , I can finish it
I'am doing something important, maybe after 0 minutes , I can finish it
I'am doing something important, maybe after 0 minutes , I can finish it
I'am doing something important, maybe after 0 minutes , I can finish it
I'am doing something important, maybe after 0 minutes , I can finish it
I'am doing something important, maybe after 0 minutes , I can finish it
I'am doing something important, maybe after 0 minutes , I can finish it

看到没有!它堵了!没错,当池里没有可用资源的时候,会阻塞获取资源的操作。因此,当对应的资源完成使命时,要及时释放,如下:

/**
 * @Author: kuromaru
 * @Date: Created in 16:44 2019/4/30
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
        CommonsPool2TargetSource commonsPool2TargetSource = (CommonsPool2TargetSource) acac.getBean("myPool");
        MyTarget myTarget = (MyTarget) acac.getBean("myProxy");
        myTarget.process();
        System.out.println(commonsPool2TargetSource.getMaxSize());

        MyTarget fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
        fromPool.process();
        System.out.println(commonsPool2TargetSource.getActiveCount());
        for (int i = 2; i <= 10; i++) {
            System.out.println("获取资源" + i);
            fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
            fromPool.process();
            System.out.println("释放资源" + i);
            commonsPool2TargetSource.releaseTarget(fromPool);
        }
        System.out.println("=============================================");
        fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
        fromPool.process();
        System.out.println(commonsPool2TargetSource.getActiveCount());
        }
}

五月 05, 2019 9:55:29 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2cdf8d8a: startup date [Sun May 05 09:55:29 CST 2019]; root of context hierarchy
before before before before
I'am doing something important, maybe after 0 minutes , I can finish it
10
I'am doing something important, maybe after 0 minutes , I can finish it
1
获取资源2
I'am doing something important, maybe after 0 minutes , I can finish it
释放资源2
获取资源3
I'am doing something important, maybe after 0 minutes , I can finish it
释放资源3
获取资源4
I'am doing something important, maybe after 0 minutes , I can finish it
释放资源4
获取资源5
I'am doing something important, maybe after 0 minutes , I can finish it
释放资源5
获取资源6
I'am doing something important, maybe after 0 minutes , I can finish it
释放资源6
获取资源7
I'am doing something important, maybe after 0 minutes , I can finish it
释放资源7
获取资源8
I'am doing something important, maybe after 0 minutes , I can finish it
释放资源8
获取资源9
I'am doing something important, maybe after 0 minutes , I can finish it
释放资源9
获取资源10
I'am doing something important, maybe after 0 minutes , I can finish it
释放资源10
=============================================
I'am doing something important, maybe after 0 minutes , I can finish it
2

Process finished with exit code 0

此时,maxActive还是2。而每当执行一次release操作,idleCount的值就会加1,如下:

/**
 * @Author: kuromaru
 * @Date: Created in 16:44 2019/4/30
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);
        CommonsPool2TargetSource commonsPool2TargetSource = (CommonsPool2TargetSource) acac.getBean("myPool");
        MyTarget myTarget = (MyTarget) acac.getBean("myProxy");
        myTarget.process();
        System.out.println(commonsPool2TargetSource.getMaxSize());

        MyTarget fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
        fromPool.process();
        System.out.println(commonsPool2TargetSource.getActiveCount());

        MyTarget fromPool2 = (MyTarget) commonsPool2TargetSource.getTarget();
        fromPool2.process();
        System.out.println(commonsPool2TargetSource.getActiveCount());

        System.out.println("释放资源");
        commonsPool2TargetSource.releaseTarget(fromPool);
        commonsPool2TargetSource.releaseTarget(fromPool2);
        System.out.println("池中空闲的资源数是" + commonsPool2TargetSource.getIdleCount());
        System.out.println("=============================================");
        fromPool = (MyTarget) commonsPool2TargetSource.getTarget();
        fromPool.process();
        System.out.println(commonsPool2TargetSource.getActiveCount());
        System.out.println("池中空闲的资源数是" + commonsPool2TargetSource.getIdleCount());
    }
}

输出结果:

五月 05, 2019 10:10:33 上午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2cdf8d8a: startup date [Sun May 05 10:10:33 CST 2019]; root of context hierarchy
before before before before
I'am doing something important, maybe after 0 minutes , I can finish it
10
I'am doing something important, maybe after 0 minutes , I can finish it
1
I'am doing something important, maybe after 0 minutes , I can finish it
2
释放资源
池中空闲的资源数是2
=============================================
I'am doing something important, maybe after 0 minutes , I can finish it
1
池中空闲的资源数是1

Process finished with exit code 0

申请了两个资源fromPool和fromPool2,当对这两个资源调用releaseTarget方法后,池中就有两个空闲的资源,此时,再通过getTarget申请一个资源,空闲资源则变为1。

  • Prototype Target Sources
    这种target source类似于池。每次方法调用时,都会创建一个新的目标实例。参考spring的测试代码,编写代码如下:
/**
 * Bean that changes state on a business invocation, so that
 * we can check whether it's been invoked
 * @author Rod Johnson
 */
public class SideEffectBean {

   private int count;

   public void setCount(int count) {
      this.count = count;
   }

   public int getCount() {
      return this.count;
   }

   public void doWork() {
      ++count;
   }

}
/**
 * @Author: kuromaru
 * @Date: Created in 10:19 2019/5/5
 * @Description:
 * @modified:
 */
@Configuration
public class MyConfig {

    @Bean("single")
    public SideEffectBean mySideEffectBean1() {
        SideEffectBean sideEffectBean = new SideEffectBean();
        sideEffectBean.setCount(10);
        return sideEffectBean;
    }

    @Bean("prototype")
    @Scope("prototype")
    public SideEffectBean mySideEffectBean2() {
        SideEffectBean sideEffectBean = new SideEffectBean();
        sideEffectBean.setCount(10);
        return sideEffectBean;
    }

    @Bean("myPrototypeTargetSource")
    public PrototypeTargetSource prototypeTargetSource() {
        PrototypeTargetSource prototypeTargetSource = new PrototypeTargetSource();
        prototypeTargetSource.setTargetBeanName("prototype");
        return prototypeTargetSource;
    }

    @Bean
    public MyAdvice myAdvice() {
        return new MyAdvice();
    }

    @Bean("myPrototypeFactoryBean")
    public ProxyFactoryBean myPrototypeFactoryBean() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTargetSource(prototypeTargetSource());
        return proxyFactoryBean;
    }

    @Bean("mySingleFactoryBean")
    public ProxyFactoryBean mySingleFactoryBean() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setInterceptorNames("single");
        return proxyFactoryBean;
    }
}
/**
 * @Author: kuromaru
 * @Date: Created in 10:27 2019/5/5
 * @Description:
 * @modified:
 */
public class Client {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(MyConfig.class);

        SideEffectBean prototype = (SideEffectBean) acac.getBean("myPrototypeFactoryBean");
        System.out.println(prototype.getCount());
        prototype.doWork();
        System.out.println(prototype.getCount());

        SideEffectBean single = (SideEffectBean) acac.getBean("mySingleFactoryBean");
        System.out.println(single.getCount());
        single.doWork();
        System.out.println(single.getCount());
    }
}

输出结果:

五月 05, 2019 5:33:16 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2cdf8d8a: startup date [Sun May 05 17:33:16 CST 2019]; root of context hierarchy
10
10
10
11

Process finished with exit code 0

要注意,被prototypeTargetSource管理的对象一定要设置scope("prototype"),否则会报错。
结果挺有意思,prototype的两次getCount都是返回10,因为每次调用都会新建一个实例。可以这么考虑,prototype是一个代理类,每次需要调用被代理对象的方法时,都需要invoke,此时就需要获取一个实例,而获取实例是通过getTarget来实现的,根据API的说明,每次调用getTarget都会得到一个新的实例。

  • ThreadLocal Target Sources
    ThreadLocalTargetSource是对象池的一个备选方案。根据ThreadLocal的语义,每个线程都持有一个目标对象的副本,线程之间避免了冲突。
    具体要怎么用。。。还不知道。后面补上。
  1. 自定义Advice
    自定义通知类型需要实现Advice接口。

到此,spring的IOC和AOP都已经简单整理了一遍。

posted @ 2020-05-22 10:08  kuromaru  阅读(249)  评论(0)    收藏  举报