1.Bean的配置
String 采用了XML配置文件来指定实例之间的依赖关系,从Spring2.0开始,spring推荐采用了XML Schema 来定义配置文件的语义约束。
在idea中创建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">
</beans>
在上述代码中引入了一个beans的定义,它是一个根元素,而XSD文件也被引入了,这样它所定义的元素将可以对应的Sping Bean
2.Bean的标识
在上一个标题中我们创建了完整的beans.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">
<!--把所有对象创建到spring的IOC容器中,并起上名字
id:表示给对象起名字
class:类的全类名
-->
<bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>
<bean id="bookService3" class="com.zhao.factory.BookServiceFactory" factory-method="getBean"></bean>
</beans>
在配置文件中,Spring配置Bean实例通过ID作为其唯一标识,程序通过ID属性值访问该Bean实例。
当然也可以使用name属性来指定Bean的ID,代码如下;
<?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的IOC容器中,并起上名字
id:表示给对象起名字
class:类的全类名
-->
<bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>
<bean id="bookService" class="com.zhao.service.impl.BookServicelmpl">
<property name="name" value="张三" />
<property name="bookDao" ref="bookDao" />
</bean>
</beans>
3.BeanFactory的作用?
BeanFactory是Spring中核心的顶层接口,是Bean的工厂,主要职责就是生产Bean,实现了简单工厂的设计模式,根据传入一个唯一的标识来获得Bean对象,它有非常多的实现类。
BeanFactory也是容器,因为它管理Bean的生命周期。
BeanDefinition的作用:
主要是用来存储Bean的定义信息,决定Bean的生产方式。
比如类名,对象名,作用域,懒加载。
这里是利用了反射的机制,是拿到了类名之后用class.forname拿到类,然后通过newinstance来创建对象。
ApplicationContext和BeanFactory的关系?
容器和工厂的关系类似于4S店和汽车店的关系。
Application不生产bean,只是通知BeanFactory来生产bean。ApplicationConext实现了BeanFactory。
但是ApplicationCOntext可以自动地把bean注册进来,加载环境变量,实现事件监听,支持多语言。
SpringIOC容器的加载过程
从new ApplicationContext开始加载。
- 概念态
- 定义态
- 纯净态
- 成熟态
实例化一个ApplicationContext的对象;
调用bean工厂后置处理器完成扫描;
循环解析扫描出来的类信息;
实例化一个BeanDefinition对象来存储解析出来的信息;
把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来, 以便后面实例化bean;
再次调用其他bean工厂后置处理器;然后,会检查是否lazy,是否抽象等等,再通过反射进行实例化得到纯净的bean,然后进行属性注入,创建AOP等初始化,在put到单例池。
3.Bean管理
在 Spring 中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。所以所谓的Bean管理就是对对象的管理。包含创建对象、给对象注入属性。
Spring IoC容器管理Bean有两种方式:基于 xml 配置文件;基于注解方式。
3.1什么是Bean,JavaBean和SpringBean和对象的区别?
由SpringIOC容器管理的对象就是bean,由IOC容器实例化组装和管理的对象
3.2配置bean有几种方式?
- xml配置文件直接配置
- @Component(Controller,Service,Repostory) 前提得配置扫描,是直接通过反射得到的。
- JavaConfig:@Bean是标注在方法上面的,必须返回一个对象作为Bean,一般是加载Configuration注解下面的。
- @Import
3.3Bean的作用域
当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定为Bean指定的作用域。Spring支持如下6种作用域。
- singleton:单例模式,在整个Spring IOC容器中,singleton作用域的Bean将只生成一个实例。
- prototype:每次通过容器的getBean()方法获取prototype作用域的Bean域时,都将产生一个新的Bean实例。
- Request:每次发起请求都创建一个Bean
- Session:每个对话都创建一个对象
- application:一个全局应用共享对象
- websocket:在整个WebSocket的通信过程中,该Bean只生成一个实例。只有在web应用中使用Spring时,该作用域才真正生效。
3.4Spring的Bean是线程安全的吗?如何处理单程并发问题?
并不是,单例Bean在类中声明了成员变量并且有读写操作,这个时候就会出现线程不安全的情况。但是,只需要把成员变量放在方法中,就是线程安全的了。
解决方法:
- 设置为多例
- 将成员变量放在ThreadLocal中,也就是让成员变量与本地的线程做关联,这样就是线程安全的了。
- 使用同步锁 比如加入synchronized就行了
3.5实例化Bean有几种方式
构造器方式(反射机制)
- 实例工厂方式(@bean)
- FactoryBean
3.6解释不同方式的自动装配,spring 自动装配 bean 有哪些方式?
autowired:
- byName 根据bean的名字进行装配,如果bean的名字和属性相同,就会自动相配
- ByType:根据参数的数据类型进行自动装配
- constructor:利用构造函数进行装配
- no:不自动装配,手动通过ref属性或者Autowired手动指定需要自动注入的属性。
3.7Bean的生命周期回调方法
有两个重要的bean 生命周期方法,第一个是init , 它是在容器加载bean时候被调用。第二个方法是 destroy 它是在容器卸载类的时候被调用。
bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。
3.8Bean的生命周期
- 实例化:通过反射去推断构造函数进行实例化
- 属性赋值:解析自动装配,进行依赖注入(循环依赖)
- 初始化:调用Aware回调方法,调用初始化生命周期回调方法(如果实现了它)如果bean出现了aop,那就会创建动态代理。
- 销毁:Spring容器关闭的时候进行调用。
3.9如何解决循环依赖
三级缓存。
一级缓存:存储完整的Bean
二级缓存:避免动态代理重复创建
三级缓存:缓存函数接口,可以通过lambda表达式吧方法传进去,但是不会立刻调用,可能会aop创建。
如何在bean创建完之后进行扩展?
beanFactoryPostProcesso
Spring如何避免在并发下获取不完整的Bean?
设置双重检查锁, 在创建bean的过程中加锁,直到放到一级缓存中。但是不是放到一级缓存中,而是把锁加载到二级缓存中。而另一个线程会在能执行后再次寻找一级缓存中的bean。
之所以不放在一级缓存里,是因为性能——因为锁住一级缓存,其他线程就拿不出来其他的bean,会影响其他线程的性能。
JavaConfig是如何代替Spring.xml方式的?
1.以前Xml
a. Spring容器 ClassPathXmlApplicationContext(“xml”)
b. Spring.xml
c. bean的配置:< bean scope lazy>
d. 扫描包: < component-scan>
e. 引入外部属性配置文件 < property-placeHodeler resource=“xxx.properties”>
f. < property name=“password” value=“${mysql.password}”></ property>
g. 指定其他配置文件:<import resource=“”
2.javaconfig
a. Spring容器:AnnotationConfigApplicationContext(javaconfig.class)
b. 配置类 @Configuration
c. @Bean @Scope @Lazy
d. 扫描包: @ComponentScan
e. 引入外部属性配置文件 @PropertySource(“classpath:db.properties”)
f. @Value(“${mysql.password}”)
g. @Import @Import({配置类}) 使用比较灵活
如何在没有找到自动注入的bean/找到多个bean的时候不报错
@Autowired(required = false) 不报错
@Primary 多个bean
4.Bean的属性
4.1简单值的注入
将简单的值注入到Bean中是非常容易的,首先定义一个类,通过暴露setter方法来声明它所需设置的属性。
Bean代码如示:
package com.zhao.service.impl;
import com.zhao.dao.BookDao;
import com.zhao.service.BookService;
/**
* 低配版
*/
public class BookServicelmpl implements BookService {
private String name;
private BookDao bookDao;
public void setName(String name) {
this.name = name;
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
@Override
public void save() {
System.out.println("BookServicelmpl...save"+name);
bookDao.insert();
}
}
配置文件中:
<?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的IOC容器中,并起上名字
id:表示给对象起名字
class:类的全类名
-->
<bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>
<bean id="bookService" class="com.zhao.service.impl.BookServicelmpl">
<property name="name" value="张三" />
<property name="bookDao" ref="bookDao" />
</bean>
</beans>
测试类:
package com.zhao.servlet;
import com.zhao.service.BookService;
import com.zhao.service.impl.BookServiceVip;
import com.zhao.service.impl.BookServicelmpl;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class BookServlet {
BookService bookService;
/**
* 更换功能需要修改源代码
*/
@Test
public void add(){
System.out.println("BookServlet...add");
//1.获得IOC容器
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
//2.根据名字从容器中获取对象
bookService= (BookService) context.getBean("bookService");
bookService.save();
}
}
集合注入:
Bean组件的属性可能不仅仅是单一的值,而是一个对象集合。接下来的示例显示了如何最常用的List、Map和Properties类型的集合属性。首先定义一个类,代码如下:
接口类:
package com.zhao.service;
public interface BookService {
void save();
}
业务处理:
package com.zhao.service.impl;
import com.zhao.service.BookService;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class BookSeviceVip implements BookService {
List<String> list;
Set<String> set;
Map<String,String> map;
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
@Override
public String toString() {
return "BookSeviceVip{" +
"list=" + list +
", set=" + set +
", map=" + map +
'}';
}
@Override
public void save() {
System.out.println("BookSeviceVip...save");
}
}
配置文件中:
<?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">
<bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>
<bean id="bookService" class="com.zhao.service.impl.BookServiceImpl" scope="singleton">
<property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookService1" class="com.zhao.service.impl.BookServiceImpl" scope="singleton">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookService2" class="com.zhao.service.impl.BookSeviceVip" scope="singleton">
<property name="list">
<list>
<value>张三</value>
<value>李四</value>
<value>王二</value>
<value>麻子</value>
</list>
</property>
<property name="set">
<set>
<value>南阳</value>
<value>郑州</value>
<value>周口</value>
<value>信阳</value>
</set>
</property>
<property name="map">
<map>
<entry key="name" value="张三"/>
<entry key="age" value="20"/>
<entry key="sex" value="男"/>
</map>
</property>
</bean>
<!-- 配置工厂,但是获得的工厂的产品-->
<bean id="bookService3" class="com.zhao.factory.BookServiceFactory" factory-method="getBean"></bean>
</beans>
测试类:
package com.zhao.test;
import com.zhao.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test03 {
@Test
public void test01(){
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
BookService bookService1= (BookService) context.getBean("bookService");
System.out.println(bookService1);
BookService bookService= (BookService) context.getBean("bookService2");
System.out.println(bookService);
}
}
测试结果:
5.单例和原型
单例(singleton)和原型(prototype)是Bean的两种常用作用域。如果不指定Bean的作用域,Spring默认使用singleton作用域。
singleton作用域的Bean实例是同一个共享实例,可以重复使用。而prototype作用域的Bean实例都是全新产生的实例,相当于new操作。而实例的创建和销毁等工作都会增加系统的开销,因此,应该尽量避免将Bean设置成prototype作用域。
<?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">
<bean id="bookDao" class="com.zhao.dao.impl.BookDaoImpl"></bean>
<bean id="bookService" class="com.zhao.service.impl.BookServiceImpl" scope="singleton">
<property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookService1" class="com.zhao.service.impl.BookServiceImpl" scope="singleton">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
</bean>
<!-- 配置工厂,但是获得的工厂的产品-->
<bean id="bookService3" class="com.zhao.factory.BookServiceFactory" factory-method="getBean"></bean>
</beans>
测试代码如下:
package com.zhao.test;
import com.zhao.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test01 {
@Test
public void test01(){
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
BookService b1= (BookService) context.getBean("bookService");
BookService b2= (BookService) context.getBean("bookService");
System.out.println(b1);
System.out.println(b2);
//单例,框架会唯一创建对象,后续获取的都是同一个,在配置文件加载的时候就创建了
//多例,框架在每次获取的时候创建一个新的,在获得时候创建 多线程
}
}
6.注解
6.1@Autowired 注解有什么作用
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。
6.2atuowired和resource有什么区别?
autowired是spring提供的,默认类型。
resource是jdk提供的,默认名字。
6.3使用@Autowired注解自动装配的过程是怎样的?
6.3.1@Autowired 通过Bean的后置处理器进行解析的
在创建一个spring容器的时候,在构造函数中进行注册。
- 在实例化后预解析(解析@Autowired标注的属性、方法 比如:把属性的类型、名称、属性所在的类… 元数据缓存起)
- 在属性注入真正的解析(拿到上一步缓存的元数据 去ioc容器帮进行查找,并且返回注入)
- 首先根据预解析的元数据拿到 类型去容器中进行查找
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false
6.4使用@Component注解装配Bean
- Component代表Spring IOC会把这个类扫描生成Bean实例,而其中的value属性代表这个类在Spring中的Id,相当于XML方式定义的Bean的ID,也可以写成@Component(BookDaoImpl ),或者直接写成@Component,那么其ID就是类名首字母小写。
- @Value:代表值的注入,这里只是注入一些简单的值,注入的时候Spring会成为其转化类型。
接口:
package com.zhao.dao;
public interface BookDao{
void insert();
}
在dao包中的impl包里定义一个BookDaoImpl类:
package com.zhao.dao.impl;
import com.zhao.dao.BookDao;
import org.springframework.stereotype.Component;
@Component
public class BookDaoImpl implements BookDao {
@Override
public void insert() {
System.out.println("BookDaoImpl...insert");
}
}
配置文件:
<?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">
<!--让系统去扫描注解-->
<context:component-scan base-package="com.zhao"/>
</beans>
接口:
package com.zhao.service;
public interface BookService {
void save();
}
在service中创建一个impl包然后定义一个BookServiceImpl类:
package com.zhao.service.impl;
import com.zhao.dao.BookDao;
import com.zhao.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component("bookService")
public class BookServiceImpl implements BookService {
//自动注入优先按照类型注入
@Autowired
BookDao bookDao;
// public void setBookDao(BookDao bookDao) {
//
// this.bookDao = bookDao;
// }
@Override
public void save() {
System.out.println("BookServiceImpl...save");
bookDao.insert();
}
}
测试类:
package com.zhao.servlet;
import com.zhao.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test01 {
BookService bookService;
@Test
public void test01(){
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
bookService= (BookService) context.getBean(BookService.class);//按照类型获得对象,前提类型有唯一的对象
bookService.save();
}
}