035用Spring工厂类在XML中 获取其他bean的属性值、获取Field值、执行普通方法并且获取返回值

这篇博客介绍了如何在Spring的XML配置中使用工厂类来实现高级依赖配置,包括获取其他bean的属性值、Field值以及执行普通方法并获取返回值。讲解了PropertyPathFactoryBean、FieldRetrievingFactoryBean和MethodInvokingFactoryBean的用法,并提供了相关方法的详细说明和配置示例。

你能写出来的java代码基本上都可以在xml配置,下面介绍一些高级依赖配置

获取其他bean的属性值

利用的工厂类:org.springframework.beans.factory.config.PropertyPathFactoryBean
该类中有如下两个方法:

  • setTargetObject(Object targetObject) :指定要获取哪个实例的属性(当然可以用ref指定其他bean)
  • setPropertyPath(String propertyPath) :指定获取哪个属性

说明:获取某bean的属性底层是通过getter方法来获取,所以对应的属性必须有规范的getter方法。

配置很简单,只要给PropertyPathFactoryBean配置两个property就行

	<bean id="dog" class="com.lyx.service.Dog" p:color="黄色" p:age="3"/>
    <bean id="person" class="com.lyx.service.Person" p:dog-ref="dog"/>
    
	<!--获取dog的color属性
	伪码:theColor -> dog.getColor()-->
    <bean id="theColor" 
    class="org.springframework.beans.factory.config.PropertyPathFactoryBean"
        p:targetObject-ref="dog" p:propertyPath="color"/>
     <!--获取person的dog属性中的age属性
     伪码:theAge -> person.getDog().getAge()-->
    <bean id="theAge"  
    class="org.springframework.beans.factory.config.PropertyPathFactoryBean"
          p:targetObject-ref="person" p:propertyPath="dog.age"/>

也可以用util命名空间的简化配置

<util:property-path path="dog.color" id="theColor2"/>
<util:property-path path="person.dog.age" id="theAge2"/>

获取Field值

利用的工厂类:org.springframework.beans.factory.config.FieldRetrievingFactoryBean
两种情况:
当Field是静态成员变量时:

该类中有如下两个方法:

  • setTargetClass(String targetClass) :指定要获取哪个的static Field
  • setTargetField(String targetField) :指定获取哪个Field

当Field是实例成员变量时:
该类中有如下两个方法:

  • setTargetObject(Object targetObject) :指定要获取哪个实例的Field(当然可以用ref指定其他bean)
  • setTargetField(String targetField) :指定获取哪个Field

说明:获取某bean的field底层是通过 对象.field 的方式获取,所以对应的field必须是public修饰(对于实例Field,这个在实际项目中很少出现,field一般不会用public修饰)

<!--获取static field-->
	 <!--获取Frame的CROSSHAIR_CURSOR
    伪码:cc-> Frame.CROSSHAIR_CURSOR-->
	<bean id="cc" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
        <property name="targetClass" value="java.awt.Frame"/>
        <property name="targetField" value="CROSSHAIR_CURSOR"/>
    </bean>
    <!--上面的配置还可以改成用如下的staticField配置(文中没有说明)-->
    <bean id="cc2" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
        <property name="staticField" value="java.awt.Frame.CROSSHAIR_CURSOR"/>
    </bean>
    
<!--获取实例field-->
	<!--创建实例先-->
    <bean id="person" class="com.lyx.service.Person">
        <property name="dog">
            <bean class="com.lyx.service.Dog" p:color="绿色" p:age="5"/>
        </property>
    </bean>
    <!--获取person的dog
    伪码:theColor -> person.dog-->
    <bean id="theColor" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
        <property name="targetObject" ref="person"/>
        <property name="targetField" value="dog"/>
    </bean>

获取static field 也可以用util命名空间的简化配置(实例field没有提供util方案,也暗示了一般不会直接获取实例field)

<util:constant static-field="java.awt.Frame.CROSSHAIR_CURSOR" id="cc3"/>

执行普通方法并且获取返回值

利用的工厂类:org.springframework.beans.factory.config.MethodInvokingFactoryBean
MethodInvokingFactoryBean有如下三个方法:

同样分为静态方法和实例方法
静态方法:

  • setTargetClass(String targetClass): 指定调用哪个类的static 方法
  • setTargetMethod(String targetMethod): 指定方法名
  • setArguments(Object[] arguments): 指定方法参数

实例方法:

  • setTargetObject(Object targetObject): 指定调用哪个实例的方法(当然可以用ref指定其他bean)
  • setTargetMethod(String targetMethod): 指定方法名
  • setArguments(Object[] arguments): 指定方法参数

Dog.java

public class Dog
{
    private String color;
    private String age;
//省略setter和getter和toString方法
}

Person.java

public class Person
{
    private String name;
    private Dog dog;
    public String hunt(Dog dog,String time)
    {
        System.out.println(name+"在"+time+"带着"+dog+"去打猎!");
        return "兔子";
    }
//省略setter和getter和toString方法
}

beans.xml

	<bean id="dog" class="com.lyx.service.Dog" p:color="黄色" p:age="3"/>
    <bean id="person" class="com.lyx.service.Person" p:name="笨笨" p:dog-ref="dog"/>
    
    <bean id="h" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="person"/>
        <property name="targetMethod" value="hunt"/>
        <property name="arguments" >
            <list>
                <ref bean="dog"/>
                <value>早上</value>
            </list>
        </property>
    </bean>

驱动

public class Driver
{
    public static void main(String[] args)
    {
        var ctx = new ClassPathXmlApplicationContext("beans.xml");
        System.out.println("----------------------------------------------");
        System.out.println("打到一只"+ctx.getBean("h"));
        //输出如下:
        // 笨笨在早上带着Dog{color='黄色', age='3'}去打猎!
		// ----------------------------------------------
		//打到一只兔子
    }
}

这里只演示了实例方法,static 方法基本上一样就不再演示

<think> 我们正在讨论的是在Java项目的main方法获取属性注入对象的方法。属性注入通常由Spring这样的IoC容器管理,但main方法是静态的,而Spring的依赖注入通常作用于容器管理的bean实例。因此,我们需要在main方法获取Spring容器上下文,然后通过上下文获取bean。 根据引用[3]和引用[5],我们可以知道SpringBoot启动中的Main方法如何获取容器,以及如何从容器中获取bean。引用[5]特别提到了使用`getBean`方法,并注意了返回值和配置的型匹配问题。 步骤: 1. 启动SpringApplication,获取应用上下文(ApplicationContext)。 2. 通过ApplicationContext的getBean方法获取所需的bean。 但是,在标准的Spring Boot应用中,main方法通常是程序的入口,我们可以在其中获取应用上下文。例如: ```java @SpringBootApplication public class Application { public static void main(String[] args) { // 启动Spring Boot应用,并返回应用上下文 ApplicationContext context = SpringApplication.run(Application.class, args); // 从上下文中获取bean YourService yourService = context.getBean(YourService.class); // 然后就可以使用yourService了 } } ``` 如果我们需要在main方法获取一个通过属性注入(比如使用@Autowired)的对象,那么该对象必须是Spring容器管理的bean。因此,我们需要确保该bean已经被正确配置(例如,使用@Component、@Service等注解标记),然后就可以通过上面的方式获取。 另外,引用[5]提到:返回值必须是service,强转的也必须是service,getBean的(即applicationContext配置的)必须是ImplService。也就是说,当使用接口和实现时,我们需要注意型匹配。通常,我们使用接口型来获取bean,前提是容器中只有一个该接口的实现。若有多个,则需要使用bean的名称(id)来指定。 示例: 假设有一个接口MyService和其实现MyServiceImpl,我们在容器中注册了MyServiceImpl,那么获取时可以使用: ```java MyService myService = context.getBean(MyService.class); ``` 或者 ```java MyService myService = (MyService) context.getBean("myServiceImpl"); // 使用bean名称,注意默认名称是名首字母小写 ``` 此外,如果我们在非Spring管理的中需要获取bean(比如一个普通的工具),那么我们可以将应用上下文保存为静态变量,以便在任何地方访问。但要注意,这可能会导致上下文在应用启动完成前被访问的问题。 另一种方式是使用ApplicationContextAware接口,让一个bean实现该接口,从而获取应用上下文,并保存到静态变量中。这样,我们就可以在静态方法获取bean了。例如: ```java @Component public class ApplicationContextProvider implements ApplicationContextAware { private static ApplicationContext context; public static ApplicationContext getApplicationContext() { return context; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } } ``` 然后,在main方法或其他静态方法中,我们可以这样获取: ```java YourService yourService = ApplicationContextProvider.getApplicationContext().getBean(YourService.class); ``` 需要注意的是,在main方法中,我们必须确保Spring容器已经启动,因此ApplicationContextProvider的setApplicationContext方法已经被调用,即静态变量context已经被赋。 总结: 在main方法获取属性注入对象的方法: 1. 在Spring Boot入口中,通过SpringApplication.run返回的ApplicationContext直接获取。 2. 使用ApplicationContextAware接口自定义工具,保存应用上下文,然后通过该工具获取bean(适用于非入口中的静态方法)。 注意:在Spring Boot中,我们通常将业务逻辑放在Spring管理的bean中,而不是在main方法中处理。main方法主要用于启动应用。 相关问题: 1. 如何在非Spring管理的获取Spring容器中的bean? 2. Spring Boot启动过程中如何自定义应用上下文? 3. 在main方法获取bean时,如何解决循环依赖问题? 4. 如何确保在Spring容器完全启动后再从静态上下文中获取bean? </think> 根据您的问题描述和提供的引用内容,以下是Java项目中在`main`方法获取属性注入对象的完整解决方案: ### 一、解决方案核心步骤 1. **初始化Spring容器** ```java public static void main(String[] args) { // 创建Spring应用上下文(根据配置文件位置初始化) ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); // 或使用注解配置(Spring Boot方式) ApplicationContext context = SpringApplication.run(MainApplication.class, args); } ``` 参考引用[5]关于XML路径配置的说明,路径需从`resources`目录开始计算[^5] 2. **获取注入对象** ```java // 通过接口获取(推荐) YourService service = context.getBean(YourService.class); // 通过Bean名称获取 YourServiceImpl serviceImpl = (YourServiceImpl) context.getBean("yourBeanName"); ``` 注意引用[5]强调的**型匹配原则**:返回值型需与容器配置的型匹配[^5] 3. **访问注入对象的属性** ```java // 获取特定属性(需考虑访问权限) Field field = service.getClass().getDeclaredField("privateField"); field.setAccessible(true); // 突破私有访问限制 Object value = field.get(service); ``` 引用[1]和[4]详细说明了使用反射访问私有属性的机制[^1][^4] --- ### 二、关键注意事项 1. **final属性处理** 如引用[2]指出,final属性无法通过属性注入,必须在构造方法中初始化[^2]: ```java @Service public class YourService { private final UnmodifiableObject obj; // 构造器注入是唯一可行方案 public YourService(UnmodifiableObject obj) { this.obj = obj; } } ``` 2. **容器依赖问题** 引用[2][3]强调此方案需依赖IoC容器,普通Java项目需先集成Spring框架[^2][^3]。 3. **推荐实践架构** ```mermaid graph TD A[main方法] --> B[初始化Spring容器] B --> C[获取ApplicationContext] C --> D[通过getBean获取目标对象] D --> E[反射访问私有属性] ``` --- ### 三、完整示例代码(Spring Boot) ```java @SpringBootApplication public class MainApplication { public static void main(String[] args) throws Exception { // 初始化容器 ApplicationContext ctx = SpringApplication.run(MainApplication.class, args); // 获取服务Bean UserService userService = ctx.getBean(UserService.class); // 反射获取私有属性 Field emailField = userService.getClass().getDeclaredField("userEmail"); emailField.setAccessible(true); String email = (String) emailField.get(userService); System.out.println("获取的私有属性值: " + email); } } @Service class UserService { @Value("${user.default.email}") // 属性注入 private String userEmail; // 私有属性 } ``` --- ### 相关问题 1. 如何在非Spring环境中实现依赖注入? 2. 反射访问私有属性会破坏封装性,有哪些替代方案? 3. 如何避免在main方法中直接操作Spring容器? 4. 属性注入(setter) vs 构造器注入,在main方法场景下如何选择? 5. Spring Boot的`@SpringBootApplication`注解背后做了哪些容器初始化工作? [^1]: Java反射机制访问私有属性 [^2]: final属性的注入限制 [^3]: Spring Boot属性注入原理 [^4]: Java反射动态访问机制 [^5]: getBean方法型匹配规则
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值