In this case(spring 5.0.x version):

public class AppConfig {
 @Bean
 public ProxyFactoryBean proxyFactoryBean(TargetService targetService) {
     ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
     proxyFactoryBean.setProxyTargetClass(true);
     proxyFactoryBean.setTarget(targetService);
     proxyFactoryBean.setInterceptorNames("myBeforeAdvice");
     return proxyFactoryBean;
 }
}

public class MyBeforeAdvice implements MethodBeforeAdvice {

 @Override
 public void before(Method method, Object[] args, Object target) throws Throwable {
     System.out.println("before invoke method [ " + method.getName() + " ], aop before logic invoked");
 }
}
@Priority(12)
public class TargetService {

 public void testAopApi() {
     System.out.println("testAopApi has invoked");
 }
}
public class Entry {

 public static void main(String[] args) {
     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
             TargetService.class,
             MyBeforeAdvice.class,
             AppConfig.class);

     context.getBean(TargetService.class).testAopApi();
 }
}

I need to get the proxy bean of TargetService type by "getBean(Class requiredType)" api, but it throws following exception:

Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.eugene.sumarry.resourcecodestudy.newissue.TargetService' available: Multiple beans found with the same priority ('12') among candidates: [targetService, proxyFactoryBean]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.determineHighestPriorityCandidate(DefaultListableBeanFactory.java:1421)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1027)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:338)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:333)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107)
    at com.eugene.sumarry.resourcecodestudy.newissue.Entry.main(Entry.java:13)

As we know spring has two solution to deal with above situation that is by @Primary and @Priority annotation。

// org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveNamedBean(java.lang.Class<T>, java.lang.Object...)
String candidateName = determinePrimaryCandidate(candidates, requiredType);
if (candidateName == null) {
 candidateName = determineHighestPriorityCandidate(candidates, requiredType);
}

When I replace the @Priority(12) with the @Primary annotation, this problem can be solved perfectly.

But, when I try to use the @Priority annotation to deal with the problem, I don't know how to solve it, because I cannot add @Priority annotation to proxy object.

After a lot of testing, I found that this is indeed a problem. When the ProxyFactoryBean proxies the bean in the Spring container, we will get an above exception if we use the getBean(Class<T> requiredType) API to get the bean.

Comment From: quaff

You should try latest 5.2.x

Comment From: sbrannen

Please note that the example works if you switch to the following lookup.

context.getBeansOfType(TargetService.class).get("proxyFactoryBean").testAopApi();

Does that suit your needs?

Comment From: AvengerEug

Please note that the example works if you switch to the following lookup.

java context.getBeansOfType(TargetService.class).get("proxyFactoryBean").testAopApi();

Does that suit your needs?

Yeah, this is a solution such as official documents aop-api-proxying-intf chapter. I just found this problem when I learning spring aop api. I think this is indeed a problem. If we try to get bean by "getBean(Class requiredType)" api, we will eventually delegate to “getBean(String name)” api. Can Spring-Team provide a way such as PostProcessor to add @Priority annotation to proxy objects to solve this problem? If the cost of fixing this problem is too high, I think we can add some remarks about this problem in the official documents

Comment From: AvengerEug

Please note that the example works if you switch to the following lookup.

java context.getBeansOfType(TargetService.class).get("proxyFactoryBean").testAopApi();

Does that suit your needs?

It also doesn't work in spring 5.2.x version.

Comment From: quaff

When I replace the @Priority(12) with the @Primary annotation, this problem can be solved perfectly.

Which bean(original or proxied) do you get and want?

Comment From: AvengerEug

When I replace the @priority(12) with the @primary annotation, this problem can be solved perfectly.

Which bean(original or proxied) do you get and want?

It depends on whether I put @primary on Targetservice or ProxyFactoryBean. Spring-Framework will return bean that owns @Primary annotation. In case A, will return targetService that is original object. In case B, will return targetService proxy object

// case A
@Primary
public class TargetService {
    public void testAopApi() {
        System.out.println("testAopApi has invoked");
    }
}

@Bean
public ProxyFactoryBean proxyFactoryBean(TargetService targetService) {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setProxyTargetClass(true);
        proxyFactoryBean.setTarget(targetService);
        proxyFactoryBean.setInterceptorNames("myBeforeAdvice");
        return proxyFactoryBean;
}

// --------------------------------------------------------------------------------------

// case B
public class TargetService {
    public void testAopApi() {
        System.out.println("testAopApi has invoked");
    }
}

@Primary
@Bean
public ProxyFactoryBean proxyFactoryBean(TargetService targetService) {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setProxyTargetClass(true);
        proxyFactoryBean.setTarget(targetService);
        proxyFactoryBean.setInterceptorNames("myBeforeAdvice");
        return proxyFactoryBean;
}

There is solution to this problem such as "getBean(String name)" api and @Primary.

There are two solutions in spring source code when we get multiple beanName by "getBean(Class requiredType)" api. source code:

@Nullable
    private <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
    // Get bean names by type
    String[] candidateNames = getBeanNamesForType(requiredType);
    // .............................

    if (candidateNames.length > 1) {
        Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
        for (String beanName : candidateNames) {
                if (containsSingleton(beanName) && args == null) {
                        Object beanInstance = getBean(beanName);
                        candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
                }
                else {
                        candidates.put(beanName, getType(beanName));
                }

                // @Primary annotation strategy
                String candidateName = determinePrimaryCandidate(candidates, requiredType);
                if (candidateName == null) {
                        // @Priority annotation strategy
                        candidateName = determineHighestPriorityCandidate(candidates, requiredType);
                }
        }

       .........
    }
}

I can't solve the problem by @Priority annotation.

Comment From: quaff

Because priority of both beans are equals, what do you think which one should be first? Have you tried promote priority by annotating @Order ?

Comment From: quaff

It seems @Order doesn't affects priority, is this by design or we could treat @Order as negated priority? @sbrannen

Comment From: mdeinum

Chancing the @Order won't help. There is the real object and a class-based proxy of that object. Both are annotated with the same @Ordered (as both are the class). So chancing the @Order value won't help.

Although I would suspect this being a corner-case, it will only happen in the case of one creating your own class based proxies for a bean that has an @Order without one being the @Primary. The solution would be to annotate the proxy with @Primary or don't use the ProxyFactoryBean but use @Aspect to define aspects (as this will replace the bean with a proxy instead of adding a second bean).

Comment From: sbrannen

Thanks for chiming in, @mdeinum.

Although I would suspect this being a corner-case, it will only happen in the case of one creating your own class based proxies for a bean that has an @Order without one being the @Primary. The solution would be to annotate the proxy with @Primary or don't use the ProxyFactoryBean but use @Aspect to define aspects (as this will replace the bean with a proxy instead of adding a second bean).

After all of the discussion here, I was going to make a similar point to your last point.

@AvengerEug, let's look at this from a different perspective.

What exactly are you trying to achieve?

Do you have a concrete need for the existence of both the proxy and the target object as top-level beans in the ApplicationContext?

Phrased differently, what guided you to create the ProxyFactoryBean via an @Bean method?

As @mdeinum hinted at, implementing an @Before advice method in an @Aspect class using AspectJ would be a more elegant solution if your ultimate goal is to have only the proxied/advised TargetService bean in the ApplicationContext.

Comment From: AvengerEug

Chancing the @Order won't help. There is the real object and a class-based proxy of that object. Both are annotated with the same @Ordered (as both are the class). So chancing the @Order value won't help.

Although I would suspect this being a corner-case, it will only happen in the case of one creating your own class based proxies for a bean that has an @Order without one being the @Primary. The solution would be to annotate the proxy with @Primary or don't use the ProxyFactoryBean but use @Aspect to define aspects (as this will replace the bean with a proxy instead of adding a second bean).

Thank you for your advice.

Comment From: AvengerEug

Thanks for chiming in, @mdeinum.

Although I would suspect this being a corner-case, it will only happen in the case of one creating your own class based proxies for a bean that has an @Order without one being the @Primary. The solution would be to annotate the proxy with @Primary or don't use the ProxyFactoryBean but use @Aspect to define aspects (as this will replace the bean with a proxy instead of adding a second bean).

After all of the discussion here, I was going to make a similar point to your last point.

@AvengerEug, let's look at this from a different perspective.

What exactly are you trying to achieve?

Do you have a concrete need for the existence of both the proxy and the target object as top-level beans in the ApplicationContext?

Phrased differently, what guided you to create the ProxyFactoryBean via an @Bean method?

As @mdeinum hinted at, implementing an @Before advice method in an @Aspect class using AspectJ would be a more elegant solution if your ultimate goal is to have only the proxied/advised TargetService bean in the ApplicationContext.

Yean, I agree with @mdeinum. I just found this bug when I learning aop api from Official documents: "Using the ProxyFactoryBean to Create AOP Proxies" chapter. I tried to solve it, but there was nothing I could do. So I created this issue.

Comment From: sbrannen

Thanks for the feedback, @AvengerEug.

In light of that, I am closing this issue as "works as designed".

To avoid the presence of both the proxy and the target object as top-level beans in the ApplicationContext, please either use an auto-proxy facility or Spring's @AspectJ support.