回调注入
在Spring容器中一般以Aware作为结尾命名的对象都是能够接收一些特殊的回调通知功能,因此在Spring回调相关对象的时候可以注入一些依赖,从而实现基于回调的依赖注入。
代码案例如下:
package org.idea.spring.dependency.inject.aware; import org.idea.spring.ioc.bean.Person; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; /** * @Author linhao * @Date created in 11:11 上午 2021/4/23 */ public class AwareInjectDemo implements BeanFactoryAware, ApplicationContextAware { private BeanFactory beanFactory; private ApplicationContext applicationContext; public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(); annotationConfigApplicationContext.register(AwareInjectDemo.class); annotationConfigApplicationContext.refresh(); AwareInjectDemo awareInjectDemo = annotationConfigApplicationContext.getBean(AwareInjectDemo.class); Person person = (Person) awareInjectDemo.beanFactory.getBean("person"); System.out.println(annotationConfigApplicationContext.getBeanFactory() == awareInjectDemo.beanFactory); //当前的上下文和回调注入的上下文是同一个对象 System.out.println(awareInjectDemo.applicationContext == annotationConfigApplicationContext); System.out.println(person); annotationConfigApplicationContext.close(); } @Bean public Person person(){ return new Person(1,"idea"); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } 复制代码
关于Spring内部的依赖注入类型大致整理了一下如下所示:
而在实际使用过程中,我们还有可能会遇到一些复杂参数的注入,这里我也做了一些相关的技术整理。
特殊格式参数的注入
枚举字段注入
基于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="user" class="org.idea.spring.dependency.inject.type.User"> <property name="id" value="1"></property> <property name="name" value="idea"></property> <property name="age" value="11"></property> <property name="cities" value="WUHAN,BEIJING,SHANGHAI" ></property> </bean> </beans> 复制代码
代码案例:
package org.idea.spring.dependency.inject.type; import javax.annotation.PostConstruct; import java.util.Arrays; public class User { private int id; private String name; private Integer age; private City[] cities; public User() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public City[] getCities() { return cities; } public void setCities(City[] cities) { this.cities = cities; } @PostConstruct public void init(){ System.out.println("this is init"); } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", cities=" + Arrays.toString(cities) + '}'; } } 复制代码
枚举对象
package org.idea.spring.dependency.inject.type; public enum City{ WUHAN, BEIJING, SHANGHAI; City() { } } 复制代码
注入代码案例
package org.idea.spring.dependency.inject.type; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; /** * 关于枚举的注入 * * @Author linhao * @Date created in 12:28 下午 2021/4/23 */ public class EnumInjectDemo { public static void main(String[] args) { DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory); String xmlPath = "classpath:/META-INF/type-independency-inject.xml"; xmlBeanDefinitionReader.loadBeanDefinitions(xmlPath); User user = defaultListableBeanFactory.getBean(User.class); System.out.println(user); } } 复制代码
List集合的注入
代码案例:
package org.idea.spring.dependency.inject.type; import java.util.List; /** * @Author linhao * @Date created in 3:51 下午 2021/4/23 */ public class ListObject { private Integer id; private String username; private List<String> list; public ListObject() { } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } public ListObject(Integer id, String username, List<String> list) { this.id = id; this.username = username; this.list = list; } } 复制代码
针对list类型参数的依赖注入
package org.idea.spring.dependency.inject.type; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; /** * @Author linhao * @Date created in 3:53 下午 2021/4/23 */ public class ListObjectInjectDemo { public static void main(String[] args) { String xmlPath = "classpath:/META-INF/type-list-independency-inject.xml"; DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory); xmlBeanDefinitionReader.loadBeanDefinitions(xmlPath); ListObject listObject = defaultListableBeanFactory.getBean(ListObject.class); System.out.println(listObject); } } 复制代码
资源文件的注入
关于资源这块的注入我们需要先引入一些properties文件作为基础
properties内容
user.passowrd=10001 user.email=idea@qq.com 复制代码
对应的Bean对象中使用org.springframework.core.io.Resource作为引用。
public class ResourceObject { private Long id; private String name; private Integer age; private Resource configFileResource; //gett sett 省略 } 复制代码
而对应的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="user" class="org.idea.spring.dependency.inject.type.ResourceObject"> <property name="id" value="1"></property> <property name="name" value="idea"></property> <property name="age" value="11"></property> <property name="configFileResource" value="classpath:/META-INF/user-config.properties" ></property> </bean> </beans> 复制代码
Qualifier注解的注入
这里介绍下Qualifier这款注解。
我们常用的@Autowired注解是根据类型进行自动装配的,如果Spring的上下文中按照类型匹配出现了多个重复的bean的时候,就会抛出异常。例如下边这段代码中,存在user1,user2两个Bean,但是他们的Bean类型都是User,因此在进行自动装配的时候就会抛出异常,此时如果多加入一个@Qualifier注解,就能够同时支持@Autowired区分Bean名称来进行注入的效果了。
但是我个人习惯一般是比较推崇使用@Resource方式的注入,而且官方也并不是特别推崇@Autowired 注解。
package org.idea.spring.dependency.inject.type; import org.idea.spring.ioc.bean.Person; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import javax.annotation.Resource; /** * @Author linhao * @Date created in 4:02 下午 2021/4/23 */ public class QualifierInjectDemo { @Autowired // @Resource //按照user2的名字进行字段注入,这里会注入user2的对象实例 //如果加入了@Resource注解的话,就会注入user1对象,而且优先级比user2要高 @Qualifier("user2") private User user1; // @Autowired @Resource private User user2; @Autowired @Qualifier //这里带有@Qualifier注解之后,会自动注入person() 这个bean private Person person; public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(QualifierInjectDemo.class); applicationContext.refresh(); QualifierInjectDemo beanDemo = applicationContext.getBean(QualifierInjectDemo.class); System.out.println("user1:" + beanDemo.user1); System.out.println("user2:" + beanDemo.user2) ; System.out.println("普通:" + beanDemo.user1); System.out.println("person 分组:"+beanDemo.person); applicationContext.close(); } @Bean @Qualifier public Person person(){ return new Person(1,"idea"); } @Bean public Person femalePerson(){ return new Person(2,"idea2"); } @Bean public User user1(){ return createUser("user1"); } @Bean public User user2(){ return createUser("user2"); } private static User createUser(String name) { User user = new User(); user.setName(name); return user; } } 复制代码
总结思考
本篇文章主要还是以实践为主,因为我个人觉得在实际工作中,用得最多方式也是
@Resource,
@Autowired这两种基于字段的注入方式,对于Spring容器有过二次开发的朋友可能会对Api的注入方式了解颇深。
在通过上述的种种实践案例分析之后,再回过头来进行思考。
依赖注入和依赖查找的比对
相比于主动查找来说,依赖注入总是能够一步到位地将所需要的bean给注入到容器里面,从而不需要后边再做主动查找,更加简单方便。依赖查找中肯定是要使用相应的api接口去主动查询,这对于业务代码是具有一定的侵害性,所以两种方案的选择需要进过自己去深入琢磨。
Setter注入和构造器注入有什么差别
对比两种方式来说,我个人认为都有好坏
1.首先构造器注入有着很明显的先后顺序限制,它和setter注入不同,setter注入的时候通常是由调用方来限制先后顺序的,没法像构造器一样有序地控制注入顺序。
2.构造器注入的方式来说,如果一个类里面需要注入的bean过多的时候,使用构造器注入反而写起来代码会很臃肿,比较难以维护。
3.sett注入的时候,bean可能会有新的变化,也就是说它要比构造器注入更加灵活,动态化控制注入的bean信息。
4.如果某个引入的bean需要在容器启动的时候就被初始化注入,那么使用构造器注入的方式可能更加适合。
5.对于父子继承的对象来说,使用bean的setter注入可以同时影响到子类的注入。
6.构造器注入的方式通常对于注入的bean都是具有统一的状态管理。而且java里面也是推荐一旦对象生成了,后边就不做过多的修改。
Spring内部的bean注入来源有哪些
1.自定义的bean
2.容器内初始化构建好的bean(例如说我们的enviorment对象)
3.容器内建的依赖对象 (例如说我们的beanfactory,无法通过getbean来获取)