完整項目結構
├── pom.xml
└── src/├── main/│ ├── java/│ │ └── com/│ │ └── zhang/│ │ ├── bean/│ │ │ ├── Address.java│ │ │ ├── MyBeanPostProcessor.java│ │ │ └── Person.java│ │ └── factory/│ │ ├── MyFactoryBean.java│ │ ├── PersonInstanceFactory.java│ │ └── PersonStaticFactory.java│ └── resources/│ │ ├── db.properties│ └── ioc.xml└── test/└── java/└── MyTest.java
項目主要內容和學習方面
這個項目是一個Spring框架的學習練習demo,主要學習了以下幾個方面:
-
Spring IoC容器基礎
- 通過
ClassPathXmlApplicationContext
加載配置文件并創建IoC容器 - 學習了從容器中獲取Bean的多種方式(根據id、根據類型)
- 通過
-
Bean的作用域
- 測試了Spring中Bean的單例模式
-
依賴注入
- 通過配置文件實現
Person
和Address
等實體類的依賴注入
- 通過配置文件實現
-
Bean的生命周期
- 實現了
MyBeanPostProcessor
類,學習Bean的后置處理器機制
- 實現了
-
工廠模式
- 學習了多種工廠創建Bean的方式:
- 靜態工廠(
PersonStaticFactory
) - 實例工廠(
PersonInstanceFactory
) - FactoryBean(
MyFactoryBean
)
- 靜態工廠(
- 學習了多種工廠創建Bean的方式:
-
數據源配置
- 項目中包含了Druid數據源的配置測試
主要Java類說明
- 實體類:
Person.java
(人員實體)、Address.java
(地址實體) - 后置處理器:
MyBeanPostProcessor.java
(Bean生命周期處理器) - 工廠類:
PersonStaticFactory.java
(靜態工廠)、PersonInstanceFactory.java
(實例工廠)、MyFactoryBean.java
(FactoryBean實現) - 測試類:
MyTest.java
(用于測試Spring的各種功能)
項目依賴包括Spring框架的核心模塊、MySQL驅動和Druid數據源。
代碼
bean
public class Address {private String province;private String city;private String town;public Address() {System.out.println("Address被創建");}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getTown() {return town;}public void setTown(String town) {this.town = town;}@Overridepublic String toString() {return "Address{" +"province='" + province + '\'' +", city='" + city + '\'' +", town='" + town + '\'' +'}';}
}import java.util.*;public class Person {private Integer id;private String name;private Integer age;private String gender;
// private Date date;private String[] hobbies;private Address address;private List<Address> lists;
private Set<String> sets;
private Map<String,Object> maps;
private Properties properties;public void init(){
// 先寫n行代碼完成初始化功能System.out.println("person對象初始化完成");
}
public void destroy(){System.out.println("person對象被銷毀");
}public Person() {System.out.println("person被創建");}public Person(Address address) {this.address = address;}public Person(Integer id, String name, Address address) {this.id = id;this.name = name;this.address = address;}// public Person(Integer id, String name, Integer age, String gender, Date date) {
// this.id = id;
// this.name = name;
// this.age = age;
// this.gender = gender;
// this.date = date;
// }public Person(Integer id, String name, Integer age, String gender) {this.id = id;this.name = name;this.age = age;this.gender = gender;}public Person(Integer id, String name, String gender) {this.id = id;this.name = name;this.gender = gender;System.out.println("gender... ...");}public Person(Integer id, String name, Integer age) {this.id = id;this.name = name;this.age = age;System.out.println("age... ...");}public Integer getId() {return id;}public void setId(Integer 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 String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String[] getHobbies() {return hobbies;}public void setHobbies(String[] hobbies) {this.hobbies = hobbies;}public Address getAddress() {return address;}public void setAddress(Address address) {this.address = address;}public List<Address> getLists() {return lists;}public void setLists(List<Address> lists) {this.lists = lists;}public Set<String> getSets() {return sets;}public void setSets(Set<String> sets) {this.sets = sets;}public Map<String, Object> getMaps() {return maps;}public void setMaps(Map<String, Object> maps) {this.maps = maps;}public Properties getProperties() {return properties;}public void setProperties(Properties properties) {this.properties = properties;}
// public Date getDate() {
// return date;
// }
//
// public void setDate(Date date) {
// this.date = date;
// }@Overridepublic String toString() {return "Person{" +"id=" + id +", name='" + name + '\'' +", age=" + age +", gender='" + gender + '\'' +", hobbies=" + Arrays.toString(hobbies) +", address=" + address +", lists=" + lists +", sets=" + sets +", maps=" + maps +", properties=" + properties +'}';}}
初始化的時候做事情
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanPostProcessor implements BeanPostProcessor {
// 在每一個對象的初始化方法之前執行public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("postProcessBeforeInitialization:"+beanName);return bean;}/*** 在每一個對象的初始化方法之后執行* @param bean 表示創建的具體對象* @param beanName 表示bean的id屬性* @return* @throws BeansException*/public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof Person){System.out.println();}else {System.out.println();}System.out.println("postProcessAfterInitialization:"+beanName);return bean;}
}
工廠
單例模式
import com.zhang.bean.Person;
import org.springframework.beans.factory.FactoryBean;
/*此方式是spring創建bean方式的一種補充,用戶可以按照需求創建對象,
* 創建的對象交由spring IOC容器來進行管理,無論是否是單例,都是在
* 用到的時候才會創建該對象,不用該對象不會創建*/
public class MyFactoryBean implements FactoryBean<Person> {/*返回獲取的bean*/public Person getObject() throws Exception {Person person=new Person();person.setId(3);person.setName("王五");return person;}
/*獲取返回bean的類型*/public Class<?> getObjectType() {return Person.class;}
/*判斷當前bean是否是單例的*/public boolean isSingleton() {return true;}
}import com.zhang.bean.Person;
//*實例工廠類*/
public class PersonInstanceFactory {public Person getInstance(String name){Person person = new Person();person.setId(2);person.setName(name);person.setAge(22);return person;}}import com.zhang.bean.Person;
/*靜態工廠類*/
public class PersonStaticFactory {public static Person getInstance(String name){Person person = new Person();person.setId(1);person.setName(name);person.setAge(11);return person;}
}
測試
import com.alibaba.druid.pool.DruidDataSource;
import com.zhang.bean.Address;
import com.zhang.bean.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("ioc.xml");/*根據bean標簽的id來獲取對象*/
// Person person = context.getBean("person", Person.class);
// Person person1 = context.getBean("person", Person.class);
// System.out.println(person==person1);
// System.out.println(person);/*根據bean的類型來獲取對象* 注意:當通過類型進行獲取的時候,如果存在兩個相同類型對象,將無法完成獲取工作* */
// Person bean = context.getBean(Person.class);
// System.out.println(bean);/*當需要從容器中獲取對象的時候,最好要保留無參構造方法,因為底層的實現是反射*Object obj = clazz.newInstance() ;默認調用無參的構造方法,此方法已經被棄用Object obj = clazz.newConstructorInstance();*/
// Person bean = context.getBean("person6",Person.class);
// System.out.println(bean);
// Address address2 = context.getBean("address2", Address.class);
// System.out.println(address2);
// Person son = context.getBean("son",Person.class);
// System.out.println(son);
// Person parent = context.getBean("parent",Person.class);
// System.out.println(parent);
// Person person2 = context.getBean("person2", Person.class);
// Person person3 = context.getBean("person2", Person.class);
// System.out.println(person2==person3);
// Person person = context.getBean("person", Person.class);
// System.out.println(person);
// Person person2 = context.getBean("person2", Person.class);
// System.out.println(person2);
// Person myFactoryBean = context.getBean("myFactoryBean", Person.class);
// System.out.println(myFactoryBean);Person person = context.getBean("person2", Person.class);System.out.println(person);
// 關閉ioc容器
// ((ClassPathXmlApplicationContext) context).close();
// DruidDataSource dataSource = context.getBean("dataSource2", DruidDataSource.class);
// System.out.println(dataSource);
// System.out.println(dataSource.getCloseCount());}
}
配置文件
<?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:p="http://www.springframework.org/schema/p"xmlns:cont="http://www.springframework.org/schema/context"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!--7到149一起注釋掉--><!-- <!–在進行框架配置的時候,可以使用xml文件,也可以使用注解的方式--><!-- 很多同學覺得xml的方式比較麻煩,但是xml的配置方式還是要學習的,因為在項目開發過程中,--><!-- 很多情況下是xml和注解一起工作,而且xml配置的方式比較完整–>--><!-- <!–<bean id="person" class="com.zhang.bean.Person" scope="prototype">–>--><!-- <!– scope="prototype"改為分單例–>--><!-- <bean id="person" class="com.zhang.bean.Person">--><!-- <!–根據屬性值設置的時候,name的名稱取決于set方法后面的參數首字符小寫的名稱–>--><!-- <property name="id" value="1"></property>--><!-- <property name="name" value="zhangsan"></property>--><!-- <property name="age" value="20"></property>--><!-- <property name="gender" value="男"></property>--><!-- <!– <property name="date" ref="now"></property>–>--><!-- <!– <property name="date" value="2020/02/06"></property>–>--><!-- </bean>--><!-- <bean id="now" class="java.util.Date">--><!-- </bean>--><!-- <!–使用構造器方法賦值的時候,參數的name屬性是由什么來決定的?由構造方法的參數名稱決定的--><!-- name:表示參數列表的名稱--><!-- value:表示實際的具體值--><!-- type:表示值的類型--><!-- index:表示值的下標,從0開始--><!-- –>--><!-- <bean id="person2" class="com.zhang.bean.Person">--><!-- <constructor-arg name="id" value="2"></constructor-arg>--><!-- <constructor-arg name="name" value="lisi"></constructor-arg>--><!-- <constructor-arg name="age" value="20"></constructor-arg>--><!-- <constructor-arg name="gender" value="男"></constructor-arg>--><!-- </bean>--><!-- <!–當通過構造器方法賦值的時候,可以把name屬性省略不寫,但是要注意必須要保證參數值跟構造器的參數列表的順序一致,--><!-- 如果非要不一致的話,可以通過index的下標方式來標注,從0開始 –>--><!-- <bean id="person3" class="com.zhang.bean.Person">--><!-- <constructor-arg value="2" index="2"></constructor-arg>--><!-- <constructor-arg value="lisi"></constructor-arg>--><!-- <constructor-arg value="22" index="0"></constructor-arg>--><!-- <constructor-arg value="男"></constructor-arg>--><!-- </bean>--><!-- <!–當有多個相同參數的構造方法存在的時候,默認情況下是覆蓋的過程,后面的構造方法會覆蓋前面的構造方法,--><!-- 如果非要賦值給另外一個構造方法的話,可以使用type的參數來進行指定–>--><!-- <bean id="person4" class="com.zhang.bean.Person">--><!-- <constructor-arg value="4"></constructor-arg>--><!-- <constructor-arg value="wangwu"></constructor-arg>--><!-- <constructor-arg value="22" type="java.lang.Integer"></constructor-arg>--><!-- </bean>--><!-- <!–總結:--><!-- 在日常工作中,一般都是用name, value的方式,很少有人去使用index或者type的方式,但是要注意各種情況出現的問題--><!-- –>--><!-- <!– 使用p命名空間來給屬性命名--><!-- –>--><!-- <bean id="person5" class="com.zhang.bean.Person" p:id="5" p:name="zhaoliu" p:age="25" p:gender="女"></bean>--><!-- <!– 給復雜類型進行賦值–>--><!-- <bean id="person6" class="com.zhang.bean.Person">--><!-- <property name="id" value="6"></property>--><!-- <property name="name" value="zhangsan6"></property>--><!-- <property name="age" value="20"></property>--><!-- <property name="gender" value="男"></property>--><!-- <!– 給數組賦值 使用array標簽–>--><!-- <!– <property name="hobbies" value="book,girl,movie"></property>–>--><!-- <property name="hobbies">--><!-- <array>--><!-- <value>book</value>--><!-- <value>girl</value>--><!-- <value>movie</value>--><!-- </array>--><!-- </property>--><!-- <!– 引用類型賦值,可以使用ref引入外部bean–>--><!-- <property name="address" ref="address"></property>--><!-- <!– 給list 賦值–>--><!-- <!– <property name="lists" value="1,2,3"></property>–>--><!-- <property name="lists">--><!-- <list>--><!-- <!–使用內部bean,無法從IOC容器中直接獲取對象的值–>--><!-- <bean id="address2" class="com.zhang.bean.Address">--><!-- <property name="province" value="北京"></property>--><!-- </bean>--><!-- <bean class="com.zhang.bean.Address">--><!-- <property name="province" value="上海"></property>--><!-- </bean>--><!-- <!–使用外部bean,可以隨意從IOC容器獲取對象的值–>--><!-- <ref bean="address"></ref>--><!-- </list>--><!-- </property>--><!-- <property name="sets">--><!-- <set>--><!-- <value>zhangsan</value>--><!-- <value>zhangsan</value>--><!-- <value>wangwu</value>--><!-- </set>--><!-- </property>--><!-- <property name="maps">--><!-- <map>--><!-- <entry key="a" value="aaa"></entry>--><!-- <entry key="address" value-ref="address"></entry>--><!-- <entry key="address2">--><!-- <bean class="com.zhang.bean.Address">--><!-- <property name="province" value="廣東"></property>--><!-- </bean>--><!-- </entry>--><!-- <entry>--><!-- <key>--><!-- <value>hehe</value>--><!-- </key>--><!-- <value>haha</value>--><!-- </entry>--><!-- <entry key="list">--><!-- <list>--><!-- <value>11</value>--><!-- <value>22</value>--><!-- </list>--><!-- </entry>--><!-- </map>--><!-- </property>--><!-- <property name="properties">--><!-- <props >--><!-- <prop key="111">aaa</prop>--><!-- <prop key="222">bbb</prop>--><!-- </props>--><!-- </property>--><!-- </bean>--><!-- <bean id="address" class="com.zhang.bean.Address">--><!-- <property name="province" value="河北省"></property>--><!-- <property name="city" value="邯鄲"></property>--><!-- <property name="town" value="武安"></property>--><!-- </bean>--><!--<!–bean之間的繼承關系–>--><!--<!– 可以使用abstract標簽定義抽象bean,無法進行實例化–>--><!-- <bean id="parent" class="com.zhang.bean.Person" abstract="false">--><!-- <!–根據屬性值設置的時候,name的名稱取決于set方法后面的參數首字符小寫的名稱–>--><!-- <property name="id" value="1"></property>--><!-- <property name="name" value="zhangsan"></property>--><!-- <property name="age" value="20"></property>--><!-- <property name="gender" value="男"></property>--><!-- </bean>--><!--<!– 可以通過parent屬性來獲取父bean中的某些屬性值–>--><!--<bean id="son" class="com.zhang.bean.Person" parent="parent">--><!-- <property name="name" value="haha"></property>--><!--</bean>--><!-- 創建bean的時候依賴關系當bean對象被創建的時候,是按照xml配置文件定義的順序創建的,誰在前誰就先被創建,如果需要干擾創建的順序,可以使用 depends-on屬性一般在實際工作中不必在意bean創建的順序,無論依賴的對象在創建完成之后都會進行賦值操作--><!-- <bean id="person" class="com.zhang.bean.Person" depends-on="address"> </bean>--><!-- <bean id="address" class="com.zhang.bean.Address" ></bean>--><!-- 設置bean對象的生命周期通過scope屬性可以指定當前bean的作用域singleton:單例模式、從ioc容器中獲取的都是同一個對象,默認的作用域prototype:多例模式、從IOC容器中獲取的對象每次都是新創建的在spring4.x的版本中還包括另外兩個作用域:request:每次發送請求都會有一個新的對象session:每次會話都會由一個新的對象幾乎不用,從來沒有用過,因此在5版本的時候就被淘汰了--><!-- 注意:如果是單例作用域的話,每次在創建ioc容器完成之前此對象就已經創建完成如果是prototype作用域的話,每次是在需要用到此對象的時候才會創建--><!-- <bean id="person2" class="com.zhang.bean.Person" scope="singleton"></bean>--><!--利用工廠方式創建bean--><!--靜態工廠類名.靜態方法()--><!-- <bean id="person" class="com.zhang.factory.PersonStaticFactory" factory-method="getInstance">--><!-- <constructor-arg value="zhangsan"></constructor-arg>--><!-- </bean>--><!--實例工廠:先創建工廠實例,然后調用工廠實例的方法factory-bean:表示具體工程類的實例factory-method:表示具體工廠實例方法--><!-- <bean id="instanceFactory" class="com.zhang.factory.PersonInstanceFactory"></bean>--><!-- <bean id="person2" class="com.zhang.bean.Person" factory-bean="instanceFactory" factory-method="getInstance">--><!-- <constructor-arg value="lisi"></constructor-arg>--><!-- </bean>--><!-- <bean id="myFactoryBean" class="com.zhang.factory.MyFactoryBean"></bean>--><!-- spring容器在創建對象的時候可以指定具體的初始化和銷毀方法init-method:在對象創建完成之后會調用初始化方法destroy-method:在容器關閉的時候會調用銷毀方法--><!-- 初始化和銷毀的方法跟scope屬性也是相關聯的如果是singleton的話,初始化和銷毀的方法都存在如果是prototype的話,初始化會調用,但是銷毀的方法不會調用--><!-- <bean id="person" class="com.zhang.bean.Person" init-method="init" destroy-method="destroy"></bean>--><!-- <bean id="myBeanPostProcessor" class="com.zhang.bean.MyBeanPostProcessor"></bean>--><!-- <bean id="address" class="com.zhang.bean.Address"></bean>--><!-- <!–spring管理第三方bean–>-->
<!-- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">-->
<!-- <property name="username" value="root"></property>-->
<!-- <property name="password" value="1234"></property>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/exam"></property>-->
<!-- <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>-->
<!-- </bean>-->
<!-- <!– 當需要引入外部的配置文件的時候,需要導入一些context的命名空間–>--><!-- <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>-->
<!-- <!–在配置文件編寫屬性的時候需要注意,-->
<!-- spring容器在進行啟動的時候,會讀取當前系統的某些環境變量的配置,-->
<!-- 當前系統的用戶名是用username來表示的,所以最好的方式是添加前綴來做區分–>-->
<!-- <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource">-->
<!-- <property name="username" value="${jdbc.username}"></property>-->
<!-- <property name="password" value="${jdbc.password}"></property>-->
<!-- <property name="url" value="${url}"></property>-->
<!-- <property name="driverClassName" value="${driverClassName}"></property>-->
<!-- </bean>-->
<!-- <bean id="person" class="com.zhang.bean.Person">-->
<!-- <property name="name" value="${jdbc.username}"></property>-->
<!-- </bean>-->
<!-- spring中的自動裝配--><bean id="address" class="com.zhang.bean.Address"><property name="province" value="河北"></property><property name="city" value="邯鄲"></property><property name="town" value="武漢"></property></bean>
<!-- <bean id="address2" class="com.zhang.bean.Address">-->
<!-- <property name="province" value="河北2"></property>-->
<!-- <property name="city" value="邯鄲2"></property>-->
<!-- <property name="town" value="武漢2"></property>-->
<!-- </bean>-->
<!-- 在spring中,可以使用自動裝配的功能,spring會把某些bean注入到另外的bean中
可以使用autowire屬性來實現自動裝配,有一下幾種情況
default/no:不裝配
byName:按照id進行裝配,根據set方法的后面的名稱首字母小寫決定的,不是參數列表的名稱
byType:按照bean的類型來進行裝配,但是如果有多個類型,就會報錯,不知道選擇哪一個具體的類型
constructor:按照構造器進行裝配,首先按照類型進行判斷,如果有多個類型相同的bean,再按照id進行判斷
--><!-- 為什么使用bytype的時候會加載環境變量出來
環境變量中存儲的就是key:valve會放到那里面,換一種類型就不會了
--><!-- <bean id="person" class="com.zhang.bean.Person" autowire="constructor"></bean>-->
<!-- SpEL表達式語言的使用--><bean id="person2" class="com.zhang.bean.Person">
<!--可以引入bean對象的屬性-->
<property name="name" value="#{address.province}"></property>
<!--可以支持運算符的所有操作--><property name="age" value="#{2*3}"></property>
<!--可以使用外部bean--><property name="address" value="#{address}"></property>
<!-- 可以調用靜態方法-->
<property name="hobbies" value="#{T(java.util.UUID).randomUUID().toString().substring(0,4)}"></property>
<!--調用非靜態方法--><property name="gender" value="#{address.getCity()}"></property></bean>
</beans>
properties
jdbc.username=root
jdbc.password=1234
url=jdbc:mysql://localhost:3306/test
driverClassName=com.mysql.jdbc.Driver
pom.xml
<?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.zhang</groupId><artifactId>spring_study02</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.5</version></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.6</version></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.48</version></dependency></dependencies>
</project>