目錄
1、什么是IOC
2、java實現創建對象的方式有哪些
3、基于配置文件的di實現
3.1、什么是di
3.2、入門案例
3.3、環境搭建
接口和實現類
ioc配置文件
測試程序
3.4、案例總結
3.5、簡單類型屬性的賦值(set注入)
set注入要求
JavaBean
spring配置文件
3.6、非簡單類型屬性的賦值(set注入)
3.7、構造注入
byName形式
byType形式
3.8、基于注解的di實現
3.8.1、簡單類型的注解di實現
3.8.2、引用類型的注解di實現
學生類
School類
1、什么是IOC
IoC (Inversion of Control) : 控制反轉, 是一個理論,概念,思想。把對象的創建,賦值,管理工作都交給代碼之外的容器實現, 也就是對象的創建是有其它外部資源完成,這樣做實現了與解耦合。
正轉:對象的創建、賦值等操作交由程序員手動完成,即使用類似new Xxx(Xxx Xxx)、Xxx.setXxx()語句完成對象的創建與賦值,缺點是一旦程序功能發生改變,涉及到的類就要修改代理,耦合度高,不便于維護和管理。
反轉:對象的創建、賦值等操作交由代碼之外的容器實現,有容器代替程序員完成對象的創建、賦值;且當程序功能發生變化時,只需要修改容器的配置文件即可。
2、java實現創建對象的方式有哪些
1、構造方法:new student()
2、反射
3、序列化
4、動態代理
5、容器:tomcat容器、ioc容器
?其實在以前我們已經接觸過了容器創建對象的場景,還記得tomcat服務器嗎,在tomcat啟動時會實例化servletContext上下文對象;在發出請求時,相應的servlet對象也不是由開發人員進行實例化的,而是在tomcat內部由tomcat容器實例化的,回憶一下在學習javaweb的時候,我們有寫過類似new XxxServlet()這樣的代碼嗎,現在想必大家對容器有一個大概的概念了吧。????????
3、基于配置文件的di實現
3.1、什么是di
DI(Dependency Injection) :依賴注入, 只需要在程序中提供要使用的對象名稱就可以, 至于對象如何在容器中創建, 賦值,查找都由容器內部實現。
DI是ioc技術的實現方式(即容器如何創建對象這一問題的實現方式)
3.2、入門案例
使用ioc容器創建對象,調用對象的方法
3.3、環境搭建
創建maven項目,目前都是javase項目,推薦使用骨架,選擇quickstart
加入maven依賴:分別是spring依賴、junit依賴
創建類(接口和它的實現類)
創建spring需要使用的配置文件
測試
接口和實現類
//接口
public interface SomeService {void doSome();
}
//實現類
public class SomeServiceImpl implements SomeService {//無參構造public SomeServiceImpl() {System.out.println("SomeServiceImpl類的無參構造執行了...");}@Override public void doSome() {System.out.println("執行了SomeServiceImpl的doSome()方法");}
}
ioc配置文件
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--聲明bean(告訴spring要創建某個類的對象)1、id:自定義名稱,唯一值,spring通過該id的屬性值找到對象2、class:要創建類的全限定類名3、下述的聲明語句在spring底層類似與執行了以下代碼:SomeService service = new SomeServiceImpl();4、對象的保存:spring將對象保存到內部的map中,map.put(id值,對象)map.put("someService",new SomeServiceImpl())5、一個bean標簽聲明一個java對象6、spring容器根據bean標簽創建對象,盡管存在class屬性相同的bean標簽,只要是id值不同,spring容器就會創建該class的對象--><bean id="someService" class="com.mms.service.impl.SomeServiceImpl"/><bean id="someService2" class="com.mms.service.impl.SomeServiceImpl"/><!--spring容器也可以創建非自定義類的對象,例如java.lang.String類的對象,只要指定了class屬性,spring容器就可以創建該類的對象--><bean id="myString" class="java.lang.String"/>
</beans>
測試程序
//使用spring容器創建對象@Testpublic void test02() {//1、指定spring配置文件的名稱String config = "beans.xml";//2、創建表示spring容器的對象 ApplicationContext//ClassPathXmlApplicationContext:表示從類路徑中加載spring配置文件ApplicationContext ac = new ClassPathXmlApplicationContext(config);//3、從容器中獲取對象SomeService service = (SomeService)ac.getBean("someService");//4、調用方法service.doSome();}/*** 測試spring容器創建對象的時機* 在創建spring容器時,會創建配置文件中的所有對象*/@Testpublic void test03() {//1、指定spring配置文件路徑String config = "beans.xml";//2、創建spring容器ApplicationContext ac = new ClassPathXmlApplicationContext(config);/*** 測試輸出結果:* SomeServiceImpl類的無參構造執行了...* SomeServiceImpl類的無參構造執行了...* 驗證了spring調用類的無參構造完成對象的創建*/}//獲取spring容器中java對象的信息@Testpublic void test04() {String config = "beans.xml";ApplicationContext ac = new ClassPathXmlApplicationContext(config);//獲取spring容器中對象的個數int beansCount = ac.getBeanDefinitionCount();System.out.println("spring容器中的對象個數="+beansCount);//獲取spring容器中對象的名稱(即bean標簽的id值)String[] beansNames = ac.getBeanDefinitionNames();for (String beanName : beansNames) {System.out.println(beanName);}}
3.4、案例總結
spring配置文件中一個bean標簽就代表一個對象,該對象有bean標簽的id值唯一標識,從spring拿對象是使用getBean(“bean標簽的id值”)
spring默認是使用類的無參構造來創建對象的
3.5、簡單類型屬性的賦值(set注入)
在入門案例的總結我們說過了spring容器默認是使用無參構造構造來實例化對象的,那么對象的屬性必定為初始值,例如int類型為0,boolean類型為false等,那么當我們想使用相關屬性進行操作時必然要手動使用set方法給屬性賦值,那么有沒有辦法讓容器幫我們完成對象屬性的賦值呢?讓我們直接就能夠從容器中拿到有屬性值的對象?答案是肯定的,下面就通過代碼演示簡單類型的屬性賦值。
set注入要求
JavaBean必須要有set方法,因為ioc容器是使用javabean的set方法進行屬性賦值的
spring容器調用的是setXxx()方法,而不管對象是否具有Xxx屬性(即對象沒有的屬性只要有set方法也可以實現注入),Xxx不區分大小寫
看看代碼:
JavaBean
public class Student {private String name;private int age;private School school;public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}public void setSchool(School school) {this.school = school;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", school=" + school +'}';}
}
spring配置文件
<!--聲明Student對象--><bean id="student" class="com.mms.component.Student"><!--1、簡單類型使用property和value標簽給對象屬性賦值2、簡單類型:8個基本類型+String3、當spring容器加載到這一行時會在創建完對象的同時使用對象的set方法給屬性賦值,底層調用的是對象的set方法4、spring容器調用的是setXxx()方法,而不管對象是否具有Xxx屬性,Xxx不區分大小寫--><property name="name" value="張三"/><property name="age" value="23"/><!--測試對象沒有屬性的set方法--><property name="graName" value="s1"/></bean>
測試類
//使用set注入給對象屬性賦值@Testpublic void test01() {String config = "ba01/applicationContext.xml";ApplicationContext ac = new ClassPathXmlApplicationContext(config);//執行完14行此時Student對象的屬性已被賦值,獲取對象進行驗證Student stu = (Student) ac.getBean("student");System.out.println(stu); //Student{name='張三', age=23}}//驗證set注入調用的是對象的set方法@Testpublic void test02() {String config = "ba01/applicationContext.xml";/** 此時會調用set方法進行賦值* setName...* setAge...*/ApplicationContext ac = new ClassPathXmlApplicationContext(config);}//驗證沒有屬性的setXxx方法是否報錯@Testpublic void test03() {String config = "ba01/applicationContext.xml";ApplicationContext ac = new ClassPathXmlApplicationContext(config);//獲取對象Student stu = (Student) ac.getBean("student");}
3.6、非簡單類型屬性的賦值(set注入)
上文中的set注入使用property標簽的name和value屬性給對象屬性賦值,但是value知識給簡單類型屬性賦值,對于非簡單類型我們是使用property標簽的name和ref屬性給對象屬性賦值。我們現在給Student類增加一個屬性address,該屬性是一個引用類型,那當ioc容器創建Student對象時如何給address屬性賦值呢?
Student類:別的地方與上文的student類一致,這里只給出address屬性和其set方法
//引用類型屬性private Address address;public void setAddress(Address address) {System.out.println("引用類型address的set方法執行了...");this.address = address;}
Address類
public class Address {private String homeAddress;private String schoolAddress;public void setHomeAddress(String homeAddress) {this.homeAddress = homeAddress;}public void setSchoolAddress(String schoolAddress) {this.schoolAddress = schoolAddress;}@Overridepublic String toString() {return "Address{" +"homeAddress='" + homeAddress + '\'' +", schoolAddress='" + schoolAddress + '\'' +'}';}
}
applicationContext.xml配置文件
<!--聲明Student對象--><bean id="student" class="com.mms.component.Student"><property name="name" value="張三"/><property name="age" value="23"/><!--測試對象沒有屬性的set方法--><property name="graName" value="s1"/><!--引用類型屬性的set注入property標簽屬性name:屬性名ref:引用對象的id值--><property name="address" ref="address"/></bean><!--Student對象的引用屬性Address--><bean id="address" class="com.mms.component.Address"><!--set注入--><property name="homeAddress" value="新疆"/><property name="schoolAddress" value="西安"/></bean>
上文執行流程分析:當ioc容器創建id為student的對象時,會進行set注入,當執行到最后一個propert標簽時發現使用了ref屬性,則ioc容器知道了name為address的屬性是非簡單類型,它就會暫時跳過address屬性的賦值以及Student對象的創建,轉而去配置文件的下文去找bean標簽id值等于ref屬性值的對象,現將該對象創建,再將該對象賦值給之前的address屬性并將Student對象創建。
3.7、構造注入
顧名思義,構造注入是使用javabean的構造方法進行屬性的賦值的。與set注入一樣,構造注入要求javabean必須提供構造方法,且必須是有參構造(如果是無參構造還怎么給屬性賦值,對吧),構造注入使用較少,了解就可以了,我們一般使用set注入。看看代碼吧,將Student類的set方法注釋,加入構造方法,別的地方不用改變,只需要改變spring配置文件即可(這里就可以看出ioc容器與程序的解耦合的好處了)。
?
<!--構造注入1、使用constructor-arg標簽完成構造注入2、構造注入方式一:根據形參名字3、構造注入方式二:根據形參順序,默認下標從0開始遞增--><!--根據形參名構造注入,形參的出現順序不是必須的--><bean id="student" class="com.mms.value.Student"><constructor-arg name="name" value="李四"/><constructor-arg name="age" value="24"/><constructor-arg name="address" ref="address"/></bean><bean id="address" class="com.mms.value.Address"><constructor-arg name="homeAddress" value="新疆"/><constructor-arg name="schoolAddress" value="西安"/></bean><!--構造注入,使用下標,出現的順序沒要求,因為已經通過下標綁定起來了--><bean id="diByContructor" class="com.mms.value.Student"><constructor-arg index="0" value="趙六"/><constructor-arg index="1" value="26"/><constructor-arg index="2" ref="address"/></bean>
3.8 非簡單類型的自動注入
對于非簡單類型,我們在上面是使用ref屬性指向一個非簡單類型的對象來完成賦值的,那么當ioc容器每次給一個對象的非簡單類型屬性賦值時,就要在bean標簽內部寫一行ref這樣的代碼,這樣會造成重復代碼的大量堆積,可以使用引用類型的自動注入。
有兩種方式的引用類型自動注入
byName形式的引用類型自動注入:
通過java對象引用類型的屬性名與spring容器中bean標簽對象的id值一樣且數據類型是一致的,這樣能夠實現引用類型的自動注入
byType形式的引用類型自動注入
通過java對象引用類型屬性的數據類型和spring容器中 bean標簽的class屬性值是同源關系;
常見的同源關系:
1)java引用類型屬性數據類型和bean標簽的class屬性值數據類型一樣
2)java引用類型屬性數據類型和bean標簽的class屬性值數據類型是父子關系
3)java引用類型屬性數據類型和bean標簽的class屬性值數據類型是接口和實現類關系
注意:在一個配置文件中,符合條件的同源關系只能有一個
下面通過配置文件來詳細說明兩種形式的實現,在這里還是以Student類的address屬性為例來說明。
byName形式
<bean id="student" class="com.mms.ba03.Student" autowire="byName"><!--簡單類型賦值--><property name="name" value="張三"/><property name="age" value="23"/></bean><!--引用類型--><bean id="school" class="com.mms.ba03.School"><property name="schoolName" value="石河子大學"/><property name="schoolAddress" value="石河子市"/></bean>
匹配詳解:?當ioc容器在創建Student對象時,發現使用了autowire屬性且屬性值為byName,ioc容器就會去Student類中去拿引用類型的屬性名與和spring配置文件中的bean標簽的id值進行比對,若發現有一致的且數據類型一致,則將該對象賦值給引用類型屬性。
byType形式
<!--使用byType實現引用類型自動注入--><bean id="student2" class="com.mms.ba03.Student" autowire="byType"><!--簡單類型賦值--><property name="name" value="李四"/><property name="age" value="24"/></bean><!--引用類型<bean id="school2" class="com.mms.ba03.School"><property name="schoolName" value="西南大學"/><property name="schoolAddress" value="重慶市"/></bean>--><!--聲明School的子類--><bean id="primarySchool" class="com.mms.ba03.PrimarySchool"><property name="schoolName" value="西北大學"/><property name="schoolAddress" value="西安"/></bean>
3.8、基于注解的di實現
除了使用配置文件實現ioc創建對象的功能外,使用spring提供的注解也可以實現di。下面來介紹注解方式的di實現,下面是spring提供的di實現的常用注解。
@Component:該注解的功能是使用spring容器創建對象
1)、在要創建對象的類的聲明上方加入該注解,該注解有一個屬性value,value為spring創建的該類對象的id值
2)、開發中使用將value省略,直接使用雙引號將值鍵入即可
3)、該注解使用類的無參構造創建對象
@Repository 創建dao類對象,訪問數據庫的對象
@Service 創建service類對象,業務層對象
@Controller 創建控制器對象,用于分發用戶的請求和顯示處理結果
?下面通過代碼來看看@Component注解是怎么實現di的。
@Component(value = "student")
public class Student {...
}
該語句就等價為在spring配置文件中進行了以下聲明
<bean id = "student" class = "com.mms.component.Student"/>
但是怎么讓配置文件知道哪些類是使用注解進行創建對象的呢?需要在配置文件中聲明組件掃描器
<context:component-scan base-package="com.mms.component"/>
當spring讀取配置文件時,讀取到組件掃描器聲明語句時,就會去base-package指定的包和其子包下去遞歸的尋找有注解修飾的類,并根據注解的功能去執行相應的動作
3.8.1、簡單類型的注解di實現
簡單類型的注入使用@Value注解實現,哪些簡單類型要設置屬性值,直接在簡單類型屬性聲明語句的上面加入注解@Value即可,并在@Value的括號內鍵入屬性值,注意不論簡單類型屬性的數據類型,均由雙引號將屬性值括起來。例如之前的Student類使用注解注入如下。
@Component("student")
public class Student {@Value("張三")private String name;@Value("23")private int age;
}
注意別忘了該類要加注解@Component注解,因為要創建該類對象。
3.8.2、引用類型的注解di實現
引用類型的注入使用@Autowired注解完成。
@Autowired
@Autowired是spring提供的屬性賦值,用于給引用類型賦值,有byName和byType兩種方式,默認使用byType方式自動注入
若是要強制至于byName方式,要在@Autowired注解下面加入 @Qualifier(value = “bean的id”)注解,若程序在給引用類型注入時在xml文件中找不到 該id的bean標簽或者手找不到該id的@Component注解,則報錯;若不想讓程序在賦值失敗時報錯,可以在@Autowired注解的required屬性值置為false
還是拿Student類的school屬性的賦值來舉例。
學生類
@Component("student")
public class Student {/*引用類型注入(byType方式)@Autowiredprivate School school;*///引用類型賦值(byName方式)@Autowired(required = false)@Qualifier(value = "mySchool")private School school;
}
School類
@Component("mySchool")
public class School {//注入值@Value("西南大學")private String schoolAddress;@Value("新疆")private String homeAddress;@Overridepublic String toString() {return "School{" +"schoolAddress='" + schoolAddress + '\'' +", homeAddress='" + homeAddress + '\'' +'}';}
}