问题描述
我编写了一组简单的类来向朋友展示有关对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 {}
测试类获取通过 @ ContextConfiguration 批注.由于未创建组件或未自动检测到任何组件,因此@Autowired
失败.
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.
请记住,@EnableAspectJAutoProxy
的proxyTargetClass
属性具有默认值"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 beanctx.getBean(ImplementationClassType)
fails to return a bean
案例2
何时:@EnableAspectJAutoProxy(proxyTargetClass = true )
-
ctx.getBean(InterfaceType)
返回一个bean -
ctx.getBean(ImplementationClassType)
返回一个bean
ctx.getBean(InterfaceType)
returns a beanctx.getBean(ImplementationClassType)
returns a bean
案例3
何时:@EnableAspectJAutoProxy
注释不存在
-
ctx.getBean(InterfaceType)
返回一个bean -
ctx.getBean(ImplementationClassType)
返回一个bean
ctx.getBean(InterfaceType)
returns a beanctx.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无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!