一、引入外部属性文件
首先我们将依赖进行导入:
<!--MySQL驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.22</version> </dependency> <!--数据源--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.31</version> </dependency>
我们所载入的bean如下所示:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean>
除此之外,我们进行查看其它标签:
<!--initialSize:设置当前的数据库连接池中初始化为我们设置的连接个数--> <property name="initialSize" value="5"></property> <!--maxActive:当前数据库连接池里面最大能够存在的连接的数量,默认为8,获取第九个的时候,会一直处于阻塞状态--> <property name="maxActive" value="8"></property> <!--maxWait:等待Druid为我们分配连接的最大等待时间,如果不进行设置,则会一直处于阻塞状态--> <property name="maxWait" value="4"></property>
我们进行测试:
public class DataSourceTest { @Test public void testDataSource() throws SQLException { ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml"); DruidDataSource dataSource = ioc.getBean(DruidDataSource.class); System.out.println(dataSource.getConnection()); } }
我们进行测试之后如下所示:
持此之外,IOC也可以利用读取·文件进行引入:
我们创建jdbc.properties文件,如下所示:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC jdbc.username=root jdbc.password=123456
我们在IOC里面进行引入:
<!--引入jdbc.properties,之后可以通过${key}的方式访问value--> <context:property-placeholder location="jdbc.properties"></context:property-placeholder> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
我们进行测试:
public class DataSourceTest { @Test public void testDataSource() throws SQLException { ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-datasource.xml"); DruidDataSource dataSource = ioc.getBean(DruidDataSource.class); System.out.println(dataSource.getConnection()); } }
我们运行完之后如下所示:
二、bean的作用域:
在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义如下表所示:
取值 含义 创建对象的时机 singleton(默认) 在IOC容器中,这个bean的对象始终为单实例 IOC容器初始化时 prototype 这个bean在IOC容器中有多个实例 获取bean时 我们载入的bean如下所示:
<bean id="student" class="com.rgf.spring.pojo.Student"> <property name="sid" value="1001"></property> <property name="sname" value="张三"></property> </bean>
我们进行测试如下所示:
public class ScopeTest { @Test public void testScope(){ ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-scope.xml"); Student student1 = ioc.getBean(Student.class); Student student2 = ioc.getBean(Student.class); System.out.println(student1==student2); }
运行之后如下所示:
我们发现默认为单例模式。
我们利用scope进行设置:
<bean id="student" class="com.rgf.spring.pojo.Student" scope="prototype"> <property name="sid" value="1001"></property> <property name="sname" value="张三"></property> </bean>
我们继续进行测试,发现如下所示:
此时即不再为单例模式,而是多例模式。
<!--scope:设置bean的作用域: scope="singleton|prototype" singleton(单例):表示获取该bean所对应的对象都是同一个 prototype(多例):表示获取该bean所对应的对象都不是同一个 (struts2里面处理请求响应的action即为多例:struts2里面将处理请求和响应的一般叫做action,里面进行设置成员变量,比如有一个user,我们为其设置setter和getter方法, setter用来收集浏览器传输过来的请求参数,getter方法是把我们当前的成员变量共享在我们的请求域中,我们实现修改功能,需要把用户信息提交到我们当前的服务器里面。 由我们当前的action里面的user请求获取,是利用set方法进行获取,会把浏览器传输过来的各个用户信息通过set方法赋值给user中的各个属性。而我们进行添加功能的时候 ,是没有id的,我们也要把数据提交到我们的服务器中,通过user收集我们添加的这些数据,添加功能没有id的,但是现在操作的action是被spring管理的,所以我们所获取的action‘ 都是同一个,经过修改功能,我们action里面的user就已经有值了,添加功能的时候,访问的仍然是同一个action,这个时候user里面的id也是有值的。添加之后,会给我们user里面的 各个属性进行赋值,添加页面没有id,user里面的id不会被覆盖,使用的仍然是修改功能里面的id。) --> <bean id="student" class="com.rgf.spring.pojo.Student" scope="prototype"> <property name="sid" value="1001"></property> <property name="sname" value="张三"></property> </bean>
如果在WebApplicationContext环境下还会有另外两个作用域(但不常用):
取值 含义 request 在一个请求范围内有效 session 在一个会话范围内有效
三、bean的生命周期
具体的生命周期过程
bean对象创建(调用无参构造器)
给bean对象设置属性
bean对象初始化之前操作(由bean的后置处理器负责)
bean对象初始化(需要配置bean时指定初始化方法)
bean对象初始化之后操作(由bean的后置处理器负责)
bean对象就绪可以使用
bean对象销毁(需要配置bean时指定销毁方法)
IOC容器关闭
我们创建如下类所示:
package com.rgf.spring.pojo; public class User { private Integer id; private String username; private String password; private Integer age; public User(Integer id, String username, String password, Integer age) { this.id = id; this.username = username; this.password = password; this.age = age; } public User() { System.out.println("生命周期1:实例化"); } public Integer getId() { return id; } public void setId(Integer id) { System.out.println("生命周期2:依赖注入"); this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", age=" + age + '}'; } public void initMethod(){ System.out.println("生命周期3:初始化"); } public void destroyMethod(){ System.out.println("生命周期4:销毁"); } }
我们进行注入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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.rgf.spring.pojo.User" init-method="initMethod" destroy-method="destroyMethod"> <property name="id" value="1"></property> <property name="username" value="admin"></property> <property name="password" value="123456"></property> <property name="age" value="23"></property> </bean> </beans>
测试如下所示:
package com.rgf.spring.test; import com.rgf.spring.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class LifeCycleTest { /** * 1.实例化 * 2.依赖注入 * 3.初始化,需要通过bean的init-method属性指定初始化的方法 * 4.IOC容器关闭时销毁,需要通过bean的destroy-method属性指定销毁的方法 */ @Test public void test(){ //ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法 ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml"); User user = ioc.getBean(User.class); System.out.println(user); ioc.close(); } }
运行之后如下所示:
bean的后置处理器:
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行。
我们进行创建该方法:
package com.rgf.spring.process; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //此方法在bean的生命周期初始化之前执行 System.out.println("MyBeanPostProcessor-->后置处理器postProcessBeforeInitialization"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // //此方法在bean的生命周期初始化之后执行 System.out.println("MyBeanPostProcessor-->后置处理器postProcessAfterInitialization"); return bean; } }
我们进行载入bean里面:
<bean id="MyBeanPostProcessor" class="com.rgf.spring.process.MyBeanPostProcessor"></bean>
此时我们继续进行测试:
package com.rgf.spring.test; import com.rgf.spring.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class LifeCycleTest { /** * 1.实例化 * 2.依赖注入 * 3.后置处理器的postProcessBeforeInitialization方法 * 4.初始化,需要通过bean的init-method属性指定初始化的方法 * 5.后置处理器的postProcessAfterInitialization方法 * 6.IOC容器关闭时销毁,需要通过bean的destroy-method属性指定销毁的方法 * 注意: * 若bean的作用域为单例时,生命周期的前三个步骤会在获取IOC容器时执行 * 若bean的作用域为多例时,生命周期的前三个步骤会在获取bean时执行 */ @Test public void test(){ //ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法 ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml"); User user = ioc.getBean(User.class); System.out.println(user); ioc.close(); } }
我们运行之后如下所示:
四、bean的作用域对生命周期的影响和后置处理器
当为单例模式的时候,我们在获取IOC的时候直接就进行了初始化:
当我们设置为多例模式的时候:
<bean id="user" class="com.rgf.spring.pojo.User" init-method="initMethod" destroy-method="destroyMethod" scope="prototype"> <property name="id" value="1"></property> <property name="username" value="admin"></property> <property name="password" value="123456"></property> <property name="age" value="23"></property> </bean>
我们再次执行如下所示:
我们发现此时bean没有初始化,因为有多个对象,所以没有必要刚开始就进行bean初始化。
我们继续进行如下测试:
@Test public void test(){ //ConfigurableApplicationContext是ApplicationContext的子接口,其中扩展了刷新和关闭容器的方法 ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-lifecycle.xml"); User user = ioc.getBean(User.class); System.out.println(user); ioc.close(); }
运行之后如下所示:
我们发现当我们在bean里面设置为多例的时候,此时ioc没有进行销毁。此时的销毁的方法则不再由IOC容器来进行管理。
/** * 1.实例化 * 2.依赖注入 * 3.后置处理器的postProcessBeforeInitialization方法 * 4.初始化,需要通过bean的init-method属性指定初始化的方法 * 5.后置处理器的postProcessAfterInitialization方法 * 6.IOC容器关闭时销毁,需要通过bean的destroy-method属性指定销毁的方法 * 注意: * 若bean的作用域为单例时,生命周期的前三个步骤会在获取IOC容器时执行 * 若bean的作用域为多例时,生命周期的前三个步骤会在获取bean时执行 */
五、FactoryBean
FactoryBean是Spring提供的一种整合第三方框架的常用机制,和普通的bean不同,配置一个FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是getObject()方法的返回值,通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。
我们进行创建UserFactoryBean,
package com.rgf.spring.factory; import com.rgf.spring.pojo.User; import org.springframework.beans.factory.FactoryBean; /** * FactoryBean是一个接口,需要创建一个类实现该接口 * 其中有三个方法: * getObject():通过一个对象交给IOC容器管理 * getObjectType():设置所提供对象的类型 * isSingleton():所提供的对象是否单例 * 当把FactoryBean的实现类配置为bean时,会将当前类中getObject()所返回的对象交给IOC容器管理。 */ public class UserFactoryBean implements FactoryBean<User> { @Override public User getObject() throws Exception { return new User(); } @Override public Class<?> getObjectType() { return User.class; } }
我们进入FactoryBean,查看是否是单例:
default boolean isSingleton() { return true; }
是单例模式
我们将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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="com.rgf.spring.factory.UserFactoryBean"> </bean> </beans>
我们进行测试如下所示:
package com.rgf.spring.test; import com.rgf.spring.pojo.User; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class FactoryBeanTest { @Test public void testFactoryBean(){ ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-factory.xml"); User u = ioc.getBean(User.class); System.out.println(u); } }
运行之后如下所示:
我们在bean里面没有进行配置该对象,但是最后却获取到了。