Java aop ComponentScan不起作用& AnnotationConfigApplicationContext getBean无法正常工作 [英] Java aop ComponentScan not working & AnnotationConfigApplicationContext getBean not working

查看:428
本文介绍了Java aop ComponentScan不起作用& AnnotationConfigApplicationContext getBean无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了一组简单的类来向朋友展示有关对AOP使用注释(而不是xml config)的知识.我们无法使@ComponentScan正常工作,并且AnnotationConfigApplicationContext getBean也行为不当.我想了解两件事.参见下面的代码:

I wrote a simple set of classes to show a friend about using Annotations for AOP (instead of xml config) . We couldnt get the @ComponentScan to work AND AnnotationConfigApplicationContext getBean too misbehaves. I wanted to understand two things . See Code below :

PersonOperationsI.java

PersonOperationsI.java

package samples.chapter3;

import org.springframework.stereotype.Component;

@Component
public interface PersonOperationsI {

    public String getName();

}

PersonOperations.java

PersonOperations.java

/**
 * 
 */
package samples.chapter3;

import org.springframework.stereotype.Component;

@Component
public class PersonOperations implements PersonOperationsI {


    public String getName() {
        return "";
    }

}

PersonOperationsConfigClass.java

PersonOperationsConfigClass.java

package samples.chapter3;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
//question2  - Below Component Scan didnt work - Test Case failing in setup()
//@ComponentScan(basePackages = {"samples.chapter3"})
@EnableAspectJAutoProxy

public class PersonOperationsConfigClass {

}

PersonOperationsAdvice.java

PersonOperationsAdvice.java

/**
 * 
 */
package samples.chapter3;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class PersonOperationsAdvice {

    /**
     * execution( [Modifiers] [ReturnType] [FullClassName].[MethodName]
    ([Arguments]) throws [ExceptionType])

     * @param joinPoint
     * @return
     */
    @Before("execution(public * samples.chapter3.PersonOperations.getName()))")
    public String beforeGetName(JoinPoint joinPoint) {
        System.out.println("method name = " + joinPoint.getSignature().getName());
        return null;
    }
}

PersonOperationsTest.java

PersonOperationsTest.java

package samples.chapter3;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { PersonOperationsConfigClass.class })
public class PersonOperationsTest {

    //@Autowired
    private PersonOperationsI obj;

    @Before
    public void setUp() {

        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.scan("samples.chapter3");
        ctx.refresh();
        obj = ctx.getBean(PersonOperationsI.class);
//obj = ctx.getBean(PersonOperations.class);//getBean of Child class not working - why ?

        Assert.assertNotNull(obj);
        ctx.close();
    }

    @Test
    public void test() {
        System.out.println(obj.getName());
    }

}

问题1-为什么@componentscan不起作用.如果我在测试用例中不使用AnnotationConfigApplicationContext,而仅依靠@componentscan&自动接线-测试用例中的对象为

Question1 - Why @componentscan doesnt work .If I dont use AnnotationConfigApplicationContext in test case and just rely on @componentscan & autowired - the object in test case is null

问题2-ctx.getBean(PersonOperations.class);//子类的getBean无法正常工作-为什么?

Question2 - ctx.getBean(PersonOperations.class);//getBean of Child class not working - why ?

推荐答案

A1 ,由于用于加载ApplicationContext的组件类." PersonOperationsConfigClass

@Configuration
//@ComponentScan(basePackages = {"samples.chapter3"})
@EnableAspectJAutoProxy

public class PersonOperationsConfigClass {}

测试类获取通过

The test class gets the ApplicationContext created from the component classes specified with the @ContextConfiguration annotation. Since no components were created or auto detected , @Autowired failed.

@Before注释的方法中使用AnnotationConfigApplicationContext时,将以编程方式创建ApplicationContext.ctx.scan("samples.chapter3");进行扫描并自动检测到PersonOperations并带有@Component注释. obj引用已设置为代码obj = ctx.getBean(PersonOperationsI.class);.此对象不是'Autowired'.

When AnnotationConfigApplicationContext was used within a method annotated with @Before , an ApplicationContext was programmatically created.ctx.scan("samples.chapter3"); scanned and auto-deteced PersonOperations annotated with @Component. obj reference got set with the code obj = ctx.getBean(PersonOperationsI.class);. This object was not 'Autowired'.

根据OP中的评论进行更新

Junit 4批注和@ExtendWith(SpringExtension.class)组合对我不起作用.

The Junit 4 annotations and the @ExtendWith(SpringExtension.class) combination is not working for me.

以下测试类成功运行,错误/失败为零. obj是自动接线的,不为null.我使用了来自Junit的相应注释 5.

Following Test class runs successfully with zero errors/failures. obj is autowired and not null. I have used the corresponding annotations from Junit 5.

package rg.app.aop.so.q1;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes= {PersonOperationsConfigClass.class})
public class PersonOperationsTest {

    @Autowired
    private PersonOperationsI obj;

    @BeforeEach
    public void setUp() {
        System.out.println("init ::"+ obj);
        Assertions.assertNotNull(obj);
    }

    @Test
    public void testPersonOps() {
        Assertions.assertNotNull(obj);
    }
}

配置类

package rg.app.aop.so.q1;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = {"rg.app.aop.so.q1"})
public class PersonOperationsConfigClass {

}


A2 以下是我的分析.


A2, Following are my analysis.

请记住,@EnableAspectJAutoProxyproxyTargetClass属性具有默认值"false".此属性确定代理机制:JDK代理(false)或CGLIB代理(true).

Remember , @EnableAspectJAutoProxy has got a default value "false" for proxyTargetClass attribute. This attribute determines the proxying mechanism : JDK proxy (false) or CGLIB proxy (true) .

在这里,带有有效建议的有效Aspect将导致实际代理生效.仅当建议对其产生影响时,组件才会被代理.简而言之,只有在需要时才进行Component的代理.

Here the presence of a valid Aspect with a valid advice results in the actual proxying to kick in. A component will get proxied only when the advice has any effect on it. In short , the proxying of a Component happens only if required.

案例1

何时:@EnableAspectJAutoProxy/@EnableAspectJAutoProxy(proxyTargetClass = false )

  • ctx.getBean(InterfaceType)返回一个bean
  • ctx.getBean(ImplementationClassType)无法返回bean
  • ctx.getBean(InterfaceType) returns a bean
  • ctx.getBean(ImplementationClassType) fails to return a bean

案例2

何时:@EnableAspectJAutoProxy(proxyTargetClass = true )

  • ctx.getBean(InterfaceType)返回一个bean
  • ctx.getBean(ImplementationClassType)返回一个bean
  • ctx.getBean(InterfaceType) returns a bean
  • ctx.getBean(ImplementationClassType) returns a bean

案例3

何时:@EnableAspectJAutoProxy注释不存在

  • ctx.getBean(InterfaceType)返回一个bean
  • ctx.getBean(ImplementationClassType)返回一个bean
  • ctx.getBean(InterfaceType) returns a bean
  • ctx.getBean(ImplementationClassType) returns a bean

情况1 ,启用Spring AOP且proxyTargetClass为false. JDK代理创建接口类型的代理bean. 创建的bean是 InterfaceType 类型,而不是 ImplementationClassType 类型.这解释了为什么ctx.getBean(ImplementationClassType)无法返回bean.

Case 1 , Spring AOP is enabled with proxyTargetClass as false. JDK proxy creates a proxy bean of Interface type. The bean created is of type InterfaceType and not ImplementationClassType . This explains why ctx.getBean(ImplementationClassType) fails to return a bean.

情况2 ,启用Spring AOP并将proxyTargetClass设置为true. CGLIB通过对用@Component注释的类进行子类化来创建代理bean. 创建的bean的类型为 ImplementationClassType ,并具有 InterfaceType 的资格.因此,两个getBean()调用都成功返回了该bean.

Case 2 , Spring AOP is enabled with proxyTargetClass as true . CGLIB creates a proxy bean by subclassing the class annotated with @Component. The bean created is of type ImplementationClassType , as well qualifies as InterfaceType. So both the getBean() calls returns this bean successfully.

案例3

Spring仅在需要任何特殊处理(例如:AOP,事务管理)时创建代理"对象.

Spring only create "proxy" objects if any special processing is required ( eg: AOP , Transaction Management ).

现在有了这种逻辑,因为不存在@EnableAspectJAutoProxy,所以将为使用@Component注释的类创建一个bean,而无需任何代理. 创建的bean的类型为 ImplementationClassType ,并具有 InterfaceType 的资格.因此,两个getBean()调用都成功返回了该bean.

Now with this logic , since @EnableAspectJAutoProxy is absent , a bean gets created for class annotated with @Component without any proxying. The bean created is of type ImplementationClassType , as well qualifies as InterfaceType. So both the getBean() calls returns this bean successfully.

使用以下代码进行分析.

Analysis done with the following code.

package rg.app.aop.so.q1;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AppMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.scan("rg.app.aop.so.q1");
        ctx.refresh();
        System.out.println();
        for(String name:ctx.getBeanNamesForType(PersonOperationsI.class)) {
            System.out.println(name);
        }
        for(String name:ctx.getBeanNamesForType(PersonOperations.class)) {
            System.out.println(name);
        }
        PersonOperationsI obj = ctx.getBean(PersonOperationsI.class);
        System.out.println(obj.getClass());

        obj = ctx.getBean(PersonOperations.class);
        System.out.println(obj.getClass());
        ctx.registerShutdownHook();

    }

}

案例1印刷品

personOperations
class com.sun.proxy.$Proxy18
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'rg.app.aop.so.q1.PersonOperations' available

案例2印刷品

personOperations
personOperations
class rg.app.aop.so.q1.PersonOperations$$EnhancerBySpringCGLIB$$c179e7f2
class rg.app.aop.so.q1.PersonOperations$$EnhancerBySpringCGLIB$$c179e7f2

第3种情况

personOperations
personOperations
class rg.app.aop.so.q1.PersonOperations
class rg.app.aop.so.q1.PersonOperations

希望这会有所帮助

这篇关于Java aop ComponentScan不起作用& AnnotationConfigApplicationContext getBean无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆