Spring框架是一個開源的Java平臺堰氓,用于構(gòu)建企業(yè)級應(yīng)用程序和Java EE應(yīng)用程序适荣。它提供了許多功能和模塊现柠,使開發(fā)者能夠更輕松地構(gòu)建和管理復(fù)雜的應(yīng)用程序。整個Spring框架的學(xué)習(xí)分成四個模塊:IOC基礎(chǔ)容器弛矛、AOP面向切面過程晒旅、Spring整合web環(huán)境、SpringMVC web層解決方案汪诉。
Spring Framework的技術(shù)棧废恋,共分為五塊,Testjunit測試類扒寄、Core容器鱼鼓、AOP層、數(shù)據(jù)接入该编、Web接入層迄本。
先解釋一下Spring的基礎(chǔ)術(shù)語:
1、IoC(Inversion of Control课竣,控制反轉(zhuǎn)):
IoC是Spring框架的兩大核心之一嘉赎。它通過將對象的創(chuàng)建和依賴注入的控制權(quán)交給Spring容器來實現(xiàn)。開發(fā)者只需要定義依賴關(guān)系和配置于樟,而不需要顯式地管理對象的創(chuàng)建和銷毀公条。那么,通俗的講迂曲,什么是控制反轉(zhuǎn)吶靶橱?就是中介。
假如你擁有一家公司,你需要雇傭一名保潔人員給你打掃衛(wèi)生关霸,那么你需要去管理這個保潔人員传黄,負責(zé)給他開工資、處理他的五險一金队寇、處理他的節(jié)假日福利等等膘掰,各種雜事,但如果你把它交給一個物業(yè)公司佳遣,那么识埋,你只需要向物業(yè)公司提出你的需求,其余的事情都交給他去處理就可以了苍日,IoC就是這個作用。之所以叫控制反轉(zhuǎn)窗声,是因為正常是你去雇傭保潔相恃,主動去做這個事。但是現(xiàn)在是讓物業(yè)公司去做笨觅,再把保潔安置到你的公司拦耐,對于保潔的控制權(quán)從你的手里,轉(zhuǎn)移到物業(yè)公司见剩,所以叫控制反轉(zhuǎn)杀糯。那么保潔人員安置到你公司的這個動作,就叫做依賴注入苍苞,所以有IoC的地方固翰,就會提到依賴注入。
2羹呵、DI(Dependency Injection骂际,依賴注入):
DI是IoC的一種具體實現(xiàn)方式,它是Spring框架的另一個核心概念冈欢。通過依賴注入歉铝,Spring容器可以在對象創(chuàng)建時自動將其依賴的其他對象注入到該對象中,降低了對象之間的耦合性凑耻,使得代碼更加靈活和可維護太示。
盜一張別人的圖,覺得這個齒輪圖香浩,很好的展現(xiàn)了IOC解耦的一個作用类缤。
3、BeanFactory:
bean工廠邻吭,去生成呀非、管理Bean對象的,是IoC的核心接口,就是通過這個接口岸裙,去實現(xiàn)反轉(zhuǎn)猖败、注入的。以下是BeanFactory的開發(fā)步驟:
<!--pom文件導(dǎo)入spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.7</version>
</dependency>
/**
* 新建一個service降允、一個實現(xiàn)類
**/
public interface UserService {}
public class UserServiceImpl implements UserService {}
<!--創(chuàng)建beans.xml配置文件恩闻,放到resources目錄下-->
<bean id="userService" class="com.dj.service.impl.UserServiceImpl"></bean>
import com.dj.dao.UserDao;
import com.dj.service.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
/**
* 創(chuàng)建測試類,獲取beanFactory創(chuàng)建的bean對象
**/
public class BeanFactoryTest {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions("beans.xml");
UserService userService = (UserService) beanFactory.getBean("userService");
System.out.println(userService);
}
}
4剧董、ApplicationContext:
ApplicationContext由BeanFactory派生而來幢尚,除了生成bean對象,也包含事件發(fā)布翅楼、國際化消息資源包等尉剩,父類共6個接口。在IDEA中(使用windows的快捷鍵)毅臊,選中要查看的對象理茎,右鍵show diagram,可以查看當(dāng)前對象有哪些父類接口管嬉。
在IDEA中(使用windows的快捷鍵)皂林,選中要查看的對象,使用Ctrl+H的快捷鍵蚯撩,可以查看類的繼承關(guān)系础倍。
import com.dj.dao.UserDao;
import com.dj.service.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
/**
* 創(chuàng)建測試類,獲取beanFactory創(chuàng)建的bean對象
**/
import com.dj.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService);
}
}
BeanFactory和ApplicationContext都能生成bean對象胎挎,但是生成的時機是不一樣的沟启。BeanFactory是在獲取bean對象時才能獲取,而ApplicaitonContext是在類加載的時候就已經(jīng)生成了犹菇。
5美浦、關(guān)于bean的常用屬性
id、class项栏、name浦辨、scope、lazy-init沼沈、init-method流酬、autowire、factory-bean列另、factory-method
1)關(guān)于id芽腾,通過配置bean的id名稱,修改beanName页衙,重要
<bean id="userService" class="com.dj.service.impl.UserServiceImpl"></bean>
//如果不配置id摊滔,beanName為com.dj.service.impl.UserServiceImpl
UserService userService = (UserService) context.getBean("com.dj.service.impl.UserServiceImpl");
2)關(guān)于name阴绢,配置別名,可以配置多個別名aa艰躺、bb呻袭、cc,幾乎不用腺兴,不重要
<bean id="userService" name="aa,bb,cc" class="com.dj.service.impl.UserServiceImpl"></bean>
UserService aa= (UserService) context.getBean("aa");
UserService bb= (UserService) context.getBean("bb");
debug模式左电,查看context,context->beanFactory->aliasMap
3)關(guān)于scope页响,如果是單純的spring環(huán)境篓足,屬性有singleton、prototype闰蚕,默認是singleton栈拖,即使實例化多個對象,對象地址都是同一個没陡,一般只用默認的涩哟。
當(dāng)scope是prototype,每次實例化诗鸭,每次都是新的對象染簇。
如果添加spring-webmvc参滴、spring-web環(huán)境時强岸,scope屬性還有request、session砾赔。
不重要蝌箍。
<bean id="userService" class="com.dj.service.impl.UserServiceImpl" scope="prototype"></bean>
UserService userService1= (UserService) context.getBean("userService");
UserService userService2= (UserService) context.getBean("userService");
4)關(guān)于lazy-init,先不創(chuàng)建暴心,誰用誰創(chuàng)建妓盲,對于beanFactory無效,ApplicationContext有效
<bean id="userService" class="com.dj.service.impl.UserServiceImpl" lazy-init="true"></bean>
5)關(guān)于init-method专普、destroy-method(用不到)悯衬,初始化方法是指定對象構(gòu)造完畢后,默認執(zhí)行的檀夹、銷毀方法是指容器關(guān)閉后筋粗,需要顯示的調(diào)用容器關(guān)閉方法,才會默認執(zhí)行該方法炸渡。
<!--在service實現(xiàn)類中添加方法名為init娜亿、destroy的方法,此處省略-->
<bean id="userService" class="com.dj.service.impl.UserServiceImpl" init-method="init" destroy-method="destroy"></bean>
此處蚌堵,還可以提到InitializingBean接口买决,利用它的afterPropertiesSet方法執(zhí)行賦值
package com.dj.service.impl;
import com.dj.service.UserService;
import org.springframework.beans.factory.InitializingBean;
public class UserServiceImpl implements UserService, InitializingBean {
public UserServiceImpl(){
System.out.println("構(gòu)造方法:UserServiceImpl ...");
}
public void init(){
System.out.println("對象初始化后執(zhí)行的方法:init ...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("對象初始化完成后的屬性操作:afterPropertiesSet ...");
}
}
import com.dj.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
System.out.println(userService);
}
}
/** 控制臺打印的結(jié)果如下沛婴,由此可看出各方法執(zhí)行的順序:
* 構(gòu)造方法:UserServiceImpl ...
* 對象初始化完成后的屬性操作:afterPropertiesSet ...
* 對象初始化后執(zhí)行的方法:init ...
* com.dj.service.impl.UserServiceImpl@478190fc
**/
6)關(guān)于spring實例化bean的兩種方式:構(gòu)造方法、工廠方法督赤。spring的實例化底層都是通過BeanFactory來實現(xiàn)的嘁灯,所以工廠方法就是兩層工廠方法的嵌套,默認就是無參構(gòu)造器够挂。
package com.dj.dao;
public interface UserDao {
}
package com.dj.dao.impl;
import com.dj.dao.UserDao;
public class UserDaoImpl implements UserDao {
}
package com.dj.service.impl;
import com.dj.dao.UserDao;
import com.dj.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl(){
System.out.println("無參構(gòu)造方法:UserServiceImpl ...");
}
public UserServiceImpl(String name){
System.out.println("有參構(gòu)造方法:UserServiceImpl ...");
}
}
<!--配置constructor-arg參數(shù)時旁仿,就調(diào)用有參構(gòu)造方法,不配置孽糖,就調(diào)用無參構(gòu)造方法-->
<bean id="userService" class="com.dj.service.impl.UserServiceImpl">
<constructor-arg name="name" value="dj"></constructor-arg>
<property name="userDao" ref="userDao"/>
</bean>
工廠方法又分為三種:靜態(tài)工廠方法枯冈、實例工廠方法、實現(xiàn)FactoryBean規(guī)范延遲實例化bean办悟。
靜態(tài)工廠方法尘奏,直接用類名調(diào)用靜態(tài)方法即可:
/**
* 新建工廠類
*/
package com.dj.service.impl;
public class UserDaoFactory1 {
public static UserDaoImpl getUserDaoBean() {
//通過此方法實例化對象,方便進行其它的邏輯處理
return new UserDaoImpl();
}
}
<bean id="userDao1" class="com.dj.service.impl.UserDaoFactory1" factory-method="getUserDaoBean"></bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Object object = context.getBean("userDao1");
System.out.println(object);
}
}
實例工廠方法病蛉,先獲取工廠bean炫加,再調(diào)用實例化方法:
package com.dj.service.impl;
import com.dj.dao.impl.UserDaoImpl;
/**
* 新建工廠類
*/
public class UserDaoFactory2 {
public UserDaoImpl getUserDaoBean() {
return new UserDaoImpl();
}
}
<bean id="userDaoFactory2" class="com.dj.service.impl.UserDaoFactory2"></bean>
<bean id="userDao2" factory-bean="userDaoFactory2" factory-method="getUserDaoBean"></bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Object object = context.getBean("userDao2");
System.out.println(object);
}
}
實現(xiàn)FactoryBean規(guī)范延遲實例化bean,通過實現(xiàn)FactoryBean接口來實現(xiàn)铺然,:
package com.dj.factory;
import com.dj.dao.UserDao;
import com.dj.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
/**
* 新建工廠類
*/
public class UserDaoFactory3 implements FactoryBean<UserDao> {
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
}
<bean id="userDao3" class="com.dj.factory.UserDaoFactory3"></bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Object object = context.getBean("userDao3");
System.out.println(object);
}
}
7)bean的依賴注入配置:通過set方法俗孝,構(gòu)造器的方法。其中魄健,ref是reference赋铝,參考、引用的意思沽瘦。
- set方法:
<property name="name" value="dj">
<property name="userDao" ref="userDao">
- 構(gòu)造器方法
<constructor-arg name="name" value="dj"></constructor-arg>
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
依賴注入的類型共三類:普通型革骨、引用型、集合數(shù)據(jù)類型析恋。此處僅演示集合類型良哲,List、Set助隧、Properties筑凫。
package com.dj.service.impl;
import com.dj.dao.UserDao;
import com.dj.service.UserService;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class UserServiceImpl implements UserService {
private List<String> stringList;
private List<UserDao> userDaoList;
private Set<String> stringSet;
private Map<String,String> stringMap;
private Properties properties;
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
public void setUserDaoList(List<UserDao> userDaoList) {
this.userDaoList = userDaoList;
}
public void setStringSet(Set<String> stringSet) {
this.stringSet = stringSet;
}
public void setStringMap(Map<String,String> stringMap) {
this.stringMap = stringMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void show(){
System.out.println(stringList);
System.out.println(userDaoList);
System.out.println(stringSet);
System.out.println(stringMap);
System.out.println(properties);
}
}
<bean id="userService" class="com.dj.service.impl.UserServiceImpl">
<property name="stringList">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<property name="userDaoList">
<list>
<ref bean="userDao"></ref>
<ref bean="userDao1"></ref>
<ref bean="userDao2"></ref>
</list>
</property>
<property name="stringSet">
<set>
<value>111</value>
<value>222</value>
<value>333</value>
</set>
</property>
<property name="stringMap">
<map>
<entry key="a" value="1"></entry>
<entry key="b" value="2"></entry>
<entry key="c" value="3"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="1">a</prop>
<prop key="2">b</prop>
<prop key="3">c</prop>
</props>
</property>
</bean>
import com.dj.service.UserService;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
public class BeanFactoryTest {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions("beans.xml");
UserService userService = (UserService) beanFactory.getBean("userService");
userService.show();
}
}
關(guān)于自動裝配,用byName居多并村,一般不會重復(fù)巍实。name是根據(jù)set方法的名字來匹配的,setUserDao()方法名中橘霎,去set蔫浆,小寫首字母,得到userDao姐叁,要與xml文件中的bean的id相匹配瓦盛,才可實現(xiàn)自動裝配洗显。
<bean id="userService" class="com.dj.service.impl.UserServiceImpl" autowire="byName"/>
<bean id="userDao" class="com.dj.dao.impl.UserDaoImpl"></bean>
package com.dj.service.impl;
import com.dj.dao.UserDao;
import com.dj.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
8)spring的xml標(biāo)簽,大體上分為兩類:默認標(biāo)簽原环、自定義標(biāo)簽挠唆。默認標(biāo)簽是可以直接使用的,不需要額外導(dǎo)入的嘱吗,如beans玄组。自定義標(biāo)簽就是需要額外引入其它命名空間,并且通過前綴來引用的谒麦。
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/beans/spring-context.xsd">
<!--通過前綴俄讹,來引用命名空間-->
<context:property-placeholder></context:property-placeholder>
</beans>
spring默認的命名空間下,默認的標(biāo)簽绕德,共4種:
- beans:xml的根標(biāo)簽患膛,一個xml也可以有多套beans,但是不會用到耻蛇。也可以用profile屬性來切換不同的環(huán)境踪蹬。
beans是默認生成的根標(biāo)簽,多套beans標(biāo)簽也是放在根標(biāo)簽里面的臣咖。如果新增<beans>標(biāo)簽跃捣,它是要加在最下面的,不然會讓之前添加的<bean>找不到根夺蛇。
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/beans/spring-context.xsd">
<bean id="userDao2" factory-bean="userDaoFactory2" factory-method="getUserDaoBean"></bean>
<bean id="userDao3" class="com.dj.factory.UserDaoFactory3"></bean>
<beans profile="test">
<bean id="userService" class="com.dj.service.impl.UserServiceImpl"/>
</beans>
</beans>
那么如何告訴程序疚漆,我現(xiàn)在需要用test環(huán)境,而不是默認的環(huán)境吶蚊惯?在主程序中設(shè)置環(huán)境變量愿卸、或者在vm啟動參數(shù)添加也可以灵临。
//放在main方法中
System.setProperty("spring.profiles.active","test");
//vm options添加此環(huán)境變量
-Dspring.profiles.active=test
指定test環(huán)境時截型,除test標(biāo)簽內(nèi)的環(huán)境變量會生效,根默認的配置也會生效儒溉。
- bean:beans的子標(biāo)簽宦焦,這個就不詳細說了,一直在用顿涣。
- import:外部配置導(dǎo)入的標(biāo)簽波闹。項目比較大的時候,可能會根據(jù)業(yè)務(wù)拆分成模塊的配置文件涛碑,此時就需要import來將其它配置添加到主配置文件中精堕。在resources資源目錄下添加xml文件,userBeans.xml蒲障。
<!--在beans根目錄下歹篓,添加import標(biāo)簽-->
<import resource="userBeans.xml"></import>
- alias:指定bean的別名瘫证,使用較少,算了庄撮,不寫了背捌,就這樣吧。
<!--在beans根目錄下洞斯,添加alias標(biāo)簽-->
<alias name="userDao" alias="xxxx"></alias>
9)spring獲取bean的方法:
- public Object getBean(String name)毡庆;常用
- public <T> T getBean(String name, Class<T> requiredType);
- public <T> T getBean(Class<T> requiredType)烙如;常用么抗,且<bean>的返回類型只能有一種
UserService userService1 = (UserService) beanFactory.getBean("userService");
UserService userService2 = beanFactory.getBean("userService", UserService.class);
UserService userService3 = beanFactory.getBean(UserService.class);
10)spring引入非自定義bean(druid、mysql)亚铁,二者沒有什么強相關(guān)性乖坠,可獨立使用,具體druid和mysql的關(guān)系刀闷,百度吧熊泵。
<!--pom文件中導(dǎo)入坐標(biāo)-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--beans.xml文件中添加Druid的實例化信息-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!--beans.xml文件中添加mysql的實例化信息,具體查詢一下mysql的實例化方法就行-->
<bean id="driver" class="java.lang.Class" factory-method="forName">
<constructor-arg name="className" value="com.mysql.jdbc.Driver"></constructor-arg>
</bean>
<bean id="connection" class="java.sql.DriverManager" factory-method="getConnection" scope="prototype">
<constructor-arg name="url" value="jdbc:mysql://localhost:3306"></constructor-arg>
<constructor-arg name="user" value="root"></constructor-arg>
<constructor-arg name="password" value="root"></constructor-arg>
</bean>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
System.out.println(context.getBean("dataSource"));
System.out.println(context.getBean("connection"));
}
}