文章目录
Spring工厂类
BeanFactory为ApplicationContext的子接口,使用的BeanFactory接口用于工厂类后需要调用getBean()方法才能创建实体类对象,而使用ApplicationContext则会在加载配置文件时,就对配置文件中的类进行实例化。
而创建工厂类也提供了两个实体类,FileSystemXmlApplicationContext可以从本地磁盘中读取配置文件,而ClassPathXmlApplicationContext则会从项目的类路径中读取配置文件。
SpringBean的实例化
使用类构造器实例化对象
在配置文件中直接添加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="employee" class="com.spring.test.service.entity.Employee"/>
</beans>
这种方法也是默认的、最常用的一种方法
使用静态工厂类实例化对象
首先创建静态工厂
package com.spring.test.service.entity;
public class EmployeeFactory {
public static Employee getEmployee() {
return new Employee();
}
}
在配置文件中class属性指向新建的工厂类,再新增一个属性factory-method
指向静态工厂用于创建类的方法
<?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="employee" class="com.spring.test.service.entity.EmployeeFactory" factory-method="getEmployee"/>
</beans>
使用实例工厂实例化对象
实例工厂与静态工厂的区别主要在于获取实例的方法是否为静态方法,创建实例工厂:
package com.spring.test.service.entity;
public class EmployeeFactory {
public Employee getEmployee() {
return new Employee();
}
}
<?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="employeeFactory" class="com.spring.test.service.entity.EmployeeFactory"/>
<bean id="employee" factory-bean="employeeFactory" factory-method="getEmployee"/>
</beans>
首先创建bean
标签指向实例工厂,生成一个工厂实例,再创建一个bean
标签使用factory-bean
属性指向工厂实例的bean
标签,再设置factory-method
调用该实例的方法获取实例。
Bean的配置
id、name
id、name都是用来标记bean
标签的名称,且在IOC中必须是唯一的,两个属性唯一的区别就在于如果名称中包含特殊字符就只能使用name
class
用于设置类的完整路径,主要用于IOC容器生成类的实例
scope(作用域)
类别 | 说明 |
---|---|
singleton | 在SpringIOC容器中仅存在一个Bean实例,即以单例模式的方式存在 |
prototype | 每次调用getBean() 时都会返回一个新的实例 |
request | 在每次HTTP请求时都会创建一个新的Bean |
session | 同一个Session共享一个Bean |
SpringIOC默认是使用单例模式创建实例
示例:
<?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="employee" class="com.spring.test.service.entity.Employee" scope="prototype"/>
</beans>
package com.spring.test;
import com.spring.test.service.UserService;
import com.spring.test.service.entity.Employee;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring {
@Test
public void test() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Employee employee = (Employee) applicationContext.getBean("employee");
Employee employee1 = (Employee) applicationContext.getBean("employee");
System.out.println(employee);
System.out.println(employee1);
}
}
由此可见使用prototype属性后,每次创建的对象都指向不同的内存地址
生命周期
在初始化或销毁bean时需要做处理工作时,可以使用init-method
属性和destory-method
属性指定初始化和销毁方法,使得对象在创建或销毁时完成指定业务。
在实现Bean的实例化到销毁过程中,需要执行以下步骤,也就是Bean的完整的生命周期
- instantiate bean调用Bean的构造方法实例化对象
- populate properties 封装属性
- 如果Bean实现
BeanNameAware
类,执行setBeanName
方法将配置文件中的指定的Bean的名称代入方法中 - 如果Bean实现
BeanNameAware
或ApplicationContextAware
,执行setBeanFactory
方法或者上下文对象setApplicationContext
方法,使Bean了解工厂信息 - 如果存在某一个类实现了
BeanPostProcessor
(后处理Bean),将会执行postProcessBeforeInitialization
方法,在类的实例化前去执行该方法
package com.spring.test.entity;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("第五步:初始化前执行该方法");
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("第八步:初始化后执行该方法");
return o;
}
}
MyBeanPostProcessor 类在创建实例时会被Spring自动调用所以在配置时不需要配置再配置bean的id属性
<?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="employee" class="com.spring.test.entity.Employee" init-method="init" destroy-method="customerDestroy">
<property name="name" value="Mary"/>
</bean>
<bean class="com.spring.test.service.entity.MyBeanPostProcessor"/>
</beans>
- 如果bean实现了
InitializingBean
,执行afterPropertiesSet
方法,在属性设置后执行该方法 - 调用
<bean init-method="init">
指定初始化方法 - 如果存在类实现后方法
BeanPostProcessor
(处理Bean),执行postProcessAfterInitialization
- 执行Bean的其他业务方法
- 如果Bean实现了
DisposableBean
执行destroy
方法 - 调用
<bean destory-method="customerDestroy">
通过指定的销毁方法销毁实例
package com.spring.test.entity;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class Employee implements BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean {
private String name;
public Employee() {
System.out.println("第一步:初始化");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("第二步:设置属性");
this.name = name;
}
@Override
public void setBeanName(String s) {
System.out.println("第三步:设置Bean名称" + s);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("第四步:了解工厂信息");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("第六步:属性后设置");
}
private void init() {
System.out.println("第七步:执行指定方法初始化实例");
}
public void say() {
System.out.println("第九步:执行其他业务方法");
}
@Override
public void destroy() throws Exception {
System.out.println("第十步:实现DisposableBean的destroy方法");
}
private void customerDestroy() {
System.out.println("第十一步:调用指定方法销毁实例");
}
}
以上的11个步骤就是Bean实例化话到销毁的整个过程,而利用postProcessAfterInitialization
方法可以对业务方法进行增强处理
示例:
创建一个接口UserService
和其实现类UserServiceImpl
package com.spring.test.service;
public interface UserService {
void findAll();
void save();
}
package com.spring.test.service.impl;
import com.spring.test.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void findAll() {
System.out.println("查询所有记录...");
}
@Override
public void save() {
System.out.println("保存记录");
}
}
当在调用findAll方法时需要对该方法增加权限限制,则可使用postProcessAfterInitialization
方法对该方法进行增强
package com.spring.test.service.entity;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("第五步:初始化前执行该方法");
return o;
}
/**
*
* @param o Bean类
* @param s Bean的名称
* @return Bean类
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("第八步:初始化后执行该方法");
if ("userService".equals(s)) {
// 使用对象代理
Object proxy = Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("findAll".equals(method.getName())) {
System.out.println("校验权限");
return method.invoke(o, args);
}
return method.invoke(o, args);
}
});
return proxy;
} else {
return o;
}
}
}
在测试类中调用findAll方法时,可以查看结果:
Spring属性注入
Spring属性注入主要依靠依赖一下两种方法:
构造方法
创建一个实体类,使用带参的构造方法创建实例:
package com.spring.test.entity;
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在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.spring.test.entity.User">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="25"/>
</bean>
</beans>
使用constructor-arg
标签,其中属性name
指定实体类的属性名称,value
指定该属性注入的值
此时在测试方法中创建实例,并打印该实例,可以得到该实例的属性信息
setter方法
在实体类中创建对应属性的setter方法
package com.spring.test.entity;
public class User {
private String name;
private Integer age;
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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在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.spring.test.entity.User">
<property name="name" value="张三"/>
<property name="age" value="25"/>
</bean>
</beans>
对于在一个实体类中包含另一个实体类的情况,可以使用property
的ref
属性指定其他的bean的id
或name
属性,例如:
新增一个用户组类,用户类中增加用户组类的属性,指定该用户所述的组别
package com.spring.test.entity;
public class UserGroup {
private String groupName;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
@Override
public String toString() {
return "UserGroup{" +
"groupName='" + groupName + '\'' +
'}';
}
}
<?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.spring.test.entity.User">
<property name="name" value="张三"/>
<property name="age" value="18"/>
<property name="userGroup" ref="userGroup"/>
</bean>
<bean id="userGroup" class="com.spring.test.entity.UserGroup">
<property name="groupName" value="管理员"/>
</bean>
</beans>
p空间属性注入
为了简化属性注入,在Spring2.5引入了p空间的概念。p:<属性名>="常量值"
注入常量属性,p:<属性名>-ref="Bean对象"
注入Bean类型
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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.spring.test.entity.User" p:name="张三" p:age="30" p:userGroup-ref="userGroup"/>
<bean id="userGroup" class="com.spring.test.entity.UserGroup" p:groupName="管理员"/>
</beans>
SpEL注入
SpEL(Spring exprission lauguage)通过Spring表达式为类进行属性注入。SpEL支持常量属性注入、引用其他Bean、使用其他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="userpw" class="com.spring.test.entity.UserPw"/>
<bean id="userGroup" class="com.spring.test.entity.UserGroup">
<property name="groupName" value="#{'管理员'}"/>
</bean>
<bean id="user" class="com.spring.test.entity.User">
<!-- 直接为属性赋值 -->
<property name="name" value="#{'张三'}"/>
<!-- 调用其他Bean的方法为该Bean的属性赋值 -->
<property name="password" value="#{userpw.getPassword()}"/>
<property name="age" value="#{30}"/>
<!-- 将其他Bean注入进该Bean -->
<property name="userGroup" value="#{userGroup}"/>
</bean>
</beans>
复杂类型数据注入
对于Array、List、Map、Set、Properties类型的属性,Spring也提供了相应的标签对复杂类型进行数据注入,例如:
新建一个类,设置Array、List、Map、Set、Properties类型的属性
package com.spring.test.service.entity;
import java.util.*;
public class CollectionBean {
private String[] arr;
private List<String> list;
private Map<String, String> map;
private Set<String> set;
private Properties properties;
public String[] getArr() {
return arr;
}
public void setArr(String[] arr) {
this.arr = arr;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "CollectionBean{" +
"arr=" + Arrays.toString(arr) +
", list=" + list +
", map=" + map +
", set=" + set +
", properties=" + properties +
'}';
}
}
在配置文件中对该类进行数据注入:
<?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="collectionBean" class="com.spring.test.service.entity.CollectionBean">
<property name="arr">
<list>
<!-- <ref bean="beanName"> -->
<value>abc</value>
<value>abc</value>
<value>abc</value>
</list>
</property>
<property name="list">
<list>
<!-- <ref bean="beanName"> -->
<value>123</value>
<value>123</value>
<value>123</value>
</list>
</property>
<property name="map">
<map>
<!-- <entry key="key" value-ref="value"> -->
<entry key="1" value="aaa"/>
<entry key="2" value="aaa"/>
<entry key="3" value="aaa"/>
</map>
</property>
<property name="set">
<set>
<!-- <ref bean="beanName"> -->
<value>abc</value>
<value>abd</value>
<value>abe</value>
</set>
</property>
<property name="properties">
<props>
<prop key="1">1</prop>
<prop key="2">1</prop>
<prop key="3">1</prop>
</props>
</property>
</bean>
</beans>
如果集合的值为数据类型可以直接使用value属性为属性赋值,如果使用的类型集合则可以使用value-ref去引入其他bean。