Spring学习笔记01-IOC

本文围绕Spring框架展开,介绍其是IOC容器,具备多种特性。阐述了Spring环境创建过程,详细讲解IOC容器原理、获取bean方式、FactoryBean与BeanFactory区别等。还介绍了组件注解、配置文件属性注入、集合属性管理,以及bean生命周期和后置处理器的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring

Spring简介

Spring是一个IOC 容器,可以帮助我们管理对象的创建获取使用销毁
且非侵入式(轻量级),具备 IOC 、 AOP 、 容器(对象工厂) 、 组件化 、 一站式

spring环境创建过程

  • Spring运行期间需要 使用日志包 打印日志
  • 借助Spring的 IOC容器的API ClassPathXmlApplicationContext;将配置文件解析为java中的 IOC容器对象
    • 类似Web项目 启动时服务器将项目对应的web.xml配置文件创建为ServletContext对象,可以管理 内部的Servlet

Spring IOC容器

IOC原理

对象由容器创建:控制反转

  • IOC: 如果需要使用bean的对象,首先将类的配置文件交给Spring IOC容器(配置文件)
  • 需要使用 直接通过容器获取对象即可(省略了 创建过程)

DI是依赖注入. IOC思想的具体实现 , 在配置文件中配置bean并注入属性值

IOC是一种反转控制的思想, DI是对IOC思想的具体实现.

IOC容器类的继承关系

类能够更好的使用方法和属性来调用使用

  • BeanFactory IOC容器的基本实现, 面向Spring框架本身
    • ApplicationContext BeanFactory的子接口, 提供了更多高级的特性,面向框架的使用者的.
      • ConfigurableApplicationContext 额外提供了 close refresh方法. 支持我们进行关闭,刷新的操作
        • ClassPathXmlApplicationContext 从类路径下读取xml文件并创建IOC容器对象
        • FileSystemXmlApplicationContext 从文件系统下读取xml文件并创建IOC容器对象
  • WebApplicationContext : 专门为web应用准备的.

获取bean的方式

User user = app.getBean(User.class);//根据类型从IOC容器中获取bean
User user = (User) app.getBean("user_xia");//User.class / 根据bean的  id属性值获取唯一的一个bean
  • 通过类型获取 ,必须保证该类型配置了唯一一个bean
  • 通过配置文件中的bean的 id属性值获取唯一的一个bean

Spring FactoryBean生产bean

通过FactoryBean接口来定义规范,spring容器直接判断并强转为FactoryBean接口调用相关方法

private String username;
	//用户通过SpringIOC容器调用工厂类 获取bean时 容器自动调用
	@Override
	public User getObject() throws Exception {
		return new User(9527, username, "123456", "xxx@12.com");
	}
	
	//获取Bean的类型
	@Override
	public Class<?> getObjectType() {
		return User.class;
	}
	
	//控制bean是否为单例模式
	@Override
	public boolean isSingleton() {
		return true;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

通过在配置文件暴露出来 可以动态设置值来让spring容器读取设置动态值

<bean class="com.hey.bean.UserFactoryBean" id="userFactoryBean">
		<property name="username" value="gaowei"></property>
</bean>
//从容器中获取bean时 ,factorybean返回的是getObject方法返回的那个bean
User user1 = ctx.getBean("userFactoryBean", User.class);
与BeanFactory的区别

BeanFactory:
IOC容器的顶级约束
FactoryBean:
用来管理bean对象的创建的

组件注解-通过注解配置管理bean

之前的方式通过bean标签一个个的配置javabean交给IOC管理过于麻烦,为了让代码更加简洁,Spring提供了基于注解的方式配置javabean给容器

  • @Component 普通组件 标识一个受Spring IOC容器管理的组件
  • @Repository 持久层的组件
  • @Service 业务层组件
  • @Controller 控制层组件
组件注解和Autowired自动装配需导aop包
  • 组件注解必须导入aop jar包
通过后置处理器处理@Autowired

在指定要扫描的包时,context:component-scan 元素会自动注册一个bean的后置处 理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记 了@Autowired、@Resource或@Inject注解的属性。

即在init前 后置处理器前处理方法会检查该bean有无autowired注解,有则根据autowired的value值找aop容器对应id的bean,无则查找对应class的bean,如果没找到,或者找到多个,都会报错,除非设置required=false,或者@Qualifiter指定bean的id

@Autowired可用于数组和集合类型
  • @Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。
  • @Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。
  • @Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。
组件注解和Autowird注入bean属性流程与注意事项

注解与Autowired配置步骤:

	1、在想要配置到容器中的类名上 使用注解即可
	2、需要在IOC容器配置文件中 指定开启自动注解扫描
			context:component-scan :开启IOC组件扫描功能
					base-package: 指定要自动扫描的包
					resource-pattern="dao/*.class"   指定保留扫描的包,其他的排除
					当前标签默认包下的所有注解都扫描
						》 默认情况:use-default-filters="true" 如果需要排除指定类型的注解,可以在标签内使用context:exclude-filter排除
								<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
									- 一般只会排除  Repository、Service、Controller,Component就会排除所有的组件
						》如果不使用默认的扫描规则:use-default-filters="false" ,需要使用context:include-filter指定要扫描的组件注解
								<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	3、使用IOC容器对象时,就可以使用注解标注的bean
			可以使用type获取,也可以使用类名首字母小写作为id获取
			Spring默认将类名首字母小写作为id绑定给bean,找不到用类型匹配,但是类型需要只有该类型唯一的对象,不然不知道匹配哪一个
			我们也可以通过注解 指定自定义id:@Component("u")
	4、Spring基于注解的 自动装配: 以后经常使用
		原理:后置处理器,在对象创建后,查找对象的 自动装配的注解 对属性进行赋值(根据类型或者 id去查找对应的值)操作
			自动装配的前提:  需要被装配的bean 和需要使用值的bean 都需要配置到容器中
						需要被自动装配的属性,需要在属性上使用 @Autowired注解
			自动装配的顺序:
					IOC容器 先会根据 属性的属性名作为id去ioc容器中匹配对应的元素进行装配
							如果属性名和要装配的bean的id值不同,解决方法:
								- 修改属性名和javabean的id值相同 private UService userService;
								- 给javabean的组件注解中设置id值 和 属性名一样 @Service("service")
					如果id找不到,再根据类型去匹配(自己的类型以及子类型的对象)进行装配
							如果该类型或该类型的实现类的类型 容器中配置了多个,会报错
					如果属性名和要装配的bean的id不同,但是容器中又配置了该类型的多个bean,解决
						使用:@Qualifier("service")在属性上 指定 要自动装配的bean的id
					如果自动装配对应的类型的对象在容器中没有,@Autowired 默认必须给该属性自动装配上,导致报错
						如果希望代码能够正常执行需要@Autowired(required=false)
<context:component-scan base-package="com.hey" use-default-filters="true">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
@Service("service")

@Autowired
@Qualifier("userService")
private UService s;
@Resource 方式
指定类型自动装配:	  
@Resource(type=UserService2.class)  
private UService s;  
	
@Resource(name="service")

因为具体的实现类型如果不同,是可以确定的

@Inject

@Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。

配置文件

配置bean

属性值注入

<bean class="com.hry.bean.User" id="user_xia">
</bean>
  • setter方法注入(实际调用的是setter方法)

    <property name="username" value="laowang"></property>

  • 通过构造器注入
    可以通过 name/type/index三种方式 指定 构造器参数的匹配
    name: 唯一匹配 推荐
    type: 需要保证 位置和构造器参数位置相同 不推荐使用
    index:从0开始计算

    <constructor-arg type="java.lang.String" value="huxinyan" index="1"></constructor-arg>
    
  • Spring2.5版本后:可以通过p名称空间 注入属性值 (使用前必须 在当前xml文件中引入支持p名称空间的xml约束)

    <bean class="com.hey.bean.User" id="user_p" 
    	p:username="2qweqwe" p:email="xxx@12.com" p:id="111" p:password="12123213"></bean>
    

属性值区分-依赖注入可以使用的值

  • 字面量:直接通过字符串表示的值 使用value 或者是来进行注入

  • 特殊字符: 可以使用实体. 或者使用 <![CDATA[ .... ]]>

  • ref引用:通过id引用 , 指定复杂类型的数据
    需要先配置一个需要使用的对象

    或者是来引用IOC容器中的bean对象

  • 级联属性操作
    ```

  • 内部bean 外部不可使用

    <property name="gf">
    	<bean class="com.hey.bean.GirlFriend" p:cailiao="塑料" p:price="100"></bean>
    </property>
    
  • 集合属性

集合属性管理

数组和list
  • 内部
    <property name="list" ref="list">
    	<!-- 内部创建的list只能在内部使用 -->
    	<list>
    		<ref bean="user_p"/>
    		<null/>
    		<bean class="com.hey.bean.User"  p:username="huxinyan"></bean>
    	</list>
    </property>
    
  • 外部
    <!-- 可以在配置文件中引入util名称空间,可以在页面中单独创建集合 -->		
    <util:list id="list" list-class="java.util.LinkedList" value-type="com.hey.bean.User">
    	<ref bean="user_p"/>
    	<null/>
    	<bean class="com.hey.bean.User"  p:username="huxinyan111"></bean>
    </util:list>	
    
map

以entry设值 map中的一个元素 一个键值对结构

  • 内部
    <property name="map">
    	<map>
    		<entry>
    			<key>
    				<value>key1</value>
    			</key>
    			<ref bean="user_p"/>
    		</entry>
    		<entry>
    			<key>
    				<value>key2</value>
    			</key>
    			<null />
    		</entry>
    		<entry>
    			<key>
    				<value>key3</value>
    			</key>
    			<bean class="com.hey.bean.User" p:username="xxxxxxx"></bean>
    		</entry>
    	</map>
    </property> 
    
  • 外部
    <util:map id="map" key-type="java.lang.String" value-type="com.hey.bean.User">
    	<entry>
    		<key>
    			<value>key1</value>
    		</key>
    		<ref bean="user_p"/>
    	</entry>
    	<entry>
    		<key>
    			<value>key2</value>
    		</key>
    		<null />
    	</entry>
    	<entry>
    		<key>
    			<value>key3</value>
    		</key>
    		<bean class="com.hey.bean.User" p:username="AAAAA"></bean>
    	</entry>
    </util:map>	
    

提取配置信息到配置文件中

为的是更加灵活,抽取配置信息到文件中,暴露出来,且与代码解耦,便于修改(修改不影响代码)

  • 如果需要在当前ioc配置文件中使用另外的配置文件,需要使用context名称空间将配置文件 加载到当前配置中(容器加载为对象,读取配置文件到这里,就将其他配置文件的信息以流对象形式读取到该容器上下文环境中)
  • 如果需要指定从类路径下加载,可以使用classpath:指定
  • 注意:propertie文件中 key名 需要加标记 如mysql.username
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 然后可以使用${key}  从配置文件中取出对应的值 -->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="druidDataSource">
 		<property name="url" value="${mysql.url}"></property>
 		<property name="driverClassName" value="${mysql.driver}"></property>
 		<!-- ${username} 操作系统也会使用username保存当前用户的用户名存到配置文件中,如果我们也是用username会冲突 -->
 		<property name="username" value="${mysql.username}"></property>
 		<property name="password" value="${mysql.password}"></property>
 	</bean>

基于XML的自动装配

  • 手动装配:以value或ref的方式明确指定属性值都是手动装配。
  • 自动装配:根据指定的装配规则,不需要明确指定,Spring自动将匹配的属性值注入bean中。

不推荐使用,不知道到底匹配的是哪个值

autowire="default"  不自动装配
autowire="byType" 根据类型自动装配
		当前bean的引用属性,如果在容器中存在类型对应的bean,容器就会自动将bean设置给该属性
		- 该方式必须保证要装配的类型只能配置一个
autowird="byName" 根据对象的属性名作为id去容器中匹配对应的对象进行装配
		如果属性名和bean的id不一样则装配失败

推荐使用之前的value、ref的方式明确指定 要注入的属性值
例如:

<bean id="xiaohong" class="com.hey.bean.GirlFriend" p:price="10000" p:cailiao="不锈钢"></bean>
	<bean id="gf" class="com.hey.bean.GirlFriend" p:price="100" p:cailiao="塑料"></bean>
<!-- 
	通过p:gf=""
		value
		p:gf-ref=""
		ref
		这种方式手动指定注入的属性值  就是手动装配
 -->
<bean class="com.hey.bean.User" p:username="xiashengsheng" autowire="byName"></bean>

bean生命周期

scope

  • singleton :默认值 单例
    ioc容器初始化后立即创建对象,以后获取的都是同一个
  • prototype : 多例,每次获取都新创建
    ioc容器初始化后不会创建对象,以后每次获取时新创建
  • request: web项目中 每次请求会创建一次该对象
  • session: web项目中 每次会话 会创建一次该对象

init和destory的选择

  • springIOC为了方便我们管理bean对象的初始化创建 也可以让我们自己指定初始化方法和销毁方法
  • IOC容器创建对象时 :init-method=“init” destroy-method=“destroy”
作用

init可以检验bean设置参数的合法正确性,以及从持久化数据中将数据加载到内存中
init类似书城项目的get方法再将bean返回给用户前做参数安全处理

destroy可以释放资源,以及持久化数据

生命周期流程

  1. 构造器 (单例在spring容器启动时加载配置文件读取时立即反射创建)
  2. setter方法设置属性值 配置文件的property配置
  3. 调用后缀处理器的 postProcessBeforeInitialization
  4. 调用自己编写的初始化方法:检查bean的属性值的范围
  5. 调用后缀处理器的postProcessAfterInitialization
  6. 使用bean对象
  7. 容器正常关闭时会自动调用销毁方法 :收尾操作(必须有close关闭操作来提醒spring容器将要关闭以进行收尾工作,如果强行关闭,则无法知晓)

后置处理器

IOC容器为了方便我们在bean外部的代码中也可以管理bean的生命周期,提供了后置处理器,一旦在spring容器中创建,就管理所有的bean

使用步骤
  1. 创建类实现BeanPostProcessor
  2. 实现方法
  3. 在IOC配置文件中配置后置处理器(IOC容器会自动使用)
    <bean class="com.hey.bean.MyBeanPostProcessor"></bean>
代码示例与后置处理器两个方法作用
//在bean的初始化方法调用之前调用: 用来检查bean的属性值的安全
	/**
	 * 参数1:
	 * 		正在初始化的bean对象,可以用来检查
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("MyBeanPostProcessor.postProcessBeforeInitialization ==>  " +beanName+"即将初始化....");
		//后缀处理器处理完bean之后必须将bean返回,否则ioc容器不能继续初始化bean
		return bean;
	}
	
	//在bean的初始化方法调用之后调用
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("MyBeanPostProcessor.postProcessAfterInitialization ==>  " +beanName+"已经初始完成....");
		return bean;
	}
注意
  • 因为单例模式bean是容器一开始创建就加载,所以后面再获取user对象,也不会再创建对象了,直接用一开始创建的对象,name(id)获取到的是null,就返回null,生命周期相关方法也只调用1次
  • 如果后缀处理器返回null,那么后处理方法将不会执行,因为null值调用不了方法
User user = applicationContext.getBean(User.class);
System.out.println(user);
User user2 = applicationContext.getBean("user",User.class);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值