完整项目结构
├── 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>