Spring IoC
Web Explain:
什么是Spring IoC?控制翻转,依赖注入
为什么用到Spring IoC
之前在写代码的时候,如果要在一个类里面,获取另一个类里面的属性方法,首先需要在本类里面调用另一个类的构造方法,通过构造方法获取其对象在调用其类里面的公共方法并通过get、set获取并修改其属性值;
或者,通过类的继承、实现接口来取得类与类之间的联系
但在Spring中,我们摒弃上面的方法,通过SpringIoC来解耦类与类之间的关系,不用再new,这个容器就叫applicationContext.xml,它就是Spring容器,也是Spring框架
那么创建这个容器的方式都有哪些?
SpringIoc的创建方式常用的为xml配置(需要额外注意构造中参数列表的参数顺序)和注解方式(推荐,在SpringBoot中也常用)
现在我们在idea上试着写一个简单的SpringIoC,来调用另一个类的属性和方法
第一步,在pom.xml中添加Spring的依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ming</groupId> <artifactId>ArvinSpringProjects</artifactId> <version>1.0-SNAPSHOT</version> <!-- spring core spring beans spring context spring aop --> <dependencies> <!--spring core jar--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans --> <!-- relevant form between class and others --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <!-- Spring Context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <!--Aspect Oriented Programming--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>RELEASE</version> <scope>test</scope> </dependency> <!-- Junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build> </project>
第二步,再加载核心配置文件,Application.context,它就是Spring容器,现在把new的工作交给容器去做,been通过id来表示,class为你想要实例化的类,如果id的名字为“person”,这句话就相当于
Person person = new Person(),即容器帮助你new了一个person,class为该类包下的路径,<property name="pName" value="arvin"/>只要是property就是setter的注入,value代表修改的值,name为构造传参,pName为普通类属性
Application来自Spring包
<?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" xmlns:aop="http://www.springframework.org/schema/aop" 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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- Person person = new Person(); --> <bean id="person" class="com.hhy.bean.Person"> <!-- <property name="pName" value="arvin"/>--> <constructor-arg name="age" value="20"/> <constructor-arg name="name" type="java.lang.String" value="michael jackson"/> </bean> </beans>
写一个普通的Person类
package com.hhy.bean; /** * Person p = new Person * p.setpName("arvin"); * * * p.getpName(); */ public class Person { //DI private String pName; private int pAge; public Person(){ } public Person(String name, int age){ this.pName = name; this.pAge = age; } public String getpName() { return pName; } public void setpName(String pName) { this.pName = pName; } public int getpAge() { return pAge; } public void setpAge(int pAge) { this.pAge = pAge; } /** * sayHello is defined by arvin * @return */ public void sayHello(){ System.out.println("hello nice to meeting you"); } @Override public String toString() { return "Person{" + "pName='" + pName + '\'' + '}'; } }
通过容器实例化它之后,第三步获取bean对象,context.getbean就获取到了bean类对象,由于Spring容器在接受时也不知道它是什么类型,所以需要强转为bean类型,第四步,测试application
package com.hhy; import com.hhy.bean.Person; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringTest { private Person person = null; @Before public void testInitial(){ //acquire object from spring container ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); person = (Person) context.getBean("person"); } @Test public void testMethod(){ //Person person = new Person(); person.sayHello(); System.out.println("pName = " + person.getpName() + ", pAge = " + person.getpAge()); } }
测试打印结果:
hello nice to meeting you pName = michael jackson, pAge = 20
每个需要容器调用的类中必须要有set方法。通过set控制属性,普通类通过set注入bean的对象的value值
刚才说过IoC伴随DI(依赖注入),有Setter、构造、接口注入的方法,注入的是什么?属性,也就是成员变量,并且普通类都要带有无参构造
关于注入的类型不止有String类型,还有map、set、list、数组、int、String数组
再创建一个bean类,来一个个测试我们的注入类型,既然用setter注入,就一定要选择我们的默认构造方法和set方法再加一个tostring方便打印
package com.hhy.bean2; import java.util.*; /** * test injection from spring framework * setter(recommend) */ public class Bean1 { private String strValue; private int intValue; private Map mapValue; private Set setValue; private List listValue; private String [] arrayValue; //Date java.util.Date; private Date dateValue; public Bean1(){ } //DI public Date getDateValue() { return dateValue; } public void setDateValue(Date dateValue) { this.dateValue = dateValue; } public String getStrValue() { return strValue; } public void setStrValue(String strValue) { this.strValue = strValue; } public int getIntValue() { return intValue; } public void setIntValue(int intValue) { this.intValue = intValue; } public Map getMapValue() { return mapValue; } public void setMapValue(Map mapValue) { this.mapValue = mapValue; } public Set getSetValue() { return setValue; } public void setSetValue(Set setValue) { this.setValue = setValue; } public List getListValue() { return listValue; } public void setListValue(List listValue) { this.listValue = listValue; } public String[] getArrayValue() { return arrayValue; } public void setArrayValue(String[] arrayValue) { this.arrayValue = arrayValue; } @Override public String toString() { return "Bean1{" + "strValue='" + strValue + '\'' + ", intValue=" + intValue + ", mapValue=" + mapValue + ", setValue=" + setValue + ", listValue=" + listValue + ", arrayValue=" + Arrays.toString(arrayValue) + '}'; } }
我们再来建一个applicationcontext用来测试属性注入
<?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" xmlns:aop="http://www.springframework.org/schema/aop" 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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--test properties injection--> <bean id="bean1" class="com.hhy.bean2.Bean1"> <!--string--> <property name="strValue" value="xiaoming"/> <!--int--> <property name="intValue"> <value>100</value> </property> <!--map--> <property name="mapValue"> <map> <entry key="key1" value="value1"/> <entry key="key2" value="value2"/> <entry key="key3" value="value3"/> </map> </property> <!--list--> <property name="listValue"> <list> <value>list1</value> <value>list2</value> <value>list3</value> </list> </property> <!--set--> <property name="setValue"> <set> <value>setValue1</value> <value>setValue2</value> <value>setValue3</value> </set> </property> <property name="arrayValue"> <list> <value>arrayValue1</value> <value>arrayValue2</value> <value>arrayValue3</value> </list> </property> <property name="dateValue" value="2019-07-23"/> </bean> <!-- DateEditProperties dep = new DateEditProperties(); --> <bean id="edit" class="org.springframework.beans.factory.config.CustomEditorConfigurer"> <property name="customEditors"> <map> <entry key="java.util.Date" value="com.hhy.bean2.DateEditProperties"/> </map> </property> </bean> <bean id="userinfo" class="com.hhy.bean.UserInfo"> <property name="username" value="lisi"/> <!--references--> <property name="person" ref="myPersonId"/> </bean> <bean id="myPersonId" class="com.hhy.bean.Person"> <property name="pName" value="zhansan"/> <property name="pAge"> <value>10</value> </property> </bean> </beans>
只要是property就是setter注入,value代表你set的值
需要注意的是:在容器中,key和value不管是什么数据类型都是字符串,只不过到具体类中对应的才是本来的类型
配置完容器后,再去测试类里面写测试
package com.hhy; import com.hhy.bean.Person; import com.hhy.bean2.Bean1; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringTest2 { private Bean1 bean1 = null; @Before public void testInitial(){ ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext-beans.xml"}); bean1 = (Bean1) context.getBean("bean1"); } @Test public void testMethod(){ System.out.println("bean1 strValue = " + bean1.getStrValue()); System.out.println("bean1 listValue = " + bean1.getListValue()); System.out.println("bean1 setValue = " + bean1.getSetValue()); System.out.println("bean1 intValue = " + bean1.getIntValue()); System.out.println("bean1 mapValue = " + bean1.getMapValue()); System.out.println("bean1 arrayValue = " + bean1.getArrayValue()); //date System.out.println("bean1 dateValue = " + bean1.getDateValue()); } }
打印结果:
text = 2019-07-23 bean1 strValue = xiaoming bean1 listValue = [list1, list2, list3] bean1 setValue = [setValue1, setValue2, setValue3] bean1 intValue = 100 bean1 mapValue = {key1=value1, key2=value2, key3=value3} bean1 arrayValue = [Ljava.lang.String;@43a0cee9 bean1 dateValue = Tue Jul 23 00:00:00 CST 2019
值得一提的是,上面的日期类,需要重新写一个类定义一下格式,因为日期分为很多时区,而且格式也不同,这里用到
SimpleDateFormat sdf = new SimpleDateFormat("yyyy--MM-dd");
package com.hhy.bean2; import org.springframework.beans.propertyeditors.PropertiesEditor; import java.beans.PropertyEditorSupport; import java.text.SimpleDateFormat; import java.util.Date; public class DateEditProperties extends PropertyEditorSupport { private String formatting = "yyyy-MM-dd"; @Override public void setAsText(String text) throws IllegalArgumentException { System.out.println("text = " + text); SimpleDateFormat sdf = new SimpleDateFormat(formatting); try{ Date date = sdf.parse(text); this.setValue(date); }catch(Exception ex){ ex.printStackTrace(); } } }