目录
一、含义
二、基于 XML 管理 Bean
1、准备
2、配置 Bean
3、获取 Bean
4、Spring 管理数据源
5、自动装配
三、基于注解管理 Bean
1、准备
2、标记
3、扫描
4、获取 Bean
5、自动装配
四、其他知识
1、Bean 的作用域
2、Bean 的生命周期
3、Bean 的后置处理器
4、FactoryBean
一、含义
把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。
二、基于 XML 管理 Bean
1、准备
创建 Maven 工程,pom.xml 添加依赖。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.1</version> </dependency> </dependencies>com.example.pojo 包下,创建实体类 Teacher、Clazz、Student。
public class Teacher { //Alt+Insert,一键生成构造器、Get、Set、toString 方法... private Integer tid; private String tname; public Teacher() { } public Teacher(Integer tid, String tname) { this.tid = tid; this.tname = tname; } public void setTid(Integer tid) { this.tid = tid; } public void setTname(String tname) { this.tname = tname; } public Integer getTid() { return tid; } public String getTname() { return tname; } @Override public String toString() { return "Teacher{" + "tid=" + tid + ", tname='" + tname + '\'' + '}'; } } public class Clazz { private Integer cid; private String cname; private List<Student> students; public Clazz() { } public Clazz(Integer cid, String cname) { this.cid = cid; this.cname = cname; } public Integer getCid() { return cid; } public String getCname() { return cname; } public List<Student> getStudents() { return students; } public void setCid(Integer cid) { this.cid = cid; } public void setCname(String cname) { this.cname = cname; } public void setStudents(List<Student> students) { this.students = students; } @Override public String toString() { return "Clazz{" + "cid=" + cid + ", cname='" + cname + '\'' + ", students=" + students + '}'; } } public class Student { private Integer sid; private String sname; private Integer age; private String gender; private Clazz clazz; private String[] hobby; private Map<String,Teacher> teacherMap; public Student() { } public Student(Integer sid, String sname, Integer age, String gender) { this.sid = sid; this.sname = sname; this.age = age; this.gender = gender; } public Integer getSid() { return sid; } public String getSname() { return sname; } public Integer getAge() { return age; } public String getGender() { return gender; } public Clazz getClazz() { return clazz; } public String[] getHobby() { return hobby; } public Map<String, Teacher> getTeacherMap() { return teacherMap; } public void setSid(Integer sid) { this.sid = sid; } public void setSname(String sname) { this.sname = sname; } public void setAge(Integer age) { this.age = age; } public void setGender(String gender) { this.gender = gender; } public void setClazz(Clazz clazz) { this.clazz = clazz; } public void setHobby(String[] hobby) { this.hobby = hobby; } public void setTeacherMap(Map<String, Teacher> teacherMap) { this.teacherMap = teacherMap; } @Override public String toString() { return "Student{" + "sid=" + sid + ", sname='" + sname + '\'' + ", age=" + age + ", gender='" + gender + '\'' + ", clazz=" + clazz + ", hobby=" + Arrays.toString(hobby) + ", teacherMap=" + teacherMap + '}'; } }2、配置 Bean
右击目录 resources,依次选择 New -> XML Configuration File -> Spring Config,创建 Spring 配置文件 applicationContext.xml。
<?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:util="http://www.springframework.org/schema/util" 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/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean 标签:用于配置 bean,将对象交给 IOC 容器管理,其中 id 用于设置 bean 的唯一标识;class 用于设置 bean 对应类型的全类名。 --> <bean id="student" class="com.example.pojo.Student"> <!-- property 标签:用于给对象的属性赋值,其中 name 用于设置属性名;value 用于设置属性值; ref 用于设置要引用的 IOC 容器中 bean 的 id,从而将 bean 对应对象赋值给当前属性。 此时,Spring 使用 bean 对应类型的全类名,通过反射调用无参构造创建对象,再通过 setXxx() 为对象的属性赋值。 --> <property name="sid" value="1001"></property> <property name="sname" value="张三"></property> <!-- 为属性赋 null 值 --> <property name="age"> <null /> </property> <!-- 为属性赋含有 >、<等符号 的值,使用 CDATA 节将内容原样解析 --> <property name="gender"> <value><![CDATA[<男>]]></value> </property> <!-- 通过引用外部已声明的 bean,为属性赋 类类型 的值 --> <!--<property name="clazz" ref="clazz"></property>--> <!-- 通过内部 bean,为属性赋 类类型 的值 --> <property name="clazz"> <!-- 内部 bean 仅用于给属性赋值,不能在外部通过 IOC 容器获取,因此可以省略 id 属性 --> <bean id="clazzInner" class="com.example.pojo.Clazz"> <property name="cid" value="3002"></property> <property name="cname" value="飞船班"></property> </bean> </property> <!-- 为属性赋 数组类型 的值 --> <property name="hobby"> <array> <value>抽烟</value> <value>喝酒</value> <value>烫头</value> </array> </property> <!-- 通过引用外部已声明的集合类型的 bean,为属性赋 Map 集合类型 的值 --> <!--<property name="teacherMap" ref="teacherMap"></property>--> <!-- 通过 map 标签,为属性赋 Map 集合类型 的值 --> <property name="teacherMap"> <map> <entry key="2001" value-ref="teacherOne"></entry> <entry key="2002" value-ref="teacherTwo"></entry> </map> </property> </bean> <bean id="clazz" class="com.example.pojo.Clazz"> <property name="cid" value="3001"></property> <property name="cname" value="火箭班"></property> <!-- 通过引用外部已声明的集合类型的 bean,为属性赋 List 集合类型 的值 --> <!--<property name="students" ref="studentList"></property>--> <!-- 通过 list 标签,为属性赋 List 集合类型 的值 --> <property name="students"> <list> <ref bean="student"></ref> </list> </property> </bean> <!-- 通过 util 约束,配置集合类型的 bean --> <util:list id="studentList"> <ref bean="student"></ref> </util:list> <!-- 通过 util 约束,配置集合类型的 bean --> <util:map id="teacherMap"> <entry key="2001" value-ref="teacherOne"></entry> <entry key="2002" value-ref="teacherTwo"></entry> </util:map> <bean id="teacherOne" class="com.example.pojo.Teacher"> <!-- constructor-arg 标签:用于指定有参构造,其中 name 用于设置参数名;value 用于设置参数值。 此时,Spring 使用 bean 对应类型的全类名,通过反射调用与 constructor-arg 标签参数匹配的有参构造创建对象。 --> <constructor-arg name="tid" value="2001"></constructor-arg> <constructor-arg name="tname" value="李白"></constructor-arg> </bean> <bean id="teacherTwo" class="com.example.pojo.Teacher"> <property name="tid" value="2002"></property> <property name="tname" value="陶渊明"></property> </bean> </beans>要点精炼:基于 XML 配置 Bean,依赖注入之 setter 注入、依赖注入之构造器注入,为属性赋字面量类型的值、null 值、含有特殊符号的值、类类型的值、数组类型的值、List 集合类型的值、Map 集合类型的值。
3、获取 Bean
根据 id、类型、id & 类型,从 IOC 容器中获取 Bean 对象。
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象 ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); //(1)根据 id,从 IOC 容器中获取 bean 对象 Student studentOne = (Student) ioc.getBean("student"); /* (2)根据类型,从 IOC 容器中获取 bean 对象 注意:要求容器中有且仅有一个类型匹配的 bean,否则报错 NoSuchBeanDefinitionException/NoUniqueBeanDefinitionException; 满足 bean 唯一性的前提下,通过 bean 的类型、bean 所继承的类的类型、bean 所实现的接口的类型均可获取 bean 对象。 */ Student studentTwo = ioc.getBean(Student.class); //(3)根据类型 & id,从 IOC 容器中获取 bean 对象 Teacher teacher = ioc.getBean("teacherOne", Teacher.class);4、Spring 管理数据源
pom.xml 添加依赖。
<!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency> <!-- 数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.31</version> </dependency>右击目录 resources,依次选择 New -> Resource Bundle,创建属性配置文件 jdbc.properties。
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8 jdbc.username=root jdbc.password=root jdbc.initialSize=10 jdbc.maxActive=20 jdbc.maxWait=5000Spring 配置文件 applicationContext.xml,配置数据源。
<!-- 配置 Alibaba Druid 数据库连接池 --> <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> <!-- 数据库连接池,初始化连接数,默认为 0 --> <property name="initialSize" value="${jdbc.initialSize}"></property> <!-- 数据库连接池,最大连接数,默认为 8 --> <property name="maxActive" value="${jdbc.maxActive}"></property> <!-- 从数据库连接池获取连接的最大等待时间,默认为 -1(无限等待) --> <property name="maxWait" value="${jdbc.maxWait}"></property> </bean> <!-- 引入外部属性文件 --> <context:property-placeholder location="jdbc.properties"></context:property-placeholder>根据类型,从 IOC 容器中获取数据源。
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象 ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); //根据类型,从 IOC 容器中获取 Druid 数据库连接池对象 DruidDataSource dataSource = ioc.getBean(DruidDataSource.class); //从数据库连接池,获取数据库连接对象 System.out.println(dataSource.getConnection());5、自动装配
IOC 容器按照既定规则,匹配某个 bean,为指定 bean 依赖的类类型或接口类型属性赋值的过程。
通过 bean 标签的属性 autowire 设置自动装配的规则,值 no、default 表不装配,属性使用默认值;byType 表根据 setter 参数类型匹配 bean,为属性赋值,无匹配则不装配,多个匹配则抛 NoUniqueBeanDefinitionException;byName 表截取 setter 名称作为 bean 的 id 匹配 bean,为属性赋值,无匹配则不装配。
值得注意的是,基于 XML 配置 Bean,设置 autowire 为 byType / byName,Spring 会先调用无参构造创建对象,再 set 注入,为对象类类型或接口类型的属性赋值;字符串、数字等字面量类型的属性,跳过不赋值,需手动 property 赋值。
三、基于注解管理 Bean
1、准备
创建 Maven 工程,pom.xml 添加依赖。
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.1</version> </dependency> </dependencies>2、标记
com.example.controller 包下,创建类 UserController,添加注解 @Controller 将其标识为控制层组件。
@Controller public class UserController { @Autowired private UserService userService; //添加用户信息 public void saveUser(){ userService.saveUser(); } }com.example.service 包下,创建接口 UserService;com.example.service.impl 包下,创建实现类 UserServiceImpl,添加注解 @Service 将其标识为业务层组件。
public interface UserService { //保存用户信息 void saveUser(); } @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public void saveUser() { userDao.saveUser(); } }com.example.dao 包下,创建接口 UserDao;com.example.dao.impl 包下,创建实现类 UserDaoImpl,添加注解 @Repository 将其标识为持久层组件。
public interface UserDao { //保存用户信息 void saveUser(); } @Repository public class UserDaoImpl implements UserDao { @Override public void saveUser() { System.out.println("保存成功!"); } }此外,注解 @Component 用于将类标识为普通组件,@Controller、@Service、@Repository 都是其派生注解,基本功能一致,仅语义分层区别,方便开发者区分代码层次;注解只是做标记,为了让 Spring 知道开发者在哪些地方标记了什么注解,还需要配置组件扫描。
3、扫描
右击目录 resources,依次选择 New -> XML Configuration File -> Spring Config,创建 Spring 配置文件 applicationContext.xml。
<?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 https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 扫描组件 --> <context:component-scan base-package="com.example"> <!-- 不扫描谁 type 用于设置排除依据,值 annotation 表根据注解排除,此时 expression 用于设置要排除的注解的全类名; 值 assignable 表根据类型排除,此时 expression 为要排除的类型的全类名; SSM 整合时,springMVC 只扫描控制层,spring 扫描其他层。 --> <!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>--> <!--<context:exclude-filter type="assignable" expression="com.example.controller.UserController"/>--> <!-- 只扫描谁,此时需要在 context:component-scan 标签中设置属性 use-default-filters 的值为 false use-default-filters 属性默认值为 true,表所设置包下的所有类都需要扫描,此时可以使用排除扫描 use-default-filters 属性设置值为 false,表所设置包下的所有类都不需要扫描,此时可以使用包含扫描 --> <!--<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>--> <!--<context:include-filter type="assignable" expression="com.example.controller.UserController"/>--> </context:component-scan> </beans>通过 注解 & 扫描 配置的 bean,id 默认为类的小驼峰(如:userController),可通过标识类的注解的 value 属性值设置 bean 的自定义 id(如:@Controller("controller"));此时,Spring 通过反射调用默认无参构造创建对象。
4、获取 Bean
根据 id、类型、id & 类型,从 IOC 容器中获取 Bean 对象。
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象 ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); //(1)根据 id,从 IOC 容器中获取 bean 对象 UserController userController = (UserController) ioc.getBean("userController"); //(2)根据类型,从 IOC 容器中获取 bean 对象 UserService userService = ioc.getBean(UserService.class); //(3)根据类型 & id,从 IOC 容器中获取 bean 对象 UserDao userDao = ioc.getBean("userDaoImpl", UserDao.class);5、自动装配
成员变量添加 @Autowired 注解,即可完成自动装配。
Spring 默认根据成员变量的类型,在 IOC 容器中匹配某个 bean,为成员变量赋值。若有多个类型匹配的 bean,则将成员变量的变量名作为 bean 的 id,在 IOC 容器中匹配某个 bean,为成员变量赋值;若有多个类型匹配的 bean,但这些 bean 的 id 均与要赋值成员变量的变量名不一致,则抛 NoUniqueBeanDefinitionException,此时,可以在要赋值成员变量的变量名上添加注解 @Qualifier,通过该注解 value 属性值指定某个 bean 的 id,为成员变量赋值。若没有任何一个类型匹配的 bean,则抛 NoSuchBeanDefinitionException,因为 @Autowired 注解中属性 required 默认值为 true,要求必须完成自动装配,可以将属性 required 设置为 false,此时能装配则装配,无法装配则使用成员变量默认值。
值得注意的是,基于注解配置 Bean,成员变量添加 @Autowired 注解,Spring 会先调用默认无参构造创建对象,再利用反射直接给成员变量赋值,不属于 setter 注入、构造器注入;而 @Autowired 注解也可标记在 set 方法、构造器上,此处不做探讨。
四、其他知识
1、Bean 的作用域
规定 Spring IOC 容器,何时创建对象、创建多少个对象,以及对象的生效范围。基于 XML 管理 Bean,通过 bean 标签的属性 scope 设置 bean 的作用域,值 singleton 表单例(默认)、prototype 表多例(每次获取均为新对象);基于注解管理 Bean,通过注解 @Scope 设置 bean 的作用域,值 ConfigurableBeanFactory.SCOPE_SINGLETON 表单例(默认)、ConfigurableBeanFactory.SCOPE_PROTOTYPE 表多例(每次获取均为新对象)。
2、Bean 的生命周期
bean 为单例,获取 IOC 容器时,创建对象、依赖注入、执行后置处理器的 postProcessBeforeInitialization() 方法、执行初始化方法、执行后置处理器的 postProcessAfterInitialization() 方法;IOC 容器关闭时,执行销毁方法,代码中没有地方持有这个 bean 的引用后,等待 JVM 发生 GC 回收对象。
bean 为多例,获取 bean 时,创建对象、依赖注入、执行后置处理器的 postProcessBeforeInitialization() 方法、执行初始化方法、执行后置处理器的 postProcessAfterInitialization() 方法;不会执行销毁方法,代码中没有地方持有这个 bean 的引用后,等待 JVM 发生 GC 回收对象。
基于 XML 管理 Bean,通过 bean 标签的属性 init-method 设置 bean 的初始化方法,destroy-method 设置 bean 的销毁方法;基于注解管理 Bean,通过注解 @PostConstruct 设置 bean 的初始化方法,@PreDestroy 设置 bean 的销毁方法。
3、Bean 的后置处理器
用于在 Bean 生命周期初始化前后,添加额外操作。创建类 MyBeanPostProcessor 实现 BeanPostProcessor 接口,重写 postProcessBeforeInitialization()、postProcessAfterInitialization() 方法;
public class MyBeanPostProcessor implements BeanPostProcessor { //Ctrl+O,选择需要实现的方法 //在 bean 生命周期初始化之前执行,参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } //在 bean 生命周期初始化之后执行,参数 bean 为当前 IOC 容器管理的 bean、beanName 为 bean 的 id @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }Spring 配置文件 applicationContext.xml,配置后置处理器。此处以 XML 方式进行配置,也可使用注解方式配置 bean。
<bean class="com.example.process.MyBeanPostProcessor"></bean>值得注意的是,Bean 的后置处理器不是单独针对某一个 bean 生效,而是针对 IOC 容器中所有 bean 都会执行。
4、FactoryBean
Spring 提供的工厂 Bean 接口,用于封装复杂对象的创建逻辑。com.example.pojo 包下,创建实体类 User。
public class User { private Integer id; private String username; private String password; private Integer age; public User() { } public User(Integer id, String username, String password, Integer age) { this.id = id; this.username = username; this.password = password; this.age = age; } public Integer getId() { return id; } public String getUsername() { return username; } public String getPassword() { return password; } public Integer getAge() { return age; } public void setId(Integer id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", age=" + age + '}'; } }创建类 UserFactoryBean 实现 FactoryBean 接口,泛型配置为最终交给 IOC 容器管理的对象类型,重写 getObject()、getObjectType()、isSingleton() 方法;
public class UserFactoryBean implements FactoryBean<User> { //指定泛型为最终交给 IOC 容器管理的对象类型 @Override public User getObject() throws Exception { //在这里手写复杂创建逻辑! return new User(); //返回最终交给 IOC 容器管理的对象 } @Override public Class<?> getObjectType() { return User.class; //返回最终交给 IOC 容器管理的对象的类型 } @Override public boolean isSingleton() { return true; //设置最终交给 IOC 容器管理的对象是否单例,true表单例(默认)、false表多例 } }Spring 配置文件 applicationContext.xml,配置 UserFactoryBean。此处以 XML 方式进行配置,也可使用注解方式配置 bean。
<bean id="userBean" class="com.example.factory.UserFactoryBean"></bean>根据 id,从 IOC 容器中获取 Bean 对象。
//通过读取类路径下 XML 格式的配置文件,创建 IOC 容器对象 ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); //根据 id,从 IOC 容器中获取 bean 对象 User user = (User) ioc.getBean("userBean"); //加 & 符号,从 IOC 容器中获取工厂本身 UserFactoryBean 对象 UserFactoryBean userFactoryBean = (UserFactoryBean) ioc.getBean("&userBean");值得注意的是,BeanFactory 是 IOC 容器最顶层根接口,是 Bean 的工厂,负责创建、获取、管理所有 Bean 对象;而 FactoryBean 是 Spring 提供的工厂 Bean 接口,用于封装复杂对象的创建逻辑,Spring 先实例化 FactoryBean 工厂,再调用 getObject() 获取目标对象放入容器,对外提供使用,工厂本身也可通过 id 加 & 前缀,从 IOC 容器中获取。
本文相关项目,请关注程序员钟霖,回复CSDN获取。
友情链接
【附安装包】Java/JDK介绍、下载、安装、配置与使用(保姆级教程)_java jdk-CSDN博客
【附安装包】Maven下载、安装、配置与使用(保姆级教程)-CSDN博客
【附安装包】IDEA下载、安装、配置与使用(保姆级教程)_idea下载安装配置教程-CSDN博客