在上一篇的文章中,我们着重讲解了Spring内部的依赖查找这玩意,这一次着重介绍关于spring的依赖注入模块。
关于依赖注入这块,本篇重点介绍实战方面的内容,实战案例的代码有些多,希望在分享结束后能对各位读者对Spring的依赖注入有个更加清晰的认识。
依赖注入的模式
我们通常在使用Spring的时候主要是用注解或者xml的方式进行注入,如果做过基于Spring容器的二次开发的朋友应该还清楚直接基于Spring的API方式进行依赖注入。
依赖注入的模式主要可以分为两个大类别:
- 手动模式的注入
- 自动模式的注入
手动模式
- XML模式的资源配置
- Java注解的配置方式
- API配置方式(一般如果没有对于容器进行开发,使用会比较少)
自动模式
Autowiring 的自动绑定模式
了解清楚了这两个大的模式之后,我们再来继续细分,依赖注入的几种常用类型。
Setter注入
XML模式
代码案例:
PersonBean对象(后边会多次用到这个对象)
package org.idea.spring.dependency.inject.setter; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.idea.spring.ioc.bean.Person; /** * 自动绑定 * * @Author linhao * @Date created in 10:44 下午 2021/4/21 */ @Data @AllArgsConstructor @NoArgsConstructor public class PersonHolder { private Person person; } Person代码 package org.idea.spring.ioc.bean; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @Author idea * @Date created in 10:45 下午 2020/4/30 */ @Data @AllArgsConstructor @NoArgsConstructor public class Person { Integer id; String name; } 复制代码
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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="malePerson" class="org.idea.spring.ioc.bean.Person" > <property name="id" value="1"></property> <property name="name" value="idea"></property> </bean> <bean class="org.idea.spring.dependency.inject.setter.PersonHolder"> <property name="person" ref="malePerson"></property> </bean> </beans> 复制代码
PersonHolder内部引用了一个Person对象。
Spring容器的代码
package org.idea.spring.dependency.inject.setter; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; /** * 基于xml的依赖注入方式案例 * * @Author linhao * @Date created in 8:29 上午 2021/4/20 */ public class InDependencyInjectDemo { public static void main(String[] args) { DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); String xmlPath = "classpath:/META-INF/independency-inject.xml"; xmlBeanDefinitionReader.loadBeanDefinitions(xmlPath); PersonHolder personHolder = beanFactory.getBean(PersonHolder.class); System.out.println(personHolder); } } 复制代码
注解模式
Spring内部代码块
package org.idea.spring.dependency.inject.setter; import org.idea.spring.ioc.bean.Person; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import java.util.Map; /** * 基于注解的依赖注入 * * @Author linhao * @Date created in 10:53 下午 2021/4/21 */ public class AnnotationDependencyInjectDemo { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(AnnotationConfigApplicationContext.class); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(applicationContext); xmlBeanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/bean-ioc.xml"); applicationContext.register(PersonHolder.class); applicationContext.refresh(); Map<String,Person> personMap= applicationContext.getBeansOfType(Person.class); for (String key : personMap.keySet()) { System.out.println(key); } PersonHolder personHolder = applicationContext.getBean(PersonHolder.class); System.out.println(personHolder); applicationContext.close(); } /** * 这里需要先对person的bean进行初始化才能注入 * * @param * @return */ @Bean public PersonHolder personHolder(Person person){ return new PersonHolder(person); } } 复制代码
API模式
基于Spring容器的内部api进行其实也可以实现依赖的注入:
package org.idea.spring.dependency.inject.setter; import org.idea.spring.ioc.bean.Person; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; /** * 通过api的方式进行注入实现 * * @Author linhao * @Date created in 11:11 下午 2021/4/21 */ public class ApiDependencyInjectDemo { @Bean public Person myPerson(){ return new Person(1,"idea"); } public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(ApiDependencyInjectDemo.class); BeanDefinition personBeanDefinition = createUserBeanDefinition(); applicationContext.registerBeanDefinition("personHolder",personBeanDefinition); applicationContext.refresh(); PersonHolder personHolder = applicationContext.getBean(PersonHolder.class); System.out.println(personHolder.getPerson()); applicationContext.close(); } private static BeanDefinition createUserBeanDefinition() { BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(PersonHolder.class); //注意这里的add方法有多种类型,需要注意下细节点 beanDefinitionBuilder.addPropertyReference("person","myPerson"); return beanDefinitionBuilder.getBeanDefinition(); } } 复制代码
上边的三种注入方式主要是基于Bean对象内部的setter方法和getter方法作为基础进行注入引用的,除此之外,其实还可以基于对象的构造函数来实现字段的引入。
构造器注入
XML模式
首先是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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="femalePerson" class="org.idea.spring.ioc.bean.FemalePerson"></bean> <bean class="org.idea.spring.dependency.inject.setter.PersonHolder"> <constructor-arg name="person" ref="femalePerson"></constructor-arg> </bean> </beans> 复制代码
Spring容器的相关代码:
package org.idea.spring.dependency.inject.constructor; import org.idea.spring.dependency.inject.setter.PersonHolder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; /** * @Author linhao * @Date created in 10:21 下午 2021/4/22 */ public class XmlDependencyInjectDemo { public static void main(String[] args) { DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory); xmlBeanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/independency-constructor-inject.xml"); PersonHolder personHolder = defaultListableBeanFactory.getBean(PersonHolder.class); System.out.println(personHolder.getPerson()); } } 复制代码
注解模式
基于注解的实现方式比较简单,这里我也直接贴出代码了:
package org.idea.spring.dependency.inject.constructor; import org.idea.spring.dependency.inject.setter.PersonHolder; import org.idea.spring.ioc.bean.Person; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; /** * @Author linhao * @Date created in 10:25 下午 2021/4/22 */ public class AnnotationDependencyInjectDemo { @Bean public Person femalePerson(){ return new Person(1,"idea2"); } public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(AnnotationDependencyInjectDemo.class); annotationConfigApplicationContext.refresh(); PersonHolder personHolder = annotationConfigApplicationContext.getBean(PersonHolder.class); System.out.println(personHolder); annotationConfigApplicationContext.close(); } /** * 自动会进行bean的注入 * * @param femalePerson * @return */ @Bean public PersonHolder personHolder(Person femalePerson){ return new PersonHolder(femalePerson); } } 复制代码
代码中的personHolder bean在进行初始化的时候会自动通过名称匹配给femalePerson进行依赖注入。
Api模式
如果有遇到过对Spring容器做改造的需求,可能就会需要了解到类似下边的这种代码:
package org.idea.spring.dependency.inject.constructor; import org.idea.spring.dependency.inject.setter.PersonHolder; import org.idea.spring.ioc.bean.Person; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; /** * @Author linhao * @Date created in 10:28 下午 2021/4/22 */ public class ApiDependencyInjectDemo { @Bean public Person myPerson(){ return new Person(1,"idea"); } public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(ApiDependencyInjectDemo.class); BeanDefinition personBeanDefinition = createUserBeanDefinition(); applicationContext.registerBeanDefinition("personHolder",personBeanDefinition); applicationContext.refresh(); PersonHolder personHolder = applicationContext.getBean(PersonHolder.class); System.out.println(personHolder.getPerson()); applicationContext.close(); } private static BeanDefinition createUserBeanDefinition() { //spring官方比较推荐的一种注入方式 BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(PersonHolder.class); //注意这里的add方法有多种类型,需要注意下细节点 beanDefinitionBuilder.addConstructorArgReference("myPerson"); return beanDefinitionBuilder.getBeanDefinition(); } } 复制代码
其实不管是注解还是xml,在底层都是基于使用BeanDefinition的构建,然后再使用Spring容器注册这个BeanDefinition来实现依赖注入的。
字段注入
字段注入的模式就是我们比较常在工作中使用的案例了,常见的注入注解有:
@Autowired
@Resource
例如下边的代码:
package org.idea.spring.dependency.inject.field; import org.idea.spring.dependency.inject.setter.PersonHolder; import org.idea.spring.ioc.bean.Person; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import javax.annotation.Resource; /** * @Author linhao * @Date created in 10:48 下午 2021/4/22 */ public class AnnotationDependencyInjectDemo { @Autowired private PersonHolder personHolder2; @Resource private PersonHolder personHolder; public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(AnnotationDependencyInjectDemo.class); applicationContext.refresh(); AnnotationDependencyInjectDemo annotationDependencyInjectDemo = applicationContext.getBean(AnnotationDependencyInjectDemo.class); System.out.println(annotationDependencyInjectDemo.personHolder); System.out.println(annotationDependencyInjectDemo.personHolder2); //这里面的两个bean都是同一个,因为bean的作用域是一致相同的 System.out.println(annotationDependencyInjectDemo.personHolder == annotationDependencyInjectDemo.personHolder2); applicationContext.close(); } @Bean public PersonHolder personHolder(){ return new PersonHolder(new Person(1,"idea")); } } 复制代码
这里面容易让人有所疑惑的点:
在同一个类里面声明了一个Bean,然后在该Bean中直接引用是否会报错?
经过实践后发现,并不会有影响。
代码案例中的personHolder2和personHolder是否是同一个对象?
如果我们的PersonHolder是单例模式的话,那么就是同一个对象。
方法注入
所谓的方法注入,我个人的理解就是对方法的入参也支持依赖注入的功能。例如下边这段代码案例:
package org.idea.spring.dependency.inject.method; import org.idea.spring.dependency.inject.setter.PersonHolder; import org.idea.spring.ioc.bean.Person; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Scope; import javax.annotation.Resource; /** * spring内部的方法注入实现 * @Author linhao * @Date created in 10:24 上午 2021/4/23 */ public class MethodInjectDemo { private PersonHolder personHolder; private PersonHolder personHolder_resource; @Resource public void initPersonHolderResource(PersonHolder personHolder){ this.personHolder_resource = personHolder; } @Autowired public void initPersonHolder(PersonHolder personHolder){ this.personHolder = personHolder; } public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(MethodInjectDemo.class); annotationConfigApplicationContext.refresh(); MethodInjectDemo methodInjectDemo = annotationConfigApplicationContext.getBean(MethodInjectDemo.class); System.out.println(methodInjectDemo.personHolder); System.out.println(methodInjectDemo.personHolder_resource); //修改bean的作用域则可以判断为不相同 System.out.println(methodInjectDemo.personHolder == methodInjectDemo.personHolder_resource); // annotationConfigApplicationContext annotationConfigApplicationContext.close(); } @Bean @Scope(value = "prototype") public Person femalePerson(){ return new Person(1,"female"); } @Bean @Scope(value = "prototype") public PersonHolder personHolder(Person person){ return new PersonHolder(person); } } 复制代码
这里你会发现我将PersonHolder的@Scope属性做了调整,所以@AutoWired注解和@Resource注解引入的对象就不会是同一个对象。