Spring 框架基础3
转载:https://blog.csdn.net/qq_35843514/article/details/114287046?spm=1001.2014.3001.5501
一、概念与定义
1、什么是Spring beans?
Bean: 在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。Bean是由Spring IoC容器实例化,组装和以其他方式管理的对象。否则,bean仅仅是应用程序中许多对象之一。Bean及其之间的依赖关系反映在容器使用的配置元数据中。
Spring Beans是构成Spring应用核心的Java对象。这些对象由Spring IoC容器实例化、组装、管理。这些对象通过容器中配置的元数据创建,例如,使用XML文件中定义的创建。
在Spring中创建的beans都是单例的beans。在bean标签中有一个属性为”singleton”, 如果设为true,该bean是单例的,如果设为false,该bean是原型bean。Singleton属性默认设置为true。因此,spring框架中所有的bean都默认为单例bean。
2、 一个 Spring Bean 定义包含什么?
一个Spring Bean 的定义包含容器必知的所有配置元数据,包括如何创建一个bean,它的生命周期详情及它的依赖。
3、如何给Spring 容器提供配置元数据?
这里有三种重要的方法给Spring 容器提供配置元数据。
(1)XML配置文件。
(2)基于注解的配置。
(3)基于Java的配置
二、Spring Beans注入属性(XML)
1、使用xml配置注入属性,set方式注入属性,有参构造方法注入,下面演示2种方式注入属性
Step 1: 创建Book.java,Orders.java类
Book.java
public class Book {
/**
* 创建属性
*/
private String bname;
private String bauthor;
/**
* 创建对应的set方法
*/
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
public void testDemo(){
System.out.println(bname + "::" + bauthor );
}
}
Orders.java
public class Orders {
private String oname;
private String address;
public Orders(String oname, String address) {
this.oname = oname;
this.address = address;
}
public void orderTest(){
System.out.println(oname + "::" + address);
}
}
Step 2: 配置Book,Order对象创建(创建对象时候,默认也是执行无参数构造方法完成对象创建)
<!--配置Book对象创建,并注入属性
id属性:唯一标识
class属性:类全路径(包类路径)
set方法注入属性-->
<bean id="book" class="com.sevattal.spring.Book">
<!--使用Property来进行属性注入-->
<property name="bname" value="Sevattal Blog"/>
<property name="bauthor" value="Sevattal"/>
</bean>
<!--有参构造方法注入属性-->
<bean id="orders" class="com.sevattal.spring.Orders">
<constructor-arg index="0" value="haha"/>
<constructor-arg index="1" value="hahhahahahha"/>
</bean>
Step 3: 测试类中添加测试方法:
@Test
public void testBookInsert(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Book book = context.getBean("book", Book.class);
book.testDemo();
System.out.println(book);
}
@Test
public void testOrderInsert(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Orders orders = context.getBean("orders", Orders.class);
orders.orderTest();
System.out.println(orders);
}
2、使用P名称空间注入(了解)
使用 p 名称空间注入,可以简化基于 xml 配置方式第一步 添加 p 名称空间在配置文件中
第一步:引入P名称空间
<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">
第二步 进行属性注入,在 bean 标签里面进行操作
<!--2 set 方法注入属性-->
<bean id="book" class="com.sevattal.spring.Book" p:bname="九阳神功" p:bauthor="无名氏">
</bean>
3、注入一些特殊类型的属性
注意:
1、property 标签在bean标签内部
2、若对类中的属性使用property注入,需要get和set方法
####(1)字面量
<!--null 值-->
<property name="address">
<null/>
</property>
####(2)属性值包含特殊符号
<!--属性值包含特殊符号
1 把<>进行转义 < >
2 把带特殊符号内容写到CDATA
-->
<property name="address">
<value><![CDATA[<<南京>>]]></value>
</property>
4、注入外部Bean
a. 创建两个类 service类 和 dao类
b. 在 service 调用 dao 里面的方法
public class UserService {
// 创建UserDao的对象,并设置set()方法
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void add(){
System.out.println("service add...");
// 1、原始方式:创建UserDao对象
/*UserDao userDao = new UserDaoImpl();
userDao.update(); */
}
}
public interface UserDao {
public void update();
}
public class UserDaoImpl implements UserDao {
@Override
public void update() {
}
}
c. 在 spring配置文件中进行配置
<!--1 service和dao的创建-->
<bean id="userService" class="com.sevattal.spring.service.UserService">
<!--注入UserDao的对象
name:属性值,类里面属性名称
ref:属性值,创建UserDao对象bean标签ID值
-->
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<bean id="userDaoImpl" class="com.sevattal.spring.dao.UserDaoImpl"/>
d. 测试
@Test
public void testBean(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService",UserService.class);
userService.add();
}
5、注入内部Bean属性
####(1)一对多关系:部门和员工
一个部门有多个员工,一个员工属于一个部门部门是一,员工是多
####(2)在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示
public class Department {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
public String getDname() {
return dname;
}
@Override
public String toString() {
return "Department{" +
"dname='" + dname + '\'' +
'}';
}
}
public class Employee {
private String eName;
private String gender;
// 员工属于一个部门
private Department department;
public void setDepartment(Department department) {
this.department = department;
}
// 第二种写法新增
public Department getDepartment() {
return department;
}
public void seteName(String eName) {
this.eName = eName;
}
public void setGender(String gender) {
this.gender = gender;
}
public void show(){
System.out.println("eName:" + eName + ",department:" + department + ",gender" + gender);
}
}
####(3)在 spring配置文件中进行配置
第一种写法:
<bean id="employee" class="com.sevattal.spring.bean.Employee">
<!--普通属性-->
<property name="eName" value="Sevattal"/>
<property name="gender" value="男"/>
<!--设置对象属性-->
<property name="department">
<bean id="department" class="com.sevattal.spring.bean.Department">
<property name="dname" value="安保部门"/>
</bean>
</property>
</bean>
第二种写法:
<!--级联赋值-->
<bean id="employee" class="com.sevattal.spring.bean.Employee">
<!--普通属性-->
<property name="eName" value="Sevattal"/>
<property name="gender" value="男"/>
<!--级联赋值-->
<property name="department" ref="department"/>
<property name="department.dname" value="技术部"/>
</bean>
<bean id="department" class="com.sevattal.spring.bean.Department">
<property name="dname" value="财务部"/>
</bean>
(3)测试
@Test
public void testBeanEmp(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Employee employee = context.getBean("employee", Employee.class);
employee.show();
}
6、注入集合属性
(1)、注入数组类型属性
(2)、注入 List 集合类型属性
(3)、注入 Map 集合类型属性
public class Student {
private String[] courses;
private List<String> list;
private Map<String,String> map;
private Set<String> set;
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void show(){
System.out.println(Arrays.toString(courses) + list.toString() + map.toString() + set.toString() +courseList.toString());
}
}
public class Course {
private String cname; //课程名称
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
}
<!--集合类型的数据注入-->
<bean id="student" class="com.sevattal.spring.collection.Student">
<!--数据类型属性的注入-->
<property name="courses">
<array>
<value>Java</value>
<value>DataBase</value>
<value>SoftWare Theory</value>
</array>
</property>
<property name="list">
<list>
<value>Micah</value>
<value>Maruko</value>
<value>Mapper</value>
</list>
</property>
<property name="map">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="DATABASE" value="database"></entry>
<entry key="SOFTWARE THEORY" value="software theory"></entry>
</map>
</property>
<property name="set">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
</set>
</property>
<!--注入list集合类型,但list元素是对象的属性-->
<property name="courseList">
<list>
<ref bean="course1"/>
<ref bean="course2"/>
</list>
</property>
</bean>
<bean id="course1" class="com.sevattal.spring.collection.Course">
<property name="cname" value="Java"></property>
</bean>
<bean id="course2" class="com.sevattal.spring.collection.Course">
<property name="cname" value="C++"></property>
</bean>
@Test
public void testBean(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Student student = context.getBean("student", Student.class);
System.out.println(student);
student.show();
}
(4)、在集合里面设置对象类型值
<!--创建多个 course对象-->
<bean id="course1" class="com.sevattal.spring.collection.Course">
<property name="cname" value="Java"></property>
</bean>
<bean id="course2" class="com.sevattal.spring.collection.Course">
<property name="cname" value="C++"></property>
</bean>
<!--注入 list 集合类型,值是对象-->
<property name="courseList">
<list>
<refbean="course1"></ref>
<refbean="course2"></ref>
</list>
</property>
(5)把集合注入部分提取出来
a. 在 spring配置文件中引入名称空间 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">
b. 使用 util标签完成 list集合注入提取
<!--1、提取list集合类型属性的注入-->
<util:list id="booklist">
<value>Spring</value>
<value>Java</value>
<value>JVM Machine</value>
</util:list>
<!--2、提取list集合类型属性的注入使用
scope:(1)"prototype"多实例
(2)"singleton"单实例
-->
<bean id="book" class="com.sevattal.spring.collection.Book" scope="prototype">
<property name="list" ref="booklist"/>
</bean>
c. 测试
@Test
public void testBean(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Book book = context.getBean("book", Book.class);
System.out.println(book.getList());
}
三、FactoryBean
1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
普通 bean:在配置文件中定义 bean 类型就是返回类型
工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
2、 案例演示
第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
public class MyBean implements FactoryBean<Course> {
/**
* 定义返回bean
*/
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setcName("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
四、Spring Bean作用域
1、在 Spring 里面,你怎样定义类的作用域?
当定义一个 在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope 属性 必须设为 singleton。
2、singleton 和 prototype 区别
第一 singleton 单实例,prototype 多实例
第二 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象,设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,而是在调用 getBean 方法时创建多实例对象。
3、解释Spring支持的几种bean的作用域。
Spring框架支持以下五种bean的作用域:
**singleton :**bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request :将单个bean定义的范围限定为单个HTTP请求的生命周期; 也就是说,每个HTTP请求都有一个自己的bean实例,它是在单个bean定义的后面创建的。 仅在基于Web的Spring ApplicationContext上下文中有效。
session :将单个bean定义的作用域限定为HTTP会话的生命周期。 仅在web的Spring ApplicationContext上下文中有效。
**application :**将单个bean定义的作用域限定为ServletContext的生命周期。 仅在基于web的Spring ApplicationContext上下文中有效。
**websocket:**将单个bean定义的作用域限定为WebSocket的生命周期。 仅在基于web的Spring ApplicationContext上下文中有效。
4、 Spring框架中的单例bean是线程安全的吗?
不,Spring 框架中的单例 bean 不是线程安全的。
五、Spring Bean生命周期
1、Spring框架中bean的生命周期。
简介
Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一个实例,而不是每次都产生一个新的对象使用Singleton模式产生单一实例,在spring中,singleton属性默认是true,只有设定为false,则每次指定别名取得的Bean时都会产生一个新的实例,Spring只帮我们管理单例模式Bean的完整生命周期,对于prototype的bean,Spring在创建好交给使用者之后则不会再管理后续的生命周期。
图例
生命周期图如下:
也可以概括为:
图例说明
1、实例化一个Bean
2、按照Spring上下文对实例化的Bean进行配置,也就是IOC注入
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,传递的参数就是Spring配置文件中Bean的id值
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(BeanFactory),传递的是Spring工厂自身
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,
BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
Bean的生命周期
Spring 容器从 XML 文件中读取bean的定义,并实例化bean。
Spring 根据 bean 的定义填充所有的属性。
如果bean实现了 BeanNameAware 接口,Spring 传递 bean 的ID 到 setBeanName 方法。
如果Bean实现了 BeanFactoryAware 接口, Spring 传递 beanfactory 给 setBeanFactory 方法。
如果有任何与bean相关联的 BeanPostProcessors,Spring 会在 postProcesserBeforeInitialization() 方法内调用它们。
如果 bean 实现 IntializingBean 了,调用它的 afterPropertySet 方法,如果bean声明了初始化方法,调用此初始化方法。
如果有 BeanPostProcessors 和 bean 关联,这些 bean的postProcessAfterInitialization() 方法将被调用。
如果 bean 实现了 DisposableBean,它将调用 destroy() 方法。
2、哪些是重要的bean生命周期方法? 你能重载它们吗?
有两个重要的bean 生命周期方法,第一个是setup , 它是在容器加载bean的时候被调用。第二个方法是 teardown 它是在容器卸载类的时候被调用。
The bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。
六、Spring Bean自动装配
1、什么是bean装配?
装配,或 bean 装配是指在 Spring 容器中把 bean 组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。
2、什么是bean的自动装配?
Spring 容器能够自动装配相互合作的 bean ,这意味着容器不需要和配置,能通过 Bean 工厂自动处理 bean 之间的协作。
3、解释不同方式的自动装配 。
有五种自动装配的方式,可以用来指导Spring容器用自动装配方式来进行依赖注入。
no:默认的方式是不进行自动装配,通过显式设置ref 属性来进行装配。
byName:通过参数名自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byname,之后容器试图匹配、装配和该bean的属性具有相同名字的bean。
byType::通过参数类型自动装配,Spring容器在配置文件中发现bean的autowire属性被设置成byType,之后容器试图匹配、装配和该bean的属性具有相同类型的bean。如果有多个bean符合条件,则抛出错误。
constructor:这个方式类似于byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
autodetect:首先尝试使用constructor来自动装配,如果无法工作,则使用byType方式。
4、自动装配有哪些局限性 ?
自动装配的局限性是:
重写: 你仍需用 和 配置来定义依赖,意味着总要重写自动装配。
基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。
5、你可以在Spring中注入一个 null 和 一个空字符串吗?
可以。
七、Spring注解
1、什么是基于Java的Spring注解配置? 给一些注解的例子.
基于Java的配置,允许你在少量的Java注解的帮助下,进行你的大部分Spring配置而非通过XML文件。
以 @Configuration 注解为例,它用来标记类可以当做一个bean的定义,被Spring IOC容器使用。另一个例子是@Bean注解,它表示此方法将要返回一个对象,作为一个bean注册进Spring应用上下文。
2、Spring 针对 Bean 管理中创建对象提供注解
(1) @Component
(2) @Service
(3) @Controller
(4) @Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例
3、什么是基于注解的容器配置?
相对于XML文件,注解型的配置依赖于通过字节码元数据装配组件,而非尖括号的声明。
开发者通过在相应的类,方法或属性上使用注解的方式,直接组件类中进行配置,而不是使用xml表述bean的装配关系。
4、怎样开启注解装配?
注解装配在默认情况下是不开启的,为了使用注解装配,我们必须在Spring配置文件中配置 context:annotation-config/元素。
5、@Required 注解
这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException。
6、@Autowired 注解
@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。
7、@Qualifier 注解
当有多个相同类型的 bean 却只有一个需要自动装配时,将@Qualifier 注解和@Autowire 注解结合使用以消除这种混淆,指定需要装配的确切的bean。
8、@Resource 注解
可以根据类型注入,可以根据名称注入
10、@Value 注解
注入普通类型属性
11、案例演示
Step 1: 引入依赖 spring-aop-5.3.7.jar
Step 2: 开启组件扫描( beans 中要添加 context命名空间 )
<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">
<!--开启组件扫描
1、如果扫描多个包,就用逗号隔开
2、dao 和 service,扫描包上层目录
-->
<context:component-scan base-package="com.sevattal"/>
Step 3: 创建类,在类上面添加创建对象注解
// value可以不写,默认是首字母小写的类名
@Service(value = "userService") //<bean id="userService" class=""/>
public class UserService {
// 注入普通属性
@Value(value = "Sevattal")
private String name;
/**
* 定义dao类型属性,不需要添加set方法,添加注入属性注解
* @Autowired : 根据类型注入
* @Qualifier(value = "userDaoImplOne") : 和上面@Autowired一起配合使用
*/
@Autowired
@Qualifier(value = "userDaoImplOne")
// @Resource 根据类型注入
// @Resource(name = "userDaoImplOne") 根据名称注入
private UserDao userDao;
public void add(){
System.out.println("add..." + name);
userDao.add();
}
}
@Repository(value = "userDaoImplOne")
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("dao add ... ");
}
}
public interface UserDao {
public void add();
}
Step 4: 新建配置类,替代xml文件
@Configuration // 作为配置类,替代xml文件
@ComponentScan(basePackages = {"com.sevattal"})
public class SpringConfig {
}
Step 5: 测试
// 添加SpringConfig之前
@Test
public void testService(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
// 添加SpringConfig之后
@Test
public void testService2(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
结果: