@ComponentScan在spring中无效的原因分析及解决方案
目录
- @ComponentScan在spring中无效
- 查了大量资料之后,找到了原因
- @Component和@ComponentScan常规理解
- @Component和@ComponentScan的联系
- @SpringBootApplication和@ComponentScan,扫描包的区别
@ComponentScan在spring中无效
在我实现第一个spring AOP程序的时候,我按照主流的推荐,采用注解@ComponentScan @Aspect @Before 来实现一个切面。
让我十分纳闷的是。 我的程序始终无法正确调用到通知。而且我的通知和主流的毫无差别。代码如下:
通知类,其中定义了切面:
package com.bfytech.spring_8_bean3; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class Advice { @Before("execution(* com.bfytech.spring_8_bean3.Person.getName(..))") public void logBeforeFunction() { System.out.println("function begin"); } @After("execution(* com.bfytech.spring_8_bean3.Person.*(..))") public void logAfterFunction() { System.out.println("function end"); } }
业务类:
package com.bfytech.spring_8_bean3; import org.springframework.stereotype.Component; @Component public class Person { private String name; private int age; public String getName() { System.out.println("getName..."); return name; } public void setName(String name) { this.name = name; System.out.println("setName..."); } public int getAge() { System.out.println("getAge..."); return age; } public void setAge(int age) { System.out.println("setAge..."); this.age = age; } }
Bean配置类:
package com.bfytech.spring_8_bean3; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan public class BeanConfig { @Bean public Advice advice() { return new Advice(); } }
AppicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd"> </beans>
最后的调用类App
package com.bfytech.spring_8_bean3; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; /** * Hello world! * */ public class App { public static void main( String[] args ) { System.out.println( "Hello World!" ); ApplicationContext context = new FileSystemXmlApplicationContext("ApplicationContext.xml"); Person person = (Person) context.getBean(Person.class); person.setName("Tony"); person.setAge(88); System.out.println(person.getName()); System.out.println(person.getAge()); } }
郁闷之余。做了大量尝试,后来发现在ApplicationContext.xml中添加如下行:
<context:component-scan base-package="com.bfytech.spring_8_bean3"></context:component-scan>
之后可以正常把AOP启动起来。
查了大量资料之后,找到了原因
原来很多资料中把xml配置和注解配置混淆在一起了!
当你采用xml配置的时候,则ApplicationContext.xml的内容会生效。但是前提是你需要采用FileSystemXmlApplicationContext或者ClassPathXmlApplicationContext去读取这个xml,配置才会生效!同时@ComponentScan会被忽略!
而当你采用注解配置的时候,则你应该使用AnnotationConfigApplicationContext来加载,这时配置类的中的@ComponentScan就会生效。
修改代码App.java为
package com.bfytech.spring_8_bean3; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; /** * Hello world! * */ public class App { public static void main( String[] args ) { System.out.println( "Hello World!" ); ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); Person person = (Person) context.getBean(Person.class); person.setName("Tony"); person.setAge(88); System.out.println(person.getName()); System.out.println(person.getAge()); } }
运行结果正常了!
顺便说,还有一个坑。execution表达式因为没有编译时检查,任何标点符号的错误也会在运行时忽略(??我很纳闷,为什么不抛异常),所以需要反复检查。比如说下面的表达式,你觉得有错么?
@Before("execution(* com.bfytech.spring_8_bean3.*.*(**))")
这个表达式是错误的,因为(**)应该是(..).而运行时这个不会报任何错误。但是切片的代码不会运行.....
@Component和@ComponentScan常规理解
@Component和@ComponentScan的联系
@Component 这个注解的作用是把我们写的bean注入到容器中,以供使用。
@ComponentScan 注解的作用则是扫描包中的bean(比如:Spring不知道你定义了某个bean除非它知道从哪里可以找到这个bean,ComponentScan做的事情就是告诉Spring从哪里找到bean),由你来定义哪些包需要被扫描。
一旦你指定了,Spring将会将在被指定的包及其下级包中寻找bean,这两个注解进行配合使用。
@SpringBootApplication和@ComponentScan,扫描包的区别
如果你的其他包都在使用了@SpringBootApplication注解的main app所在的包及其下级包,则你什么都不用做,SpringBoot会自动帮你把其他包都扫描了如果你有一些bean所在的包,不在main app的包及其下级包,那么你需要手动加上@ComponentScan注解并指定那个bean所在的包。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。