/**
* DIP依赖倒置(开发原则):面向接口编程,面向抽象编程,不要面向具体编程。
* IoC控制反转(编程思想):将控制对象的创建与销毁和对象间的关系交给了Spring的IoC容器管理。
* DI 依赖注入(控制反转实现方式):动态的向某个对象提供它所需要的其他对象。
* AOP切面编程(编程思想):单独开发通用功能块、在需要位置通过方法执行前、后等方式切入(自动调用)
*
* SpringMVC => WEB三层架构
* 基本概念:将软件系统分为 Model / view / Controller
* M:Model持久层 => 具体业务操作,如:查询数据库、封装对象
* V:view视图层 => 进行数据展示
* C:Controller控制层 => 1.获取View请求 2.调用Model将数据传给视图层进行展示
* 结构关系:
* 视图层(发送请求) => 业务逻辑层 => 数据访问层
* 数据访问层(返回数据) => 业务逻辑层 => 视图层(展示处理好的数据)
*/
Spring基本入门
①:Maven配置(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.powernode</groupId>
<artifactId>Spring6-002</artifactId>
<version>1.0-SNAPSHOT</version>
<!--配置多个仓库-->
<repositories>
<!--spring里程碑版本的仓库-->
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<!--依赖-->
<dependencies>
<!--Spring-context 基础-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--junit 单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<!--1og4j2 日志-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
②:创建实体类
public class User { //User实体类
}
public class UserDaoImpl { //UserDaoImpl实体类
}
③:创建Spring配置文件
<?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">
<!--
Spring配置文件模板
文件名称:自定义 => 建议Spring.xml
创建流程:新建 XML配置文件 => Spring配置
-->
<bean id="userBean" class="com.powernode.spring6.bean.User"/>
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDaoImpl"/>
<!--
<bean id="" class=""/>
id:bean的身份证号 => 不能重复的唯一标识
class:全限定名 => 类的全路径、带包的类名
-->
</beans>
④:创建log配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--log4j2日志配置文件-->
<configuration>
<loggers>
<!--
Level指定日志级别,从低到高的优先级:
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
-->
<root level="DEBUG">
<appender-ref ref="spring6log"/>
</root>
</loggers>
<appenders>
<!--输出日志信息到控制台-->
<console name="spring6log" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
</console>
</appenders>
</configuration>
⑤:创建测试类
package com.powernode.spring6.test;
import com.powernode.spring6.bean.User;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Spring实例化对象原理:默认情况下Spring通过反射机制、调用类的无参构造方法实例化对象
* Code实现如下:
* Class clazz = Class.forName("com.powernode.spring6.bean.User");
* Object obj = clazz.newInstance();
* Spring底层IoC实现原理:XML解析 + 工厂模式 + 反射机制
*/
public class UserTest {
@Test
public void testUserCode(){
/* ①:获取Spring容器对象
* ApplicationContext:Spring容器
* ClassPathXmlApplicationContext:从类路径加载Spring配置文件
* 配置文件加载方式:支持1-多个
* Code说明:启动Spring容器,解析Spring.xml文件,并实例化所有bean对象,放到Spring容器中 */
// 加载单个xml配置文件
ApplicationContext applicationContext1 = new ClassPathXmlApplicationContext("Spring.xml");
// 加载多个xml配置文件
// ApplicationContext applicationContext2 = new ClassPathXmlApplicationContext("demo1.xml","demo2.xml");
// 加载自定义文件夹下的xml配置文件
// ApplicationContext applicationContext3 = new ClassPathXmlApplicationContext("xml/Spring.xml");
/* log4j2记录日志基本使用:
* ①:获取指定记录对象 => 指定要记录的类 */
Logger logger = LoggerFactory.getLogger(UserTest.class);
// ②:设置日志 => 根据log4j2.xml设置的日志级别输出日志
logger.info("我是一条消息");
logger.debug("我是调试信息");
logger.error("我是错误信息");
/* ②:根据bean的id从Spring容器中获取这个对象
* getBean("userBean",User.class):①、指定Bean id ②、指定返回类型 */
// 指定返回对象类型
User userBean = applicationContext1.getBean("userBean", User.class);
// 未指定返回对象类型
Object userDaoBean = applicationContext1.getBean("userDaoBean");
System.out.println(userBean);
System.out.println(userDaoBean);
}
}
IoC容器
具有依赖注入功能的容器,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
SET方法注入 - 常用
①:配置 UserDao & UserService 类
public class UserDao {
}
public class UserService {
// 将UserDao对象通过set方法注入到UserService的userDao属性中
private UserDao userDao;
/* set注入底层原理:
* 1、通过反射机制调用属性对应的set方法给属性赋值
* 2、要求:必须提供属性的set方法 */
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
②:配置Spring Bean
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<bean id="UserService" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
<!--
set注入底层原理:
1、通过反射机制调用属性对应的set方法给属性赋值
2、要求:必须提供属性的set方法
property:属性注入标签 => 属性赋值
name:属性名 => userDao
ref:指定要注入对象的bean ID
-->
</bean>
构造方法注入 - 不常用
①:配置 UserDao & CustomerService 类
public class CustomerService {
// 将UserDao对象通过构造方法注入到CustomerService 的userDao属性中
private UserDao userDao;
// 通过构造方法注入
public CustomerService(UserDao userDao) {
this.userDao = userDao;
}
}
②:配置Spring Bean
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<bean id="CustomerService" class="com.powernode.spring6.service.CustomerService">
<constructor-arg name="userDao" ref="userDaoBean"/>
<!--
构造方法注入底层原理:
1、通过反射机制调用类的构造方法
2、要求:必须提供属性的set方法
constructor-arg:通过构造器注入
name:根据属性名进行构造方法注入
index:根据指定参数下标注入 0、1、2... 以此类推
ref:指定引用类型的bean ID
-->
</bean>
SET方法注入 - 外部Bean (推荐) & 内部Bean
<!--外部定义的Bean-->
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"></bean>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<!--
外部Bean:将外部定义的Bean引入到该Bean中,即为外部Bean
特点:外部Bean可以被多个对象引用
-->
<property name="orderDao" ref="orderDaoBean"/>
</bean>
<bean id="orderServiceBean2" class="com.powernode.spring6.service.OrderService">
<property name="orderDao">
<!--
内部Bean:在Bean中嵌套Bean,即为内部Bean
特点:仅被某对象的某属性引用
-->
<bean class="com.powernode.spring6.dao.OrderDao"></bean>
</property>
SET方法注入 - 简单类型
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!--
简单类型注入:Spring提供bean(数据库连接池) - 其他情况很少使用简单类型baen
name :属性名
value:属性值
-->
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/mydb</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>masterkaoli</value>
</property>
</bean>
SET方法注入 - 级联属性
①:方式一
<bean name="emp" class="com.powernode.spring6.bean.Emp">
<property name="EName" value="lucy"></property>
<property name="gender" value="male"></property>
<property name="dept" ref="dept"></property>
<!--
使用级联属性赋值:
注意:
①:配置顺序 -> 1.先引入bean -> 2.在赋值
②:被ref引入的类,必须提供get方法
-->
<property name="dept.dName" value="安保"></property>
</bean>
<bean name="dept" class="com.powernode.spring6.Dept"></bean>
②:方式二
<bean id="emp" class="com.powernode.spring6.bean.Emp">
<property name="ename" value="zsp"></property>
<property name="gender" value="男"></property>
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" class="com.powernode.spring6.bean.Dept">
<!--仅赋值方式不同、性质相同-->
<property name="dname" value="安保部"></property>
</bean>
SET方法注入 - 数组类型
①:User
public class User {
// 配置数组属性
private String []username;
// 通过set注入 - 数组属性
public void setUsername(String[] username) {
this.username = username;
}
}
②:配置Spring Bean
<bean id="user" class="com.shw.User">
<property name="username">
<array>
<value>张三</value>
<value>李四</value>
<value>王五</value>
</array>
</property>
</bean>
③:测试方法
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("Spring.xml");
User user=(User) context.getBean("user");
// test
String []names=user.getUsername();
for (String str : names) {
System.out.println(str);
}
}
SET方法注入 - list & set集合
<bean id="personBean" class="com.powernode.spring6.bean.Person">
<property name="listName">
<!--
List集合 => 有序可重复
类型:
简单:value
引用:ref
-->
<list>
<value>张三</value>
<value>李四</value>
<value>王五i</value>
</list>
</property>
<property name="setName">
<!--
set集合 => 无序不可重复
类型:
简单:value
引用:ref
-->
<set>
<value>张三</value>
<value>李四</value>
<value>王五</value>
</set>
</property>
</bean>
SET方法注入 - map集合
<bean id="personBean" class="com.powernode.spring6.bean.Person">
<property name="mapName">
<map>
<!--
map集合 => 键值对
类型:
简单:<entry key="" value=""/>
引用:<entry key-ref="" value-ref=""/>
-->
<entry key="1" value="110"/>
<entry key="2" value="120"/>
<entry key="3" value="119"/>
</map>
</property>
</bean>
SET方法注入 - null & 空字符串
<bean id="catBean" class="com.powernode.spring6.bean.Cat">
<!--
不注入 => 属性的默认值就是null
value="" => 注入空字符串
异常:
①:调用null属性报空指针异常
②:调用空字符串属性,不会报异常
-->
<!--<property name="name" value="tom"/>-->
<property name="name" value=""/>
<property name="age" value="30"/>
</bean>
SET方法注入 - 特殊符号
<bean id="mathBean" class="com.powernode.spring6.bean.MathBean">
<!--
注入特殊符号:
①:使用实体符号(<)代替特殊符号
②:使用<![CDATA[]]>,[]中内容当做字符串解析
③:<![CDATA[ ]]> => 仅支持<value>标签,不支持value属性
-->
<!--<property name="isDemo1" value="2 < 3" />-->
<property name="isDemo2">
<value><![CDATA[2 < 3]]></value>
</property>
</bean>
SET方法注入 - P命名空间 => 简化set方法注入机制
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
p命名空间注入 => 简化set方法注入机制
使用流程:
①:在spring的配置文件头部添加p命名空间:xmlns:p="http://www.sprinqframework.orq/schema/p"
②:使用p命名空间(p:属性名 = "属性值")
类型:
简单:p:name="小花"
引用:p:birth-ref="bean ID"
-->
<bean id="dogBean" class="com.powernode.spring6.bean.Dog" p:name="小花" p:age="30" p:birth-ref="birthBean"/>
<!-- java.util.Date => 获取系统当前时间 -->
<bean id="birthBean" class="java.util.Date"/>
</beans>
构造方法注入 - C命名空间 => 简化构造方法注入机制
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
C命名空间 => 简化构造方法注入机制
①:在spring的配置文件头部添加:xmlns:c="http://www.springframework.org/schema/c"
②:使用索引方式 c:_1 = "jack" => (_1 = 形参的索引)
③:使用形参方式 c:name="jack" => (c:属性名="属性值")
类型:
简单:c:name="小花"
引用:c:birth-ref="bean ID"
-->
<!--<bean id="peopleBean" class="com.powernode.spring6.bean.People" c:_0="zhangsan" c:_1="30" c:_2="true"></bean>-->
<bean id="peopleBean" class="com.powernode.spring6.bean.People" c:name="jack" c:age="30" c:obj-ref="true"></bean>
</beans>
构造方法注入 - util命名空间 => 快速定义集合(复用)
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--
util命名空间 => 快速定义集合(复用集合)
①:在spring的配置文件头部添加:
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
②:使用util:集合标签 [快速定义集合] => util:properties/map/list/set...
③:将util集合注入其他Bean(复用)
-->
<!--1.快速定义集合-->
<util:properties id="propDemo">
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
<prop key="username">root</prop>
<prop key="password">lirui1991</prop>
</util:properties>
<!--数据源1(复用集合)-->
<bean id="ds1" class="com.powernode.spring6.jdbc.MyDataSource1">
<property name="properties" ref="propDemo"/>
</bean>
<!--数据源2(复用集合)-->
<bean id="ds2" class="com.powernode.spring6.jdbc.MyDataSource2">
<property name="properties" ref="propDemo"/>
</bean>
</beans>
SET方法注入 - 根据属性名(byName)自动装配 (不推荐)
<!--
根据属性名称自动装配 => byName
①:主bean的属性中包含 spellChecker类型的成员属性
②:主bean的spellChecker类型的成员属性含有set方法
③:byName自动装配底层原理:
Spring IoC容器会在配置文件中查找id/name属性为spellChecker的bean,然后使用Seter方法为其注入。
-->
<bean id="orderService" class="com.powernode.spring6.service.OrderService" autowire="byName"></bean>
<bean id="orderDao" class="com.powernode.spring6.dao.OrderDao"/>
SET方法注入 - 根据属性类型(byType)自动装配 (不推荐)
<!--
根据属性名称自动装配 => byType
①:主bean的属性中包含 userDao类型的成员属性
②:userDao类型bean在指定加载的spring容器中唯一,否则报异常
③:主bean的userDao类型的成员属性含有set方法
④:byType自动装配底层原理:
Spring IoC容器会在配置文件中查找类型为userDao的bean,然后使用Seter方法为其注入。
-->
<bean id="userDao" class="com.powernode.spring6.dao.UserDao"></bean>
<bean id="customerService" class="com.powernode.spring6.service.CustomerService" autowire="byType"></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"
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 http://www.springframework.org/schema/context/spring-context.xsd">
<!--
引入外部的properties配置文件
①:引入context命名空间:
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
②:使用标签context:property-placeholder的ocation属性来指定属性配置文件的路径。
③:Location属性 => 默认从类的根路径下开始加载资源。
-->
<context:property-placeholder location="jdbc.properties"/>
<!--
将jdbc.properties配置文件的数据读取到bean中
①:外部配置文件 jdbc.properties
②:Spring默认先从Window环境变量读取数据 => 防止重名,配置文件属性建议添加前缀(jdbc.)
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring6
jdbc.username=root
jdbc.password=123
-->
<bean id="ds" class="com.powernode.spring6.jdbc.MyDataSource">
<property name="driver" value="${jdbc.driverClass}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
</beans>
Bean作用域
Bean作用域 - 单例 & 多例
<!--
bean作用域单例和多例
单例:
①:bean的scope属性 = singleton
②:加载Spring配置文件时,初始化实例对象
③:省略bean的scope属性,默认为singleton
多例:
①:bean的scope属性 = prototype
②:在调用getBean()方法时,初始化实例对象
-->
<bean id="sb" class="com.powernode.spring6.bean.SpringBean" scope="singleton"></bean>
Bean作用域 - scope其他属性
<!--
bean作用域 - Scope其他属性
属性值说明:
·singleton:默认,单例。
·prototype:每次调getBean()方法,则获取一个新的Bean对象
·request:一个请求对应一个Bean。(仅用于web)
·session:一个会话对应一个Bean。(仅用于web)
·global sessionl:portlet应用中专用的...
·application:一个应用对应一个Bean。(仅用于web)
·websocket:一个websocket生命周期对应一个Bean。(仅用于web)
·自定义scope:很少使用。
自定义scope:
①:配置自定义作用域
②:使用自定义作用域(其他bean中引入scope的自定义作用域类型即可)
-->
<!--①:配置自定义的作用域 (bean&property标签固定写法)-->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<!--entry key="threadScope" 配置自定义作用域的名称-->
<entry key="threadScope">
<!--class是Spring框架内置的多线程类型-->
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
<!--②:使用自定义的作用域-->
<bean id="sh" class="com.powernode.spring6.bean.SpringBean" scope="threadScope"></bean>
Bean的实例化方式
Bean的实例化方式 - 构造方法 & 工厂静态方法
// 产品类
public class Star {
public Star() {
System.out.println("Star无参构造...");
}
}
// 工厂类
public class StarFactory {
// 生产方法
public static Star get(){
return new Star();
}
}
<!--
Bean的获取方式 => 实例化
1.通过无参构造方法实例化
2.通过工厂静态方法实例化
3.通过工厂非静态方法实例化
4.通过FactoryBean接口实例化
-->
<!--
①:通过无参构造方法实例化:
①:bean的class属性中引入类的全类名(包名+类名)
②:Spring底层自动调用该类的无参构造实例化bean对象
-->
<bean id="starId" class="com.powernode.spring6.bean.Star"/>
<!--
②:通过简单工厂实例化:
①:创建Star产品类
②:创建Star工厂类StarFactory
③:配置StarFactory工厂类的生产方法(public static Star get(return new Star();))
④:配置starBean => starBean指向工厂类的生产方法factory-method="get"
注意:工厂类的生产方法是静态方法
-->
<bean id="starBean" class="com.powernode.spring6.bean.StarFactory" factory-method="get"/>
Bean的实例化方式 - 工厂非静态方法 & 实现FactoryBean接口
// 产品类 => 通过工厂非静态方法实例化
public class Gun {
public Gun() {
System.out.println("Gun的无参构造方法执行...");
}
}
// 工厂类
public class GunFactory {
//生产方法
public Gun get(){
return new Gun();
}
}
<!--
通过工厂非静态方法实例化
①:配置产品类Gun
②:配置工厂类GunFactory
③:配置工厂类的生产方法 public Gun get(){return new Gun();
④:配置工厂类gunFactory的Bean
⑤:配置产品类Gun的Bean => 指向工厂类的Bean的生产方法get()
factory-bean => 指向工厂类的Bean
factory-method => 指向工厂类的方法
-->
<bean id="gunFactory" class="com.powernode.spring6.bean.GunFactory"/>
<bean id="gun" factory-bean="gunFactory" factory-method="get"/>
<!--
通过FactoryBean接口实例化 => 简化2、3实例化操作
①:配置产品类产品类Person
②:配置工厂类PersonFactory => 且实现FactoryBean(工厂Bean)
③:重写工厂Bean的生产方法 getObject(){return new Person();} => 自定义返回实例化对象
④:可选 => 重写工厂Bean的单例/多例模式方法 isSingleton(){return true/false} => 默认false单例/true多例
⑤:配置产品类Person的Bean => class指向 -> 工厂类PersonFactory即可
-->
<bean id="person" class="com.powernode.spring6.bean.PersonFactory"/>
// 产品类 => 通过实现FactoryBean实例化
public class Person {
public Person() {
}
}
/**
* 工厂类 => 实现FactoryBean
* FactoryBean:该接口为工厂Bean => 用于创建普通Bean
* getObject():该方法为生产Bean的方法 => 返回自定义实例化对象 即生产该对象
* isSingleton():用于配置单例 & 多例多模式
*/
public class PersonFactory implements FactoryBean<Person> {
// 此方法为 生产方法
@Override
public Person getObject() throws Exception {
// 自定义 产品实例化对象类型
return new Person();
}
@Override
public Class<?> getObjectType() {
return null;
}
// (不用配置、Spring.xml中有默认实现)
// 返回true表示单例模式 / 返回false表示多例模式
@Override
public boolean isSingleton() {
return true;
}
}
New对象 -> 转换为Bean
@Test
public void testRegisterBean() {
// ①:自己new的对象
Student student = new Student();
System.out.println(student);
// ②:将自己new的对象 -> 注册为Bean
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerSingleton("studentBean", student);
// ③:从spring容器中获取Bean
Student studentBean = (Student)factory.getBean("studentBean");
System.out.println(studentBean);
}
Bean的生命周期
Bean生命周期 - 常规5步
注意:
- 单例:Spring容器进行完整的生命周期管理。
- 多例:Spring容器不负责销毁
/**
* Bean的生命周期五步:
* 第一步:实例化Bean
* 第二步:Bean属性赋值
* 第三步:初始化Bean
* 第四步:使用Bean
* 第五步:销毁Bean
*/
// 产品类
public class User {
private String name;
// 通过无参构造创建Bean
public User() {
System.out.println("①:无参构造方法执行...");
}
// 通过set方法注入属性
public void setName(String name) {
this.name = name;
System.out.println("②:set方法执行...");
}
// 对Bean进行初始化操作
public void initBean() {
System.out.println("③:初始化Bean...");
}
// ④:使用Bean => 此处为表现Bean的生命周期、非配置流程
// close销毁该Bean
public void destroyBean() {
System.out.println("⑤:销毁Bean...");
}
}
<!--
Bean生命周期:
init-method => 配置Baen初始化方法
destroy-method => 配置Bean销毁方法
②:set方法执行 => 需要注入Bean属性、否则缺失此步
-->
<bean id="user" class="com.powernode.spring6.bean.User" init-method="initBean" destroy-method="destroyBean">
<property name="name" value="lirui"/>
</bean>
// 测试类
public class BeanTest {
@Test
public void testBean(){
// 获取Ioc容器 => getBean
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println("④:使用Bean:"+user);
// ApplicationContext 没有close方法 => close方法在 ClassPathXmlApplicationContext 中
ClassPathXmlApplicationContext con = (ClassPathXmlApplicationContext) applicationContext;
// 销毁Bean
con.close();
}
}
Bean生命周期 - 后处理器 - 7步
/**
Bean生命周期七步:比五步添加的那两步在哪里?在初始化Bean的前和后。
第一步:实例化Bean
第二步:Bean属性赋值
第三步:执行"Bean后处理器”的before方法。
第四步:初始化Bean
第五步:执行"Bean后处理器"的after方法。
第六步:使用Bean
第七步:销毁Bean
// Bean后处理器需要手动配置
①:创建处理器实体类 实现 BeanPostProcessor 接口
重写:Before & After 方法
②:Spring.xml配置文件中配置该Bean
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>
*/
// 配置Bean后处理器 => 实现BeanPostProcessor
public class LogBeanPostProcessor implements BeanPostProcessor {
// Before方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行Bean后处理器的Before方法...");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
// After方法
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行Bean后处理器的After方法...");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
<!--
配置Bean后处理器 => 配置后即生效
注意:"Bean后处理器"将作用于当前配置文件中所有的Bean
-->
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>
<!--
Bean生命周期:
init-method => 配置Baen初始化方法
destroy-method => 配置Bean销毁方法
②:set方法执行 => 需要注入Bean属性、否则缺失此步
-->
<bean id="user" class="com.powernode.spring6.bean.User" init-method="initBean" destroy-method="destroyBean">
<property name="name" value="lirui"/>
</bean>
Bean生命周期 - 十步
- "产品类"实现对应接口 => 重写方法即可 自动调用
/**
Bean生命周期十步:比七步添加的那三步在哪里?
第一步:实例化Bean
第二步:Bean属性赋值
点位1:检查Bean是否实现了Aware相关的接口
第三步:执行"Bean后处理器”的before方法。
点位2:检查Bean是否实现了InitializingBean接口
第四步:初始化Bean
第五步:执行"Bean后处理器"的after方法。
第六步:使用Bean
点位3:检查Bean是否实现了DisposableBean接口
第七步:销毁Bean
添加的这三个点位的特点:都是在检查你这个Bean是否实现了某些特定的接口,如果实现了这些接口,则Spring容器会调用这个接口中的方法。
*/
Bean的循环依赖
- 循环依赖说明:
- 两个类互相引用为属性,A中有B、B中有A
SET注入情况
- 仅支持SET注入的情况 => 不支持构造方法注入的情况
<!--
支持情况:
SET + 两个Bean都为单例模式 / 其中一个Bean为单例模式
不支持情况:
SET + 两个Bean都为多例模式 => 报异常(多例在getBean时才会实例化)
-->
<bean id="husbandBean" class="com.powernode.spring6.bean.Husband" scope="singleton">
<property name="name" value="张三"/>
<property name="wife" ref="wifeBean"/>
</bean>
<bean id="wifeBean" class="com.powernode.spring6.bean.Wife" scope="singleton">
<property name="name" value="小花"/>
<property name="husband" ref="husbandBean"/>
</bean>
注解式IoC
注解入门程序
①:确认Maven(pom.xml)配置Spring-context核心依赖
<!--Spring-context 基础-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
②:配置context命名空间
<?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 http://www.springframework.org/schema/context/spring-context.xsd">
③:配置Spring框架扫描哪些包中的类
- 多个包,之间用逗号分割
- <context:component-scan base-package=“com.powernode.spring6.bean,
com.powernode.spring6.bean”"/>- 也可以加载多个包的父包(但会影响扫描速度)
- <context:component-scan base-package=“com.powernode.spring6”/>
<context:component-scan base-package="com.powernode.spring6.bean"/>
④:在类上使用注解
- 以下注解底层基于@Component,为增强MVC阅读性
- @Controller -> 控制器
- @Service -> 业务
- @Repository -> DAO
/**
* @Component -> 组件:仅用于类,可以被反射机制读取
* @Component(value="userBean")
* 作用等同于:<bean id="userBean" class="com.powernode.spring6.bean.Order"></bean>
*
* ( )中配置可以省略,省略后Bean名称为类名首字母小写user
* @Component
* 作用等同于:<bean id="user" class="com.powernode.spring6.bean.Order"></bean>
*/
@Component
public class User {
}
⑤:测试:获取Bean
/**
* 声明Bean的注解 => Spring注解基于Spring-context核心依赖
* @Component -> 组件:①:仅用于类,可以被反射机制读取
*
* - 以下三个注解底层基于@Component,为增强MVC阅读性
* @Controller -> 控制器
* @Service -> 业务
* @Repository -> DAO
*/
public class UserTest {
@Test
public void testBean(){
// 获取Bean
ApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml");
User user = context.getBean("user", User.class);
System.out.println(user);
}
}