目录
1. 创建config包,创建Configuration配置类
本文章内容都是跟着黑马的学习笔记~大家有需要也可以直接去看黑马在B站发布的SSM视频
一、基于XML配置方式组件管理
配置之前:
不考虑反射,Java中有四种实例化方式。通过构造函数实例化有“无参构造函数”和“有参构造函数”两种方式。通过工厂模式实例化有“静态工厂”和“非静态工厂”两种方式,共四种。不同的对象实例化方式IoC的配置方法不同。
因有参构造实例化涉及了DI(依赖注入),故先不考虑有参构造的配置方式。
开始配置!
为了防止重复导入相关依赖,首先创建一个父工程,用于导入所需依赖。其余操作再创建子工程进行。
1. 创建新的maven工程
工程创建完成后,修改pom文件。
随后删去父工程的src文件夹
2. 父工程导入所需基础依赖
刷新maven工程即可导入相关依赖
3. 父工程目录下创建相关子工程
因为子工程继承了父工程的配置文件,所以子工程无需重复引入依赖~
1. 基于无参构造函数的组件信息声明配置
1.1 准备组件类
com.目录下创建一个包:ioc_01
包下创建组件类:SadComponent
package com.landy.ioc_01;/**
* ClassName : SadComponent
* Package:com.landy.ioc_01
* Description :
* Author : LandyLee
* Create : 2024/5/5 - 22:35
* Version : v1.0
*
*/public class SadComponent {
// 默认包含无参构造函数
public void doWork(){
System.out.println("SadComponent.doWork");
}
}
1.2 创建相关配置文件
resources文件夹下创建Spring配置文件:New -> XML Configuration File -> Spring Config 即可直接创建含有Spring配置信息的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
1.3 在XML文件中声明相关的bean标签
<!-- 1. 使用无参构造函数实例化的组件 -->
<!-- bean标签:一个组件对象 id:组件的唯一标识 class:组件的类全限定名 -->
<!-- 将同一组件类声明两个组件信息,回市里两个组件对象 -->
<bean id="sadComponent" class="com.landy.ioc_01.SadComponent"/>
2. 基于静态工厂方法的的组件信息声明配置
2.1 创建相关组件类
package com.landy.ioc_01;
/**
* ClassName : ClientService
* Package:com.landy.ioc_01
* Description :
* Author : LandyLee
* Create : 2024/5/5 - 22:51
* Version : v1.0
*/
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
2.2 在XML文件中声明相关的bean标签
factory-method一定要绑定静态方法!
<!-- 2. 静态工厂类进行IoC配置 -->
<!-- id:组件的唯一标识 class:组件的类全限定名 factory-method:静态工厂方法名 -->
<bean id="clientService" class="com.landy.ioc_01.ClientService" factory-method="createInstance"></bean>
3. 基于实例工厂方法的XML的组件信息声明配置
3.1 创建相关组件类
package com.landy.ioc_01;
public class ClientServiceImplclientService {
}
package com.landy.ioc_01;
public class DefaultServiceLocator {
private static ClientServiceImplclientService clientService = new ClientServiceImplclientService ();
public ClientServiceImplclientService createInstance() {
return clientService;
}
}
3.2 在XML文件中声明相关的bean标签
<!-- 3. 实例工厂进行IoC配置 -->
<!-- 配置工厂类的组件信息:相当于先把工厂实例创建出来 -->
<bean id="defaultServiceLocator" class="com.landy.ioc_01.DefaultServiceLocator" ></bean>
<!-- 通过指定非静态工厂对象和方法名来生产IoC信息 -->
<bean id="clientServiceImplclientService" factory-bean="defaultServiceLocator" factory-method="createInstance"></bean>
4. 基于单个构造参数的组件依赖注入配置
4.1 准备组件类
public class UserDao {
}
public class UserService {
public UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
4.2 编写配置文件
<!-- 引用和被应用的组件必须在IoC容器中存在,否则会报错。 -->
<!-- 1. 单个构造参数注入 -->
<bean id="userDao" class="com.landy.ioc_02.UserDao " />
<bean id="userService" class="com.landy.ioc_02.UserService">
<!-- 构造参数传值,DI的配置 value:直接属性值 ref:引用其他组件,输入组件id值 -->
<constructor-arg ref="userDao" />
</bean>
5. 基于多个构造参数的组件依赖注入配置
5.1 准备组件类
public class UserService {
public UserDao userDao;
private int age;
private String name;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public UserService(UserDao userDao, int age, String name) {
this.userDao = userDao;
this.age = age;
this.name = name;
}
}
5.2 编写配置文件
三种方式,推荐使用第二种
<!-- Spring IoC是一个高级容器,内部有缓存。 1, 先创建对象(IoC)2. 再进行属性赋值(DI) -->
<!-- 2. 多个构造参数注入 -->
<bean id="userDao2" class="com.landy.ioc_02.UserDao " />
<bean id="userService2" class="com.landy.ioc_02.UserService">
<!-- 按构造参数顺序填写值 value:直接赋值 ref:引用其他组件,值为组件id -->
<constructor-arg ref="userDao2"></constructor-arg>
<constructor-arg value="18"></constructor-arg>
<constructor-arg value="帅哥李"></constructor-arg>
</bean>
<bean id="userService2" class="com.landy.ioc_02.UserService">
<!-- 按构造参数名称填写值,不考虑顺序 填写name = "构造参数名称" 即可 -->
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="name" value="帅哥李"></constructor-arg>
<constructor-arg name="userDao" ref="userDao2"></constructor-arg>
</bean>
<bean id="userService2" class="com.landy.ioc_02.UserService">
<!-- 按构造参数Index填写值,从左到右,从0开始 -->
<constructor-arg index="1" value="18"></constructor-arg>
<constructor-arg index="2" value="帅哥李"></constructor-arg>
<constructor-arg index="0" ref="userDao2"></constructor-arg>
</bean>
6. 基于Setter方法的组件依赖注入配置
6.1 准备组件类
public class MovieFinder {
}
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String movieName;
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
public void setMovieName(String movieName) {
this.movieName = movieName;
}
}
6.2 编写配置文件
<!-- 除法Setter方法进行注入 -->
<!-- 需要注入的引用组件 -->
<bean id="movieFinder" class="com.landy.ioc_02.MovieFinder" />
<bean id="simpleMovieLister" class="com.landy.ioc_02.SimpleMovieLister">
<!-- name:setter方法的去set和首字母小写值,调用set方法 -->
<!-- ref:其他组件的id | value:直接属性值 -->
<property name="movieName" value="复仇者联盟"/>
<property name="movieFinder" ref="movieFinder"/>
</bean>
7. IoC和DI配置完成后!创建IoC容器并使用
声明IoC容器对象,读取配置文件,使配置文件中声明的组件类信息真正的进行实例化成Bean对象和形成Bean之间的引用关系。实例化组件Bean和引用关系的维护都是再IoC容器中实现的。
7.1. 创建IoC容器
/**
* 如何创建IoC容器并读取配置文件
*/
public void creatIoc(){
// 创建IoC容器
// 创建合适的容器实现即可
/*
接口:
BeanFactory
ApplicationContext
实现类:
ClassPathXmlApplicationContext:读取类路径下的XML的配置方式 classes
FileSystemXmlApplicationContext:读取指定文件位置的XML配置
AnnotationConfigApplicationContext:读取配置类的方式的IoC容器
WebApplicationContext
*/
// 方式1:直接创建容器并指定配置文件路径
// String... 配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring_03.xml");
// 方式2:先创建IoC容器对象,再指定配置文件路径,再刷新
// 创建容器和指定路径分开
ClassPathXmlApplicationContext applicationContext1 = new ClassPathXmlApplicationContext();
applicationContext1.setConfigLocation("spring_03.xml");
applicationContext1.refresh(); // 调用IoC和DI
}
7.2 从IoC容器中获取组件实例
/**
* 如何再IoC容器中获取组件Bean
*/
public void getBeanFromIoC(){
// 1. 创建IoC容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring_03.xml");
// 2. 读取IoC容器的组件
// 方法1:根据BeanId获取:需要强转,不推荐
SadComponent sadComponent = (SadComponent) applicationContext.getBean("sadComponent");
// 方法2:根据BeanId,同时指定Bean类型 Class
SadComponent sadComponent1 = applicationContext.getBean("sadComponent", SadComponent.class);
// 方法3:直接根据类型获取,如果IoC容器存在多个通Class的Bean,会出现不唯一异常
// IoC的配置一定是实现类,但是可以根据接口获取值!底层使用instanceof进行判断
SadComponent sadComponent2 = applicationContext.getBean(SadComponent.class);
}
组件Bean:作用域和周期方法配置
周期方法:
到了对应时间节点,主动被调用的方法。如Servlet的init初始化、destroy销毁等周期方法,被Tomcat调用。
组件类的周期方法:
我们可以在组件类中定义方法,然后党IoC容器实例化和销毁组件对象的时候进行调用。
周期方法的声明:
周期方法必须是Public、void、无参的。在类中声明即可
public class JavaBean {
/**
* 必须是Public,必须void,必须是无参数的
* 初始化方法 -> 初始化业务逻辑
*/
public void init() {
System.out.println("JavaBean init");
}
/**
* 必须是Public,必须void,必须是无参数的
* 销毁方法
*/
public void destroy() {
System.out.println("JavaBean destroy");
}
}
配置文件中使用init-method和destory-method属性声明周期方法
<!-- init-method = "初始化方法名" destory-method = "销毁方法名" -->
<bean id="javaBean" class="com.landy.ioc_04.JavaBean" init-method="init" destroy-method="destroy" />
作用域:
IoC容器根据<bean/>标签的信息将组建转化为Spring内部的BeanDefinition对象,BeanDefinition对象内包含了标签的定义信息,如id,class等。SpringIoC容器可以根据BeanDefinition对象反射创建多个Bean对象实例,具体创建多少个Bean的实例对象,有Bean组件的作用域Scope属性指定。
作用域可选值:
singleton:IoC容器中的Bean对象始终为单实例。IoC容器初始化时创建对象。是Bean对象的默认值
prototype:IoC容器中的Bean对象有多个实例。IoC获取Bean时创建对象。
配置文件:
<!-- 单例模式 -->
<bean id="javaBean2" class="com.landy.ioc_04.JavaBean2" scope="singleton" />
<!-- 多例模式 -->
<bean id="javaBean22" class="com.landy.ioc_04.JavaBean2" scope="prototype" />
FactoryBean接口:简化工厂模式的Bean组件配置
工厂类实现FactoryBean即可在配置文件中省略facotry-bean/factory-method等信息的配置
创建待实例的类
public class JavaBean3 {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
创建实例的工厂类:
// 1. 实现FactoryBean接口,指定返回值泛型
public class JavaBeanFactory implements FactoryBean<JavaBean3> {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public JavaBean3 getObject() throws Exception {
// 使用自己的方式实例化对象
JavaBean3 javaBean3 = new JavaBean3();
javaBean3.setName(name);
return javaBean3;
}
@Override
public Class<?> getObjectType() {
return JavaBean3.class;
}
}
配置文件:
<!-- id="getObject返回的对象标识" class="FactoryBean的标准化工厂类" 工厂对象本身的id为 & + FactoryBean的id-->
<bean id="javaBean3" class="com.landy.ioc_05.JavaBeanFactory">
<!-- 工厂类内部赋值,是给工厂对象赋值,不是工厂模式实例化的对象的属性赋值 -->
<!-- 要想给工厂模式实例化的对象赋值,需要给工厂模式实例化的对象添加响应属性,先给工厂的属性赋值,再给工厂生产的对象赋值 -->
<property name="name" value="landy"/>
</bean>
上述便是XML配置IoC容器的基本内容~
二、基于注解配置方式组件管理
创建bean标签 -> 添加相关注解
常见注解:
@Component:用于描述Spring中的Bean,可用在任何层次(Service、Dao)。使用时只需将该注解标注在响应类上即可
@Repository:用于将数据访问层(Dao)层的类表示为Spring中的Bean,功能与@Component相同
@Service:用于将业务层(Service)层的类表示为Spring中的Bean,功能与@Component相同
@Controller:用于将控制层(如SpringMVC中的Controller),用于将控制层的类表示为Spring中的Bean,功能与@Component相同。
通过源码可知:四个注解没有本质区别,@Service、@Repository、@Controller三个注解只是在@Component注解的基础上起了三个新的名字,用于区分不同组件。
配置扫描包:
当注解注释完成后,需要配置扫描包。
<!-- 1. 普通配置包扫描 -->
<!-- base-package:指定IoC容器去哪些包下查找注解类,可指定多个包,用逗号,分隔 -->
<context:component-scan base-package="com.landy.ioc_01"/>
<!-- 排除指定组件 -->
<!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解排除 -->
<!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 -->
<context:component-scan base-package="com.landy.ioc_01">
<!-- 排除Controller注解 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 指定包含注解 -->
<!-- use-default-filters="false":表示不包含默认的过滤规则,默认为true -->
<context:component-scan base-package="com.landy.ioc_01" use-default-filters="false">
<!-- 只包含Controller注解 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
IoC注解组件的默认Id为类首字母小写名,也可以声明value属性以重命名:@Component("renameName")
组件Bean:作用域和周期方法注解
周期方法声明:
周期方法要求:方法命名无要求,但是必须时Public权限修饰符、void无返回值且无形参
@PostConstruct:注解指定初始化方法
@PreDestroy:注解指定销毁方法
作用域配置:
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON) 单例,默认
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) 多例
Bean组件中的引用类型属性赋值:
前提:参与自动装配的组件(需要装配、被装配)必须都在IoC容器中
注解:@Autowired:在成员变量直接添加此注解即可
示例如下:
需要注入的Service类:
@Service
public class UserServiceImpl implements UserService{
@Override
public void show() {
System.out.println("I like to show");
}
}
Controller中使用注解注入:
@Controller
public class UserController {
// 自动装配注解:DI
// 1. IoC容器中查找复合类型的组件对象
// 2. 赋值给当前属性
@Autowired
private UserService userService;
}
@Autowired注解实现原理:
1. 根据所需组件类型到IoC容器中查找
2. 找到唯一的Bean -> 直接装配
3. 找不到匹配类型的Bean -> 装配失败
4. 匹配类型的Bean不止一个(一个接口有多个实现) -> 查找@Qualifier(value="idName")注解,根据@Qualifier注解指定的名称作为Bean的id进行匹配 / 根据属性名寻找
@Resource(name = "idName") = @Autowired + @Qualifier @Resource注解能够简化@Autowired和@Qualifier双注解的麻烦。但是需要通过xml坐标导入相关依赖。
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
Bean组件中的基本类型属性赋值:
两种方法:直接赋值 / 注解引用外部文件
外部文件:application.properties
handsome.name = Landy
IoC中声明引用:
<context:component-scan base-package="com.landy.ioc_04"/>
<context:property-placeholder location="classpath:application.properties"/>
类中注入,可添加默认值:
@Component
public class JavaBean {
// 1. 直接赋值
// name = "Landy"
// 2. 注解赋值:可读取外部配置文件
// 冒号:后为默认值
@Value("${handsome.name:admin}")
private String name;
private String userName;
}
三、基于配置类方式组件管理
配置类->替代XML配置文件
1. 创建config包,创建Configuration配置类
/**
* java的配置类,代替XML配置文件
* 1. 包扫描注解配置
* 2. 引用外部配置文件
* 3. 声明第三方依赖的bean组件
* @Configuration:配置类注解
*
*/
@ComponentScan(value = "com.landy.ioc_01")
@PropertySource(value = "classpath:application.properties")
@Configuration
public class JavaConfiguration {
}
@Configuration:包扫描注解(value="待扫描包名",...)
@PropertySource:引入外部配置文件("classpath:配置文件名")
@Configuration:配置类注解
2. 创建配置类的IoC容器:
// IoC容器实例化
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(JavaConfiguration.class);
3. 外部组件的声明
通过方法声明:
方法的返回值类型 == bean组件的类型或者接口和父类
方法的名字 = bean id
方法体自定义实现过程即可
方法上需要添加@Bean注解,使配置类的方法创建的组件存到IoC容器
@ComponentScan(value = "com.landy.ioc_01")
@PropertySource(value = "classpath:application.properties")
@Configuration
public class JavaConfiguration {
@Value("${landy.url}")
private String url;
@Value("${landy.driver}")
private String driver;
@Value("${landy.username}")
private String username;
@Value("${landy.password}")
private String password;
/**
* 方法的返回值类型 == bean组件的类型或者接口和父类
* 方法的名字 = bean id
* 方法体自定义实现过程即可
* 方法上需要添加@Bean注解,使配置类的方法创建的组件存到IoC容器
* @return dataSource 需要声明的组件
*/
@Bean
public DataSource dataSource(){
// 实现具体的实例化过程
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driver);
return dataSource;
}
}
如何修改Bean id?
@Bean(name="newName" / value = "newName")
Bean的周期方法如何指定?
1. @PostConstruct / @PreDestory注解
2. @Bean(initMethod="methodName", destoryMethod="methodName")
Bean的作用域?
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON/PROTOTYPE)
如何引用其他组件?如JdbcTemplate需要DataSource
1. 直接调用@Bean注释的方法(不推荐!)
@Bean
public JdbcTemplate jdbcTEmplate(){
return new JdbcTemplate(dataSource());
}
2. 形参列表声明需要的组件类型
@Bean
public JdbcTemplate jdbcTEmplate(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
以形参形式注入,必须有对应类型组件于IoC中,如果没有抛出异常!
如果有多个同类型组件,使用形参名称==对应Bean id即可
4. @Import拓展
将父配置类加载给子配置类,最后只需要加载一个配置类即可。
@Import(value = ConfigurationFatherA.class, ConfigurationFatherB.class)
以上便是Spring IoC容器管理配置的三种方式啦~