@ComponentScan在spring中无效的原因分析及解决方案

2022-07-21,,,,

目录
  • @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所在的包。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

《@ComponentScan在spring中无效的原因分析及解决方案.doc》

下载本文的Word格式文档,以方便收藏与打印。