java bean注入之静态注入

本文详细介绍了Java Bean在Spring框架中的注入方式,包括XML配置中的属性注入、构造方法注入和工厂方法注入,以及注解方式的@Autowired、@Qualifier和@Resource的使用。通过实例展示了如何通过XML配置和注解定义Bean,以及启动Spring容器。文章强调了注解注入的灵活性和便利性,同时也讨论了@Autowired和@Resource的区别及其生效条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java bean之容器调用机制


作为java程序控制层数据交互部分的缓存,java bean的加载方式如上图所示,Spring容器根据xml <bean>标签的写入、java中的@configuration、@bean、@autowrite注解等信息,将相关的java bean的信息注册在容器的注册表中。之后Spring容器会根据注册表的信息将注册表中的bean类进行实例化,并将实例化后的bean对象与java程序中的bean实例绑定放入容器中,形成一个bean的缓存池,当java应用程序需要调用bean实例时,Spring容器会直接根据索引在缓存池中调用相应的bean实例。

1、bean的定义

bean的配置有三种方式:1、在xml文件中配置,2、使用注解定义并自动装配,3、基于java类提供Bean定义信息

 基于xml文件的配置

 对于基于XML的配置,Spring 2.0以后使用Schema的格式,使得不同类型的配置拥有了自己的命名空间,是配置文件更具扩展性。

①默认命名空间:它没有空间名,用于Spring Bean的定义;

②xsi命名空间:这个命名空间用于为每个文档中命名空间指定相应的Schema样式文件,是标准组织定义的标准命名空间;

③aop命名空间:这个命名空间是Spring配置AOP的命名空间,是用户自定义的命名空间。

命名空间的定义分为两个步骤:第一步指定命名空间的名称;第二步指定命名空间的Schema文档样式文件的位置,用空格或回车换行进行分分隔。

在Spring容器的配置文件中定义一个简要Bean的配置片段如下所示:

一般情况下,Spring IOC容器中的一个Bean即对应配置文件中的一个<bean>。其中id为这个Bean的标识,通过容器的getBean()即可获取对应的Bean,在容器中起到定位查找的作用,是外部程序和Spring IOC容器进行交互的桥梁。class属性指定了Bean对应的实现类。getBean的入参包括name、requiredType、args;其中name不可为空,对应的是xml文件中<bean>的id,requiredType可为空,对应的是<bean>标签的class,标明这个bean的类型,传入格式为类对象形式,即 类名.class形式。

使用注解配置信息启动spring容器

Spring提供了一个context的命名空间,它提供了通过扫描类包以应用注解定义Bean的方式:

复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!--①声明context的命名空间-->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd"
         >
    <!--②扫描类包以应用注解定义的Bean-->
   <context:component-scan base-package="com.baobaotao.anno"/>
   <bean class="com.baobaotao.anno.LogonService"></bean>
   <!-- context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/ -->
   <!-- context:component-scan base-package="com.baobaotao">
       <context:include-filter type="regex" expression="com\.baobaotao\.anno.*Dao"/>
       <context:include-filter type="regex" expression="com\.baobaotao\.anno.*Service"/>
       <context:exclude-filter type="aspectj" expression="com.baobaotao..*Controller+"/>
   </context:component-scan -->
</beans>
复制代码

在①处声明context命名空间,在②处即可通过context命名空间的component-scan的base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。

如果仅希望扫描特定的类而非基包下的所有类,你们可以使用resource-pattern属性过滤特定的类,如下所示:

< context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/ >

这里我们将基类包设置为com.baobaotao,默认情况下resource-pattern属性的值为"**/*.class",即基类包里的所有类。这里我们设置为"anno/*.class",则Spring仅会扫描基包里anno子包中的类。

Bean注入

传统方式下是在XML中配置,此时分别有属性注入、构造函数注入和工厂方法注入

属性注入

属性注入即通过setXxx()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式。

属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。

复制代码
package com.baobaotao.anno;

import org.springframework.beans.factory.BeanNameAware;

public class LogonService implements BeanNameAware{

    private LogDao logDao;
    private UserDao userDao;
    private String value; 
public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void setLogDao(LogDao logDao) { this.logDao = logDao; }
    public void setValue(String value) {
        this.value = value;
    }
public LogDao getLogDao() { return logDao; } public UserDao getUserDao() { return userDao; }
    public String getValue() {

 
        return value;
}

}
复制代码

bean.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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd"
       default-autowire="byName"
         >
    <bean id="logDao" class="com.baobaotao.anno.LogDao"/>
    <bean id="userDao" class="com.baobaotao.anno.UserDao"/>
   <bean  id="logonService" class="com.baobaotao.anno.LogonService">
       <property name="logDao" ref="logDao"></property>
       <property name="userDao" ref="userDao"></property>   

 
       <property name="value" value="60000"></property>
</ bean > </ beans >
复制代码

上述xml文件中,Spring JVM读取到相关配置后,首先将UserDao和LogDao进行bean实例化存放在容器中,然后再实例化logonService的时候,会根据property ref中的内容在容器中寻找匹配名称的实例bean注入到logonService的实例中,而value变量为基本类型变量,因此直接根据property中value的值注入logonService实例的变量value中。(注意:Spring只会检查bean中是否有setter方法,而是否有对应的属性变量则不做具体要求,但按照约定俗成的规则我们最好为其设定相应的属性变量。个人理解中,属性注入的方式实际上是根据property中的name的值匹配bean类中的变量名从而进行值注入的

构造方法注入

使用构造函数注入的前提是Bean必须提供带参数的构造函数。例如

复制代码
package com.baobaotao.anno;

import org.springframework.beans.factory.BeanNameAware;

public class LogonService implements BeanNameAware{
    private LogDao logDao;
    private UserDao userDao;
    private String value; 
public LogonService(){} public LogonService(LogDao logDao, UserDao userDao,Sting value) { this.logDao = logDao; this.userDao = userDao;
        this.value = Value;
} public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void setLogDao(LogDao logDao) { this.logDao = logDao; }
    public void setValue(String value) {
        this.value = value;
    }
public LogDao getLogDao() { return logDao; } public UserDao getUserDao() { return userDao; }
 
    public String getValue() {
        return value;
}}
复制代码

bean.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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd"
       default-autowire="byName">

    <bean id="logDao" class="com.baobaotao.anno.LogDao"/>
    <bean id="userDao" class="com.baobaotao.anno.UserDao"/>
   <bean class="com.baobaotao.anno.LogonService">
      <constructor-arg  ref="logDao"></constructor-arg>
       <constructor-arg ref="userDao"></constructor-arg>

 
      <constructor-arg index="2" type="int"><value>200</value> </constructor-arg>
</ bean > </ beans >

利用构造器方式注入bean分类可以有四种方式,根据类型匹配入参、根据索引匹配入参、索引类型联合匹配、自身类型反射入参。

上例中xml <constructor-arg ref="logDao"></constructor-arg>即是反射入参方式。如果bean构造函数入参的类型是可辨别的,由于java反射机制可以获取构造函数入参的类型,即使构造函数的注入不提供类型和索引的信息,Spring依旧可以完成构造函数信息的注入。因为之前实例化了logDao和userDao的bean,因此可以根据其id用ref指引到其在容器中匹配的实例。<constructor-arg index="2" type="String"><value>200</value> </constructor-arg>则是典型的联合匹配,其中index为变量在构造器中入参的索引位置,代表第index个入参(起始索引为0),type为入参的类型,value标签中为注入的bean的值。

当然,在type的值唯一的情况下,可以单独使用type来进行匹配,如上例value变量中,因为入参只有一个String类型,所以可以直接用 < constructor-arg index= "2" type="String"> <value>200</value> < / constructor-arg >
其外,也可以直接用index标签进行匹配, < constructor-arg index= "2" > <value>200</value> < / constructor-arg >

工厂方法注入

非静态工厂方法:

有些工厂方法是非静态的,即必须实例化工厂类后才能调用工厂放。

复制代码
package com.baobaotao.ditype;

public class CarFactory {
   public Car createHongQiCar(){
       Car car = new Car();
       car.setBrand("红旗CA72");
       return car;
   }
   
   public static Car createCar(){
       Car car = new Car();
       return car;
   }
}
复制代码

工厂类负责创建一个或多个目标类实例,工厂类方法一般以接口或抽象类变量的形式返回目标类实例,工厂类对外屏蔽了目标类的实例化步骤,调用者甚至不用知道具体的目标类是什么。

复制代码
<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- 工厂方法-->
    <bean id="carFactory" class="com.baobaotao.ditype.CarFactory" />
    <bean id="car5" factory-bean="carFactory" factory-method="createHongQiCar">
    </bean>


</beans>
复制代码

由于createHongQiCar为非静态方法,因此在调用该方法的时候需要有一个其所在类的实例,上述xml中 <bean id="carFactory" class="com.baobaotao.ditype.CarFactory" />即为实例化该类,随后<bean id="car5" factory-bean="carFactory" factory-method="createHongQiCar">用factory-bean元素指定调用的实例,再用factory-method指定调用的方发,从而创建新的bean。

静态工厂方法:

很多工厂类都是静态的,这意味着用户在无须创建工厂类实例的情况下就可以调用工厂类方法,因此,静态工厂方法比非静态工厂方法的调用更加方便。

复制代码
<?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="car6" class="com.baobaotao.ditype.CarFactory"
        factory-method="createCar"></bean>

</beans>

上述xml运作原理与非静态方法原理类似,只是调用的方法是静态方法createCar,因此只需要用class指定方法所在类,再用factory-bean指定调用的静态方法即可。

2、使用注解的方式

使用注解定义Bean

我们知道,Spring容器成功启动的三大要件分别是:Bean定义信息、Bean实现类以及Spring本身。如果采用基于XML的配置,Bean定义信息和Bean实现类本身是分离的,而采用基于注解的配置方式时,Bean定义信息即通过在Bean实现类上标注注解实现。

下面是使用注解定义一个DAO的Bean:

复制代码
package com.baobaotao.anno;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
//①通过Repository定义一个DAO的Bean

@Component("userDao")
public class UserDao {

}
复制代码

在①处,我们使用@Component注解在UserDao类声明处对类进行标注,它可以被Spring容器识别,Spring容器自动将POJO转换为容器管理的Bean。

它和以下的XML配置是等效的:

<bean id="userDao" class="com.baobaotao.anno.UserDao"/>

除了@Component以外,Spring提供了3个功能基本和@Component等效的注解,它们分别用于对DAO、Service及Web层的Controller进行注解,所以也称这些注解为Bean的衍型注解:(类似于xml文件中定义Bean<bean id=" " class=" "/>

  • @Repository:用于对DAO实现类进行标注;
  • @Service:用于对Service实现类进行标注;
  • @Controller:用于对Controller实现类进行标注;

之所以要在@Component之外提供这三个特殊的注解,是为了让注解类本身的用途清晰化,此外Spring将赋予它们一些特殊的功能。

使用注解配置信息启动spring容器

Spring提供了一个context的命名空间,它提供了通过扫描类包以应用注解定义Bean的方式:

复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!--①声明context的命名空间-->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd"
         >
    <!--②扫描类包以应用注解定义的Bean-->
   <context:component-scan base-package="com.baobaotao.anno"/>
   <bean class="com.baobaotao.anno.LogonService"></bean>
   <!-- context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/ -->
   <!-- context:component-scan base-package="com.baobaotao">
       <context:include-filter type="regex" expression="com\.baobaotao\.anno.*Dao"/>
       <context:include-filter type="regex" expression="com\.baobaotao\.anno.*Service"/>
       <context:exclude-filter type="aspectj" expression="com.baobaotao..*Controller+"/>
   </context:component-scan -->
</beans>
复制代码

在①处声明context命名空间,在②处即可通过context命名空间的component-scan的base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。

如果仅希望扫描特定的类而非基包下的所有类,你们可以使用resource-pattern属性过滤特定的类,如下所示:

< context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/ >

这里我们将基类包设置为com.baobaotao,默认情况下resource-pattern属性的值为"**/*.class",即基类包里的所有类。这里我们设置为"anno/*.class",则Spring仅会扫描基包里anno子包中的类。

基于java类提供Bean定义

在普通的POJO类中只要标注@Configuration注解,就可以为spring容器提供Bean定义的信息了,每个标注了@Bean的类方法都相当于提供了一个Bean的定义信息。

复制代码
package com.baobaotao.conf;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//①将一个POJO标注为定义Bean的配置类
@Configuration
public class AppConf {
        //②以下两个方法定义了两个Bean,以提供了Bean的实例化逻辑
    @Bean
    public UserDao userDao(){
       return new UserDao();    
    }
    
    @Bean
    public LogDao logDao(){
        return new LogDao();
    }
    //③定义了logonService的Bean
    @Bean
    public LogonService logonService(){
        LogonService logonService = new LogonService();
                //④将②和③处定义的Bean注入到LogonService Bean中
        logonService.setLogDao(logDao());
        logonService.setUserDao(userDao());
        return logonService;
    }
}
复制代码

①处在APPConf类的定义处标注了@Configuration注解,说明这个类可用于为Spring提供Bean的定义信息。类的方法处可以标注@Bean注解,Bean的类型由方法返回值类型决定,名称默认和方法名相同,也可以通过入参显示指定Bean名称,如@Bean(name="userDao").直接在@Bean所标注的方法中提供Bean的实例化逻辑。

在②处userDao()和logDao()方法定义了一个UserDao和一个LogDao的Bean,它们的Bean名称分别是userDao和logDao。在③处,又定义了一个logonService Bean,并且在④处注入②处所定义的两个Bean。

因此,以上的配置和以下XML配置时等效的:

<bean id="userDao" class="com.baobaotao.anno.UserDao"/>
<bean id="logDao" class="com.baobaotao.anno.LogDao"/>
<bean id="logService" class="com.baobaotao.conf.LogonService"
    p:logDao-ref="logDao" p:userDao-ref="userDao"/>

基于java类的配置方式和基于XML或基于注解的配置方式相比,前者通过代码的方式更加灵活地实现了Bean的实例化及Bean之间的装配,但后面两者都是通过配置声明的方式,在灵活性上要稍逊一些,但是配置上要更简单一些。

Bean的注解方式注入

 使用@Autowired进行自动注入

Spring通过@Autowired注解实现Bean的依赖注入,下面是一个例子:

复制代码
package com.baobaotao.anno;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
//① 定义一个Service的Bean(不需要在XML中定义Bean)
@Service
public class LogonService implements BeanNameAware{
        //② 分别注入LogDao及UserDao的Bean(不需要在XML中定义property属性注入)
    @Autowired(required=false)
    private LogDao logDao;
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
    
    public LogDao getLogDao() {
        return logDao;
    }
    public UserDao getUserDao() {
        return userDao;
    }
    
    public void setBeanName(String beanName) {
        System.out.println("beanName:"+beanName);        
    }
    
    public void initMethod1(){
        System.out.println("initMethod1");
    }
    public void initMethod2(){
        System.out.println("initMethod2");
    }
    
}
 
复制代码

在①处,我们使用@Service将LogonService标注为一个Bean,在②处,通过@Autowired注入LogDao及UserDao的Bean。@Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入到@Autowired标注的变量中。

使用@Autowired的required属性

如果容器中没有一个和标注变量类型匹配的Bean,Spring容器启动时将报NoSuchBeanDefinitionException的异常。如果希望Spring即使找不到匹配的Bean完成注入也不用抛出异常,那么可以使用@Autowired(required=false)进行标注:

@Service
public class LogonService implements BeanNameAware{
    @Autowired(required=false)
    private LogDao logDao;
        ...
}

默认情况下,@Autowired的required属性的值为true,即要求一定要找到匹配的Bean,否则将报异常。

使用@Qualifier指定注入Bean的名称

如果容器中有一个以上匹配的Bean时,则可以通过@Qualifier注解限定Bean的名称,如下所示:

复制代码
@Service
public class LogonService implements BeanNameAware{
    @Autowired(required=false)
    private LogDao logDao;
//①注入名为UserDao,类型为UserDao的Bean @Autowired @Qualifier(
"userDao") private UserDao userDao; }
复制代码

这里假设容器有两个类型为UserDao的Bean,一个名为userDao,另一个名为otherUserDao,则①处会注入名为userDao的Bean。

 对类方法进行标注

@Autowired可以对类成员变量及方法进行标注,下面我们在类的方法上使用@Autowired注解:

复制代码
package com.baobaotao.anno;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class LogonService implements BeanNameAware{
    
    private LogDao logDao;
    private UserDao userDao;
    
    
    @Autowired
    public void setLogDao(LogDao logDao) {
        this.logDao = logDao;
    }
    
    @Autowired
    @Qualifier("userDao")
    public void setUserDao(UserDao userDao) {
        System.out.println("auto inject");
        this.userDao = userDao;
    }
    
}
复制代码

上例中,当所有的bean被定义加载完毕存储在容器中后,实例化LongService的时候容器会调用@Autowired标注的方法在容器中寻找与入参类型匹配的bean注入到LongService的实例中。

如果一个方法拥有多个入参,在默认情况下,Spring自动选择匹配入参类型的Bean进行注入。Spring允许对方法入参标注@Qualifier以指定注入Bean的名称,如下所示:

    @Autowired
    public void init(@Qualifier("userDao")UserDao userDao,LogDao logDao){
        System.out.println("multi param inject");
        this.userDao = userDao;
        this.logDao =logDao;
    }

在以上例子中,UserDao的入参注入名为userDao的Bean,而LogDao的入参注入LogDao类型的Bean。

一般情况下,在Spring容器中大部分的Bean都是单实例的,所以我们一般都无须通过@Repository、@Service等注解的value属性为Bean指定名称,也无须使用@Qualifier按名称进行注入。

 对标准注解的支持

此外,Spring还支持@Resource和@Inject注解,这两个标准注解和@Autowired注解的功能类型,都是对类变量及方法入参提供自动注入的功能。@Resource要求提供一个Bean名称的属性,如果属性为空,则自动采用标注处的变量名或方法名作为Bean的名称。

复制代码
package com.baobaotao.anno;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

import org.springframework.stereotype.Component;

@Component
public class Boss {
    
    private Car car;
    
    public Boss(){
        System.out.println("construct...");
    }
    
    @Resource("car")
    private void setCar(Car car){
        System.out.println("execute in setCar");
        this.car = car;
    }
}
复制代码

这时,如果@Resource未指定"car"属性,则也可以根据属性方法得到需要注入的Bean名称。可见@Autowired默认按类型匹配注入Bean,@Resource则按名称匹配注入Bean。而@Inject和@Autowired一样也是按类型匹配注入的Bean的,只不过它没有required属性。可见不管是@Resource还是@Inject注解,其功能都没有@Autowired丰富,因此除非必须,大可不必在乎这两个注解。(类似于Xml中使用<constructor-arg ref="logDao"></constructor-arg>或者<property name="logDao" ref="logDao"></property>进行注入,如果使用了@Autowired或者Resource等,这不需要在定义Bean时使用属性注入和构造方法注入了)

 关于Autowired和@Resource

1.@Autowired注入是按照类型注入的,只要配置文件中的bean类型和需要的bean类型是一致的,这时候注入就没问题。但是如果相同类型的bean不止一个,此时注入就会出现问题,Spring容器无法启动。 
2.@Resourced标签是按照bean的名字来进行注入的,如果我们没有在使用@Resource时指定bean的名字,同时Spring容器中又没有该名字的bean,这时候@Resource就会退化为@Autowired即按照类型注入,这样就有可能违背了使用@Resource的初衷。所以建议在使用@Resource时都显示指定一下bean的名字@Resource(name="xxx") 

 让@Resource和@Autowired生效的几种方式

1.在xml配置文件中显式指定 

<!-- 为了使用Autowired标签,我们必须在这里配置一个bean的后置处理器 -->  
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />   
      
    <!-- 为了使用@Resource标签,这里必须配置一个后置处理器 -->  
    <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />   

2.在xml配置文件中使用context:annotation-config 它是对已注册Bean的进行操作的配置,也就是说,Bean需要首先通过某种方式(比如Xml配置,或者其他注解)被注册,然后使用这个配置,可以对已注册的Bean进行进一步操作(比如注入到某个类的内部),也就是说,这个配置是用于“激活”已注册的Bean的,让已注册的Bean开始工作。 

<context:annotation-config />

3.在xml配置文件中使用context:component-scan 首先有和<context:annotation-config/>一样的作用,此外,它还可以扫描指定包下的类,将拥有注解的它们注册到Spring中。 

<context:component-scan base-package="com.baobaotao.anno"/>

如果用<context:annotation-config/>,我们还需要配置Xml注册Bean,而使用<context:component-scan />的话,注册的步骤都免了,当然前提是我们对需要扫描的类使用的注解(比如@Componet,@Service),而如果同时使用两个配置的话,<context:annotation-config/>会被忽略掉。


参考引入:spring中bean配置和bean注入

<think>好的,我现在需要回答用户的问题:如何在Spring框架中将Bean注入静态方法中。首先,我得回忆一下Spring的依赖注入机制,通常Spring是通过容器管理Bean的生命周期和依赖关系的,但静态方法属于类级别,而Spring默认是基于实例的依赖注入,所以这里可能需要特殊处理。 用户提到了静态方法注入Bean,我需要查阅相关的Spring文档或者常见做法。根据之前提供的引用内容,比如引用[1]提到静态工厂方法创建Bean,引用[2]和[5]讨论了在静态属性中获取Bean的方法,还有使用工具类或者非静态setter方法设置静态字段。这些都是可能的解决方案。 首先,我需要明确静态方法不能直接使用@Autowired注解,因为Spring无法直接注入静态变量。所以必须通过间接的方式。根据引用[5],方法一是通过非静态的setter方法注入静态变量中,比如在非静态方法上使用@Autowired,然后在方法中将实例赋值给静态变量。这种方法可行,但需要确保Spring能调用到这个setter方法。 第二种方法是使用@PostConstruct,在Bean初始化完成后,通过一个非静态的方法来初始化静态变量。这可能需要在类加载后执行某些操作,确保Spring上下文已经准备好。 第三种方法是使用ApplicationContextAware接口,创建一个工具类获取ApplicationContext,然后通过上下文获取Bean。引用[2]和[4]提到了这种方式,通过实现ApplicationContextAware,保存上下文到静态变量中,然后提供静态方法获取Bean。 另外,引用[3]展示了在定时任务中调用静态方法,可能结合Spring的调度功能,但这不是直接注入,而是通过任务触发调用工具类获取Bean。 还需要考虑线程安全和上下文加载时机的问题,确保在调用静态方法时ApplicationContext已经初始化完成。比如,工具类需要在Spring容器启动后才会被正确初始化,否则可能导致空指针异常。 总结下来,常见的实现方式包括:使用ApplicationContextAware工具类、通过setter方法间接注入静态变量、使用@PostConstruct注解初始化静态变量。需要逐一解释这些方法的步骤和注意事项,确保用户能够理解并正确实施。 最后,验证这些方法是否符合Spring的最佳实践,是否存在潜在的问题,比如内存泄漏或上下文未正确加载的情况。例如,ApplicationContextAware的方式可能会导致工具类与Spring框架紧耦合,但在某些情况下是必要的。</think>### Spring框架中静态方法注入Bean的实现方式 在Spring框架中,由于依赖注入(DI)是基于对象实例的,直接通过`@Autowired`注入静态变量或静态方法是不可行的[^5]。以下是五种常见的实现方式,均参考了Spring的扩展机制和设计模式: --- #### 方式1:通过工具类获取ApplicationContext(推荐) 1. **创建工具类**:实现`ApplicationContextAware`接口,将上下文保存到静态变量。 ```java @Component public class SpringBeanUtil implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext ctx) { context = ctx; } public static <T> T getBean(Class<T> beanClass) { return context.getBean(beanClass); } } ``` 2. **在静态方法中调用**: ```java public class StaticClass { public static void execute() { UserService service = SpringBeanUtil.getBean(UserService.class); service.doSomething(); } } ``` *优点*:代码简洁,适用于全局调用[^2][^4]。 *注意*:需确保工具类已被Spring管理(添加`@Component`)。 --- #### 方式2:通过非静态Setter方法注入静态变量 在非静态方法中通过`@Autowired`赋值给静态变量: ```java @Component public class StaticHolder { private static UserService userService; @Autowired public void setUserService(UserService service) { StaticHolder.userService = service; } } ``` *原理*:Spring实例化`StaticHolder`时会调用`setUserService`,将实例赋值给静态变量[^5]。 --- #### 方式3:使用@PostConstruct初始化静态变量 结合`@Autowired`和`@PostConstruct`注解: ```java @Component public class StaticInitializer { @Autowired private UserService instanceService; private static UserService staticService; @PostConstruct public void init() { staticService = instanceService; } public static void staticMethod() { staticService.doSomething(); } } ``` *说明*:`@PostConstruct`确保在Bean初始化完成后执行赋值操作[^5]。 --- #### 方式4:通过XML配置静态工厂方法 1. **定义静态工厂类**: ```java public class ServiceFactory { public static UserService createUserService() { return new UserServiceImpl(); } } ``` 2. **XML配置**: ```xml <bean id="userService" class="com.example.ServiceFactory" factory-method="createUserService"/> ``` *适用场景*:需要兼容传统XML配置的项目[^1]。 --- #### 方式5:结合@Configuration静态加载 在配置类中显式注入: ```java @Configuration public class StaticConfig { private static UserService userService; @Autowired public StaticConfig(UserService service) { StaticConfig.userService = service; } } ``` *注意*:需确保配置类优先加载,避免静态方法调用时变量未初始化。 --- ### 关键问题与解决方案 | 问题类型 | 解决方案 | |------------------------|-----------------------------------| | 上下文未初始化 | 使用`@DependsOn`指定依赖顺序 | | 多线程环境下的线程安全 | 结合`@Scope("prototype")`或锁机制 | | 工具类与框架耦合 | 通过接口隔离,避免直接依赖具体类 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值