1、spring注入
spring的注入,就是对bean对象的属性设置值
2、为什么需要注入
这里我们可以先看一段代码
@Test public void testInject(){ ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("/applicationContext.xml"); Person person = cxt.getBean("person", Person.class); person.setAge(18); person.setName("zhangsan"); System.out.println(person); }
小伙伴们可以发现,我们通过getBean()取出来的对象,想要给这个对象赋值,是使用的setXX()方法,这也是一种耦合。那么可不可以让spring在初始化的时候就将bean对象给我们设置好,getBean()放回的时候,直接给我们一个完整的对象呢??
答案肯定是可以的,这里就需要引入spring的注入
3、spring的注入
spring的注入其实也非常简单,我们只需要在applicationContext.xml文件中进行配置下即可
<bean id="person" name="p1" class="com.wx.study.Person"> <property name="name"> <value>zhangsan</value> </property> <property name="age"> <value>18</value> </property> </bean>
通过截图发现,spring确实帮我们将属性设置进去了。我们这么做的好处,就是解耦
注意点:
- 我们的实体类中一定要添加setXX()方法,不然spring是没有办法帮助我们注入属性
3.1注入的原理
原来我们的setXX()方法约等于我们配置中的<property>配置,spring的底层是调用我们实体类的setXX()方法,所以我们想要spring帮助我们set注入,就一定需要生成set方法。用配置来实现注入的好处是解耦。
4、spring的注入方式
4.1、set注入
set注入分为注入两种:
- jdk内置对象
- 用户自定义对象
4.1.1、jdk内置对象
java中八种基础对象加上String类型的字符串,在配置文件中都是使用<property><values>XXX</values></property>来表示
private String name; private int id; private boolean isBoolean; private float score; private char sex; private double money; private long aLong; private byte aByte; private short aShort;
<property name="name"> <value>xiaoming</value> </property> <property name="id" value="20"></property> <property name="aByte" value="1"></property> <property name="aLong" value="10000"></property> <property name="boolean" value="false"></property> <property name="money" value="20.8"></property> <property name="score" value="100.12"></property> <property name="sex" value="1"></property> <property name="aShort" value="1"></property>
java中集合对象的注入方式
根据不同的集合类型,我们注入的配置也是不一样的。
private int[] ints; private List<String> address; private Set<String> sets; private Map<String, String> maps;
<property name="ints"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </property> <property name="address"> <list> <value>123</value> <value>456</value> <value>789</value> </list> </property> <property name="sets"> <set> <value>123</value> <value>456</value> <value>789</value> </set> </property> <property name="maps"> <map> <entry > <key><value>1</value></key> <value>111</value> </entry> <entry> <key><value>2</value></key> <value>222</value> </entry> <entry> <key><value>3</value></key> <value>333</value> </entry> </map> </property>
4.1.2、用户自定义对象
使用用户自定义对象,我们需要使用<ref bean=""></ref>标签
private Person person;
<property name="person" > <ref bean="person"></ref> </property>
4.2、构造器注入
public Students(String name, int id) { this.name = name; this.id = id; }
<constructor-arg type="java.lang.String"> <value>zhangsan</value> </constructor-arg> <constructor-arg type="int"> <value>18</value> </constructor-arg>
我们将配置文件中的property标签注释,使用构造器注入。构造器注入使用的是<constructor-arg>标签,最好使用在标签中加入type用于区分类型。
我们大部分使用的还是set注入的方式。 因为构造器注入有点麻烦
5、控制翻转
控制翻转,就是讲我们的对象交给spring来进行管理,之前我们需要使用一个对象,就是直接new出来,现在有了spring,对象都交由spring来帮助我们创建,我们需要的时候,直接找spring获取即可。
这么做的好处就是解耦合。而spring实现的方式是工厂模式。
这个代码就是典型的耦合,使我们自己new出来的对象。后期,如果实现类发生了改变,或者需要使用其他的实现类,我们都需要去修改代码。这就是一个典型的耦合案例!
public class PersonServiceImpl implements PersonService { private PersonDao personDao = new PersonDao(); @Override public void save() { personDao.save(); } }
有了spring后,我们将写一个配置文件,将bean对象交给spring来进行管理,需要的时候我们只需要找spring获取即可。后期,如果需要替换其他实现类,也只需要修改一下配置文件即可。
public class PersonDao { public void save() { System.out.println("保存"); } }
public class PersonServiceImpl implements PersonService { private PersonDao personDao; // private PersonDao personDao = new PersonDao(); @Override public void save() { personDao.save(); } public PersonDao getPersonDao() { return personDao; } public void setPersonDao(PersonDao personDao) { this.personDao = personDao; } }
<bean name="personDao" class="com.wx.dao.PersonDao"></bean> <bean name="personService" class="com.wx.service.impl.PersonServiceImpl"> <property name="personDao" ref="personDao"></property> </bean>
我们可以看到,现在已经一步一步的开始解耦了。
控制翻转其实就是:spring配置文件 + Spring工厂(ApplicationContext)
6、spring工厂创建复杂对象
6.1、什么是简单对象
我们直接用new出来就可以使用的对象,就是简单对象。反射的底层,也是使用的构造方法
6.2、复杂对象
不能通过直接new来创建的,比如Connection对象,这种对象创建过程复杂
6.3、创建复杂对象的三种方式
创建复杂对象有三种方式:
- 实现FactoryBean接口
- 实例工厂
- 静态实例工程
6.3.1、实现FactoryBean接口
实现FactoryBean接口,需要实现它的三个方法:
- getObject():用于书写复杂对象创建的代码,将创建的复杂对象返回出去
- getObjectType():返回复杂对象对应的.class文件
- isSingleton():如果这个复杂对象只用创建一次,返回true;如果这个复杂对象每次都需要创建一个新的对象,返回flase
public class PersonBean implements FactoryBean<Person> { @Override public Person getObject() throws Exception { Person person = new Person(); person.setName("zhaoliu"); person.setAge(20); return person; } @Override public Class<?> getObjectType() { return Person.class; } /** * 如果这个对象只需要创建一次,返回true * 如果每次调用都需要创建一次,返回false * @return */ @Override public boolean isSingleton() { return true; } }
<bean name="personBean" class="com.wx.study.PersonBean"> </bean>
我们可以发现一个问题,为什么我们getBean()是personBean对象,但是给我们真是返回的是Person对象呢?
这是因为我们personBean对象是一个复杂对象,spring首先会判断我们的对象是否继承了FactoryBean接口,没有继承,直接new一个对象给我们返回。如果继承了,会调用我们的getObject()方法,返回给我们一个复杂对象。这也是为啥我们最后返回的是person对象的原因。
我们的isSingleton()方法如何返回的是true,我们每次调用getBean()方法的时候,复杂对象只会创建一次,后面调用就直接返回接口。然后这里是flase,那么每次调用getBean()方法,都会给我们重新创建一个实例。
6.3.2、实例工厂
为啥我们有了FactoryBean接口了,还需要实例工厂呢?这是因为为了方便继承一些已经存在的代码,将他们也交由spring来进行管理
遗留代码:
/** * 遗留代码 */ public class OldFactory { public Person getOldPerson(){ System.out.println("我是遗留代码"); return new Person(); } }
<bean name="oldFactory" class="com.wx.study.OldFactory"></bean> <bean name="personOld" factory-bean="oldFactory" factory-method="getOldPerson"></bean>
需要现将OldFactory类交给spring来进行管理,然后通过factory-bean(调用哪个bean对象)和factory-method(调用哪个方法来创建对象)来进行操作
6.3.3、静态实例工厂
静态工厂就是调用的方法是静态的
/** * 静态工厂 */ public class StaticOldFactory { public static Person getOldPerson(){ System.out.println("我是静态遗留代码"); return new Person(); } }
因为是调用静态方法,所以我们的配置文件可以这样改写
<bean name="oldFactory" class="com.wx.study.OldFactory" factory-method="getOldPerson"></bean>
7、总结依赖注入和控制翻转
依赖注入有两种:
- set注入
- 八种基本数据类型
- String类型
- 集合类型
- 用户自定义类
- 构造器注入
控制翻转是将new的对象交由spring来进行创建管理,我们需要直接找spring获取
控制翻转分为以下两种:
- 简单对象
- 复杂对象
- FactoryBean
- 实例工厂
- 静态工厂