IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
IOC 描述的是一种思想,而DI 是对IOC思想的具体实现。
Spring提供了IOC容器的两种实现方式
-
- BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
- ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
- ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件。
- FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件。
- 在IOC容器初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。
- 是ApplicationContext的子接口,包含一些扩展方法。
- refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文(容器)的能力。
Student stu = context.getBean(Student. class);
//推荐使用 Student stu = cxt.getBean(“student”,Student. class);
<bean id="stu" class="com.jdy.spring2020.bean.Student"> <property name="age" value="18"/> <property name="name" value="jdy"/> <property name="studentId" value="001"/> </bean>
<bean id="student" class="com.jdy.spring2020.bean.Student"> <constructor-arg name="studentId" value="0001"/> <constructor-arg name="name" value="jdy"/> <constructor-arg name="age" value="18"/> </bean>
<!--通过索引值指定参数位置 索引从0开始--> <bean id="student" class="com.jdy.spring2020.bean.Student"> <constructor-arg index="0" value="0001"/> <constructor-arg index="1" value="jdy"/> <constructor-arg index="2" value="18"/> </bean>
<bean id="student" class="com.jdy.spring2020.bean.Student"> <constructor-arg index="0" value="0001" type="java.lang.String"/> <constructor-arg index="1" value="jdy" type="java.lang.String"/> <constructor-arg index="2" value="18" type="java.lang.String"/> </bean>
<!--p命名空间-->
<bean id="student" class="com.jdy.spring2020.bean.Student" p:studentId="0001" p:name="jdy" p:age="18"/>
<bean id="student" class="com.jdy.spring2020.bean.Student"> <property name="name"> <value> <![CDATA[<?><@!#^&%*!]]></value> </property>
</bean>
<bean id="student" class="com.jdy.spring2020.bean.Student"> <property name="book" ref="book"/> </bean> <bean id="book" class="com.jdy.spring2020.bean.Book"> <property name="name" value="历史"/> <property name="price" value="10.0"/> </bean>
<property> 的ref属性指明引用外部Bean的id。
<!--外部改成内部--> <bean id="student" class="com.jdy.spring2020.bean.Student"> <property name="book"> <bean class="com.jdy.spring2020.bean.Book"> <property name="name" value="历史"/> <property name="price" value="10.0"/> </bean> </property> </bean>
@Data @AllArgsConstructor @NoArgsConstructor @ToString public class Student { private String studentId; private String name; private String age; private Book book; private List<Book> books; private Map<String,Object> hobby;
private List<String> phoneNos; }
@AllArgsConstructor @NoArgsConstructor @Data @ToString public class Book {private String book_name; private String price; }
<!--数组和List--> <bean id="stu" class="com.jdy.spring2020.bean.Student"> <property name="books"> <list> <ref bean="englist_book"/> <ref bean="java_book"/> </list> </property> </bean> <bean id="englist_book" class="com.jdy.spring2020.bean.Book" p:book_name="英语书" p:price="10.0"/> <bean id="java_book" class="com.jdy.spring2020.bean.Book" p:book_name="java入门" p:price="20.0"/>
<bean id="stu" class="com.jdy.spring2020.bean.Student"> <property name="hobby"> <map> <entry> <key> <value> 爱好 </value> </key> <value> 吃 </value> </entry> <entry> <key> <value> 英语书 </value> </key> <ref bean="englist_book"/> </entry> </map> </property> </bean> <bean id="englist_book" class="com.jdy.spring2020.bean.Book" p:book_name="英语书" p:price="10.0"/>
如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要将集合bean的配置拿到外面,供其他bean重复引用。
配置集合类型的bean需要引入util名称空间。
首先在配置文件 中 xsi:schemaLocation 后面补上下面约束
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd
<bean id="englist_book" class="com.jdy.spring2020.bean.Book" p:book_name="英语书" p:price="10.0"/> <bean id="java_book" class="com.jdy.spring2020.bean.Book" p:book_name="java入门" p:price="20.0"/> <util:list id="books"> <ref bean="englist_book"/> <ref bean="java_book"/> </util:list> <util:list id="phoneNos"> <value>10086</value> <value>10001</value> <value>10000</value> </util:list> <util:map id="hobby"> <entry key="爱好" value="吃" /> <entry key="book" value-ref="java_book" /> </util:map> <bean id="stu" class="com.jdy.spring2020.bean.Student"> <property name="hobby" ref="hobby"/> <property name="books" ref="books"/> <property name="phoneNos" ref="phoneNos"/> </bean>
FactoryBean
Spring中有两种类型的bean,一种是普通bean,另一种是工厂Bean,即FactoryBean 。
-
- 工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,工厂Bean返回的是该工厂bean的getObject方法所返回的对象。
- 工厂bean必须实现org.springframework.beans.factory.FactoryBean接口。
public class MyFactoryBean implements FactoryBean<Book> { @Override public Book getObject() throws Exception { return new Book("数学","20.0"); } /** * 返回具体bean对象类型 * @return */ @Override public Class<Book> getObjectType() { return Book.class; } /** * 是否是单利 * @return */ @Override public boolean isSingleton() { return true; } }
<bean id="myFactoryBean" class="com.jdy.spring2020.factorybean.MyFactoryBean"> </bean>
@Test public void test_method02() { ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); Book book = (Book) context.getBean("myFactoryBean"); System.out.println("book = " + book); }
通过debug模式可知,在MyFactoryBean中3个方法的执行顺序依次是isSingleton、getObjectType、getObject。
<bean id="baseBook" class="com.jdy.spring2020.bean.Book" > <property name="price" value="10"/> </bean> <bean id="englist_book" class="com.jdy.spring2020.bean.Book" p:book_name="英语书" parent="baseBook"/> <bean id="java_book" class="com.jdy.spring2020.bean.Book" p:book_name="java入门" parent="baseBook"/>
-
-
如果一个bean的class属性没有指定,则必须是抽象bean
-
并不是<bean>元素里的所有属性都会被继承。比如:autowire,abstract等。
-
也可以忽略父bean的class属性,让子bean指定自己的类,而共享相同的属性配置。
public class A { public A() { System.out.println("A"); } }
public class B { public B() { System.out.println("B"); } }
<!--<bean id="a" class="com.jdy.spring2020.bean.A"></bean>-->
<bean id="a" class="com.jdy.spring2020.bean.A" depends-on="b" ></bean>
<bean id="b" class="com.jdy.spring2020.bean.B" scope="prototype"></bean>
@Test public void test_method04() { ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); }
我们知道,java类在被IOC初始化时会调用构造器,单利Bean在IOC容器初始化时,就会被初始化,多实例Bean是在调用时被初始化,基于这些条件 A的<bean>中没有depends-on 时IOC只会加载A,否则A B都会加载。
类别 | 说明 |
singleton | 在SpringIOC容器中仅存在一个Bean实例,Bean以单实例的方式存在。 |
prototype | 每次调用getBean()时都会返回一个新的实例 |
request | 每次HTTP请求都会创建一个新的Bean,该作用域适用于WebApplicationContxet环境 |
session | 同一个HTTP Seesion共享一个Bean不用的 HTTP Seesion使用不同的Bean,该作用域适用于WebApplicationContxet环境 |
-
-
Spring IOC容器对bean的生命周期进行管理的过程:
-
通过构造器或工厂方法创建bean实例
-
为bean的属性设置值和对其他bean的引用
-
调用bean的初始化方法
-
bean可以使用了
-
@ToString public class Car { private String brand; private String price; public void inti_method(){ System.out.println("Car-inti_method...."); } public void destory_method(){ System.out.println("Car-destory_method...."); } public Car() { System.out.println("Car-无参构造...."); } public void setBrand(String brand) { System.out.println("Car-setBrand方法...."); this.brand = brand; } }
<!--在配置bean时,通过init-method和destroy-method 属性为bean指定初始化和销毁方法--> <bean id="car" class="com.jdy.spring2020.bean.Car" p:brand="五菱" init-method="inti_method" destroy-method="destory_method"/>
public class Test_02 { ClassPathXmlApplicationContext context = null; { context = new ClassPathXmlApplicationContext("application_02.xml"); } @Test public void test() { for (String beanDefinitionName : context.getBeanDefinitionNames()) { System.out.println(context.getBean(beanDefinitionName)); } } }
-
-
bean后置处理器
对IOC容器里的所有bean实例逐一处理
,而非单一实例。其典型应用是:检查Bean属性的正确性或根据特定的标准更改bean的属性
。 -
bean后置处理器时需要实现接口:org.springframework.beans.factory.config.
BeanPostProcessor
。在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法: -
postProcessBeforeInitialization(Object, String)
-
<bean id="myProcessor" class="com.jdy.spring2020.Processor.MyProcessor"/>
/** * bean的购置处理器:对IOC容器中所有的bean都起作用 */ public class MyProcessor implements BeanPostProcessor { public static final String TITLE="Bean后置处理器"; /** *在bean的生命周期的初始化方法之前执行 * @param bean 正在被创建的bean对象 * @param beanName bean对象的id属性值 * @return * @throws BeansException */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println(TITLE+"-Before方法......"); return bean; } /** * bean的生命周期的初始化方法之后执行 * @param bean * @param beanName * @return * @throws BeansException */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println(TITLE+"-After方法......"); return bean; } }
添加bean后置处理器后bean的生命周期
-
-
通过构造器或工厂方法创建bean实例
-
为bean的属性设置值和对其他bean的引用
-
将bean实例传递给bean后置处理器的postProcessBeforeInitialization()方法
-
调用bean的初始化方法
-
将bean实例传递给bean后置处理器的postProcessAfterInitialization()方法
-
bean可以使用了
-
当容器关闭时调用bean的销毁方法
-
当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。
<!-- 直接配置 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="root"/> <property name="password" value="root"/> <property name="jdbcUrl" value="jdbc:mysql:///db"/> <property name="driverClass" value="com.mysql.jdbc.Driver"/> </bean>
-
xmlns:context=“http://www.springframework.org/schema/context”。同时在xsi:schemaLocation这个字符串中添加context相关的解析文件 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd【注意版本】。
- 导入依赖
<dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency>
!-- classpath:xxx 表示属性文件位于类路径下 --> <context:property-placeholder location="classpath:jdbc.properties"/>
- 从properties属性文件中引入属性值
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${prop.userName}"/> <property name="password" value="${prop.password}"/> <property name="jdbcUrl" value="${prop.url}"/> <property name="driverClass" value="${prop.driverClass}"/> </bean>
- 创建properties属性文件
prop.userName=root prop.password=root prop.url=jdbc:mysql:///test prop.driverClass=com.mysql.jdbc.Driver
public class Test_02 { ClassPathXmlApplicationContext context = null; { context = new ClassPathXmlApplicationContext("application_02.xml"); } @Test public void test() { ComboPooledDataSource dataSource = context.getBean("dataSource",ComboPooledDataSource.class); System.out.println(dataSource.getUser()); System.out.println(dataSource.getPassword()); System.out.println(dataSource.getJdbcUrl()); System.out.println(dataSource.getDriverClass()); } }
-
-
自动装配
-
-
根据名称自动装配
:必须将目标bean的名称和属性名设置的完全相同 -
通过构造器自动装配
@ToString @Data @NoArgsConstructor @AllArgsConstructor public class Person { private String name; private Dog dog; }
@ToString @Data @NoArgsConstructor @AllArgsConstructor public class Dog { private String name; private String age; }
自动装配:
-
byName
:用Person中dog属性的setDog()后面的Dog-->将Dog转成dog-->在容器中找到id为dog的bean-->装配 -
byType
: 使用bean的属性的类型与IOC容器中<bean>的class进行匹配,如果唯一匹配则装配成功, 如果匹配到多个兼容类型的bean,则抛出异常。
<bean id="person" class="com.jdy.spring2020.bean.Person" autowire="byName"> <property name="name" value="小明"/> </bean> <bean id="dog" class="com.jdy.spring2020.bean.Dog"> <property name="name" value="阿黄"/> <property name="age" value="10"/>
</bean>
-
-
持久化层组件:
@Repository
标识一个受Spring IOC容器管理的持久化层组件 -
业务逻辑层组件:
@Service
标识一个受Spring IOC容器管理的业务逻辑层组件 -
表述层控制器组件:
@Controller
标识一个受Spring IOC容器管理的表述层控制器组件 -
组件命名规则 ①默认情况:使用组件的简单类名首字母小写后得到的字符串作为bean的id ②使用组件注解的value属性指定bean的id
注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。
(1)、指定被扫描的package
<context:component-scan base-package="com.jdy.spring2020"/>
-
-
<context:exclude-filter>
子节点表示要排除在外的目标类 -
- type:指定扫描规则,常见的扫描规则:
annotation
、assignable
、regex
、
1、
<context:component-scan base-package="com" > <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
2、
<context:component-scan base-package="com.jdy.spring2020.scan" use-default-filters="false"> <context:include-filter type="assignable" expression="com.jdy.spring2020.scan.service.MyService"/> </context:component-scan>
3、
<context:component-scan base-package="com.jdy.spring2020.scan" use-default-filters="false"> <!--type是给正则表达式,类名以Service结尾的会被扫描进Spring容器--> <context:include-filter type="regex" expression="com.jdy.spring2020.scan.*Service"/>
</context:component-scan>
4、
public class MyTypeFilter implements TypeFilter { /** * * @param metadataReader:读取到当前正在扫描的类的信息 * @param metadataReaderFactory :获取其他类(如超类和接口)的元数据读取器的工厂 * @return match方法返回值true:表示规则匹配成功 false:表示规则匹配失败 * @throws IOException */ @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前正在扫描的类的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取类名 String className = classMetadata.getClassName(); //获取当前类的注解信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前类型资源信息(路径信息) Resource resource = metadataReader.getResource(); // if(className.contains("Service")){ return true; } return false; } }
- 编写xml
<context:component-scan base-package="com.jdy.spring2020.scan" use-default-filters="false"> <context:include-filter type="custom" expression="com.jdy.spring2020.MyTypeFilter"/> </context:component-scan>
-
Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。
-
实现依据
在指定要扫描的包时,
<context:component-scan>
元素会自动注册一个bean的后置处 理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记 了@Autowired、@Resource或@Inject注解的属性。 -
@Autowired注解
-
根据类型实现自动装配。首先使用byType的方法进行自动装配,如果能唯一匹配,则装配成功,如果匹配到多个兼容性的bean,还会尝试使用byName的方法进行唯一性确定如果你能唯一确定,装配成功
-
构造器、普通字段(即使是非public)、一切具有参数的方法都可以应用@Autowired注解。
-
默认情况下,所有使用@Autowired注解的属性都需要被设置。当Spring找不到匹配的bean装配属性时,会抛出异常。
-
若某一属性允许不被设置,可以设置@Autowired注解的required属性为 false
-
默认情况下,当IOC容器里存在多个类型兼容的bean时,Spring会尝试匹配bean的id值是否与变量名相同,如果相同则进行装配。如果bean的id值不相同,通过类型的自动装配将无法工作。此时可以在
@Qualifier
注解里提供bean的名称。Spring甚至允许在方法的形参上标注@Qualifiter注解以指定注入bean的名称。 -
@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。
-
@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。
-
@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。
-
-
@Resource
@Resource注解要求提供一个bean名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为bean的名称。
-
@Inject