文章目录
1、Spring
1.1 简介:
- Spring:春天------> 给软件行业带来了春天!
- 2002,首次推出了Spring框架的雏形:interface21框架!
- Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于*2004年3月24日,*发布了1.0正式版。(这个框架历史悠久啊~)
- Rod Johnson ,Spring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
- spring理念:其本身是一个大杂烩,整合了现有的技术框架!目的是使现有的技术更加容易使用
常见架构:
- SSH:Strct2 + Spring + Hibernate
- SSM:SpringMVC + Spring + Mybatis
常用访问地址:
我们常使用Maven,在pom.xml中导入依赖就可完成下载了:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
1.2、优点:
- Spring是一个开源的免费的框架(容器)!
- Spring是一个轻量级的、非入侵式的框架!
- 控制反转(IOC) , 面向切面编程(AOP)!
- 支持事务的处理,对框架整合的支持!
一句话总结:Spring就是一个轻量级的控制反转(IOC) 和面向切面编程(AOP)的框架!
1.3、组成:
概述:
- 如果作为一个整体,这些模块为你提供了开发企业应用所需的一切。但你不必将应用完全基于Spring框架。你可以自由地挑选适合你的应用的模块而忽略其余的模块。
- 就像你所看到的,所有的Spring模块都是在核心容器之上构建的。容器:定义了Bean是如何创建、配置和管理的——更多的Spring细节。当你配置你的应用时,你会潜在地使用这些类。但是作为一名开发者,你最可能对影响容器所提供的服务的其它模块感兴趣。这些模块将会为你提供用于构建应用服务的框架,例如AOP和持久性。
核心容器
这是Spring框架最基础的部分,它提供了依赖注入 (DependencyInjection)特征来实现容器对Bean的管理。这里最基本的概念是BeanFactory,它是任何Spring应用的核心。BeanFactory是工厂模式的一个实现,它使用IoC将应用配置和依赖说明从实际的应用代码中分离出来。
1.5、扩展
在Spring的官网有这个介绍:现代化的Java开发!说白就是基于Spring的开发!
- Spring Boot:
- 一个快速开发的脚手架。
- 基于SpringBoot可以快速的开发单个微服务。
- 约定大于配置!
- Spring Cloud:SpringCloud 是基于SpringBoot实现的。
Spring家族的关系依赖:
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!,Spring在其中起到了承上启下的作用!
Spring 的弊端:
弊端:Spring从出现到现在为止,已经发展了太久~,违背了原来的理念!配置十分繁琐,人称:“ 配置地狱!”
2、IOC 理论推导:
核心理念:
IOC只是一种设计理念,很多程序的写法都实现了这种理念,比如:在函数的参数中传入了实现接口的具体的类,使得它们之间的关系,解耦,耦合度很低!
**控制反转:**把实现的权利交给了用户,由用户来控制运行流程,而很少改动逻辑的代码!
2.1、案例演示:
新建一个Maven项目结构如下;
功能:(构建这个项目结构实现了面向接口编程的思想!)
-
UserDao 接口
package edu.nwu.dao; public interface UserDao { public void getUser(); }
-
UserDaoImpl 实现类
package edu.nwu.dao.impl; import edu.nwu.dao.UserDao; public class UserDaoImpl implements UserDao { public void getUser() { System.out.println("获取用户数据"); } }
-
UserService 业务接口
package edu.nwu.service; public interface UserService { public void getUser(); }
-
UserServiceImpl 业务实现类
package edu.nwu.service.Impl; import edu.nwu.dao.UserDao; import edu.nwu.dao.impl.UserDaoImpl; import edu.nwu.dao.impl.UserDaoMysqlImpl; import edu.nwu.service.UserService; public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); public void getUser() { userDao.getUser(); } }
-
进行测试:
@Test public void test(){ UserDaoImpl userDao = new UserDaoImpl(); UserServiceImpl userService = new UserServiceImpl(); userService.getUser(); }
问题:这种架构下,如果我们需要把实现类换为:Mysql的,就得如下操作:
(1) 创建一个UserDao的Mysql接口实现:
package edu.nwu.dao.impl;
import edu.nwu.dao.UserDao;
public class UserDaoMysqlImpl implements UserDao {
public void getUser() {
System.out.println("Mysql获取用户数据");
}
}
(2) 改动业务实现类的代码:
package edu.nwu.service.Impl;
import edu.nwu.dao.UserDao;
import edu.nwu.dao.impl.UserDaoImpl;
import edu.nwu.dao.impl.UserDaoMysqlImpl;
import edu.nwu.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserMysqlDaoImpl();
public void getUser() {
userDao.getUser();
}
}
我们发现:在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价十分昂贵!(而且很多代码本身就不能过多地进行修改)
解决方法:我们在业务实现类中增加一个Set接口实现. 已经发生了革命性的变化!
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
测试类的写法:
@Test
public void test(){
UserServiceImpl userService = new UserServiceImpl();
UserDaoMysqlImpl userDaoMysql = new UserDaoMysqlImpl(); // 这里多了一步!
userService.setUserDao(userDaoMysql);
userService.getUser();
}
-------
结果:
Mysql获取用户数据
set方法应用后有什么区别?(核心)
-
这一处代码的改变,不仅仅是改变了调用的方法,更是改变了一种思想:依赖倒置原则,之前的关系是:业务类的实现依赖于userDaoImpl的实现,改动数据层的代码必然要改动服务层的代码。
-
现在业务类不再依赖于它了,只要传入任意一个实现了该接口方法的类即可,服务层原本依赖于数据层,现在服务层和数据层解耦了,改动数据层代码,服务层完全可以不做任何改动。
-
这才是正常的逻辑,高层不能与底层有太强的依赖,否则,引一发而动全身。
-
"控制反转"就是"依赖倒置"原则的一种代码设计的思路,在这里具体为:依赖注入!
-
依赖注入:简单来说,就是把底层类作为参数传入上层类,实现上层对下层类的"控制"。(本来是下层控制上层,现在被”反转“了!)
-
补充:这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了(而是由调用者实现)。系统的耦合性大大降低~,可以更加专注的在业务的实现上!这是IOC 的原型!
问题:Spring为何需要如此麻烦地配置文件?为何要把创建对象的权利交给Spring?自己创建对象不香吗?
- 我们发现不仅是方法设计的思维发生了变化,简化了开发的流程。
- 还有一个重要的点,客户端调用的一个方法的时候,它必须传入这个对象,当这个对象嵌套的层级比较浅的时候后,我们还受得了,当嵌套层级关系很深的时候,我们为了调用一个方法,需要先手动构造N个对象,每个对象的构造方法我们都得熟记于心,这简直是个灾难。
- 所以:Spring 用配置文件代替了我们生成对象的方式,在文件中注明依赖关系,如何调用等,以后就靠程序来自动加载配置文件来创建类的对象,将我们解放了出来!
问题:
Spring Bean到底是接管了new对象的过程,还是其中只有一个对象,大家获取的都是同一个对象?(因为默认的注入方式是:单例模式,所以产生的都是同一个对象!)
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Hello hello = (Hello) context.getBean("hello");
hello.show();
hello.setName("小波");
Hello hello2 = (Hello) context.getBean("hello");
System.out.println(hello == hello2);
hello2.show();
}
结果:只有一个对象,大家获取的都是同一个对象!
HelloSpring
true
Hello小波
2.2、IOC本质
表现:控制反转IoC(Inversion of Control):是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转的表现就是:获得依赖对象的方式反转了。
实质:是因为对象间(设计)的依赖关系发生了转变,从依赖关系变成了松耦合,所以才要创建对象来作为参数传递,但由于对象间可能存在极其复杂的嵌套关系(创建一个对象而需要创建N个参数对象),所以我们用配置文件搞定了这些关系后就固化下来这个创建对象的流程了,由Spring代为我们创建对象,我们只需要取对象就OK了。
配置文件和注解配置的区别:采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
小结:
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
目的:Spring的IOC就是为了更高效(针对开发者,很少改动代码,耦合性低)和快捷(针对调用者,不需要明白其中的依赖关系)地 ”创建对象“。(也就是帮我们new 对象!)
2.3、 IOC理解参考(无敌解释):
注意:以上IOC的理解绝大多数参考这篇知乎上大佬的文章:Spring IOC有何好处?
3、HelloSping (第一个Spring程序)
3.1、1. 导入Spring相关jar包
注 : spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 .
<!--junit是为了方便测试!-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--Spring框架包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
3.2、编写相关代码:
项目结构如下:
编写一个Hello实体类
package edu.nwu.pojo;
public class Hello {
private String name;
public Hello(){
System.out.println("调用无参构造函数!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("调用set方法~");
}
public void show(){
System.out.println("Hello"+name);
}
}
编写我们的Spring文件,这里命名为:beans.xml
Spring Bean是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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象 , 由Spring创建和管理-->
<bean id="hello" class="edu.nwu.pojo.Hello">
<!-- collaborators and configuration for this bean go here -->
<property name="name" value="Spring"/>
</bean>
</beans>
在这个beans.xml文件中,我们写明了需要被Spring容器托管的对象类,并给定了其相应的值(这种配置文件的写法是通过调用set方法传递进去的,如果Hello中没有set对应属性的方法,就会报错!
),然后我们就可以通过Spring来创建我们的对象了!
@Test
public void test2(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
System.out.println("====================");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
hello.show();
hello.setName("小波");
Hello hello2 = (Hello) context.getBean("Hello");
System.out.println(hello == hello2);
hello2.show();
}
输出:
调用无参构造函数!
调用set方法~
====================
HelloSpring
调用set方法~
true
Hello小波
通过结果我们发现了:
- 首先,在Spring启动的时候(此时还没执行业务逻辑),这些类的无参构造就被调用了,然后通过setxxx的方法,设置了对应属性的数值。
- 他们都是来自于同一个对象(IOC容器默认创建对象的模式:单例模式)
3.3、思考问题?
问题:Hello 对象是谁创建的 ?
回答:hello 对象是由Spring创建的。
问题:Hello 对象的属性是怎么设置的 ?
回答:hello 对象的属性是由Spring容器设置的 。
这个过程就叫控制反转 :
控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的 .
反转 : 表现为:程序本身不创建对象 , 而变成被动的接收对象 .(实际上是设计set方法的人的理念发生了转变,需要传入一个对象而不是一个简单的参数,而创建对象这件事被Spring接管了,所以我们成了被动获取对象,再进行逻辑处理对象的人了)
依赖注入 : (依赖的参数对象通过注入来实现)就是利用set方法来进行注入的.(当然还可以通过:构造器传入等办法来注入~)
IOC是一种编程思想 , 由主动的创建变成被动的接收 .(因为Spring在帮我们做这些复杂的事情,创建一个对象的依赖可能很多,他帮我们做了就OK了)
可以通过newClassPathXmlApplicationContext
去浏览一下底层源码 .
小结:
OK , 到了现在 , 我们彻底不用再程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC, 一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !
4、IOC对象创建(注入)的方式:
注意:IOC默认使用的是:无参构造函数创建对象,然后再Set值进去(需要被IOC托管的对象类实现了:setXXX的方法),我们3中的示例就是这种方式来注入对象的。
IOC还有另外的创建对象(注入)的方法:有参构造
有参构造中在xml配置文件中还有三种值的传递方式:(无参构造只有一种方法:通过名字来设置值)
1、参数名:(常用)
<!--第一种,直接通过参数名来设置-->
<bean id="user" class="edu.nwu.pojo.User">
<constructor-arg name="name" value="小波"/>
</bean>
2、类型:
<!--第二种方式:通过类型创建,不建议使用!-->
<bean id="user" class="com.kuang.pojo.User">
<constructor-arg type="java.lang.String" value="xiaobo"/>
</bean>
3、下标赋值:(不如第一种清晰明了~)
<!--第一种,下标赋值!-->
<bean id="user" class="edu.nwu.pojo.User">
<constructor-arg index="0" value="小哆"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!
5、Spring配置
注:Spring中有诸多的配置选项,每一种配置选项都是为了我们更简便或者更灵活地应用Spring。
5.1、别名:
例如,我们可以在beans.xml文件中这样写:
<alias name="hello" alias="hello2"/>
<bean id="hello" name="Hello" class="edu.nwu.pojo.Hello">
<!-- collaborators and configuration for this bean go here -->
<property name="name" value="Spring"/>
</bean>
其中:
- alias的别名:hello2
- 自别名:Hello
- 两个别名都可以当做id使用!
5.2、Bean的配置:
例如:
<bean id="user" class="edu.nwu.pojo.User" name="user2 u2,u3;u4">
<property name="name" value="小哆"/>
</bean>
解析:
- id : bean 的唯一标识符,也就是相当于我们学的对象名
- class : bean 对象所对应的全限定名 : 包名 + 类型
- name :也是别名,而且name 可以同时取多个别名
- Property : 对应调用类的无参构造函数,然后通过setXX方法赋值(有参构造可同理得~)
- name :对应类中定义的属性名
- Value:对应setXXX()传入的参数值。
5.3、import
功能:这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个配置文件(然后我们只调用这个合并的配置文件即可使用~)
假设:现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
示例:
Import.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<alias name="hello" alias="hello2"/>
<bean id="hello" class="edu.nwu.pojo.Hello">
<!-- collaborators and configuration for this bean go here -->
<property name="name" value="Spring"/>
</bean>
<!-- more bean definitions go here -->
</beans>
user.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" name="Dog" class="edu.nwu.entity.Dog"/>
<bean id="cat" name="Cat" class="edu.nwu.entity.Cat"/>
<bean id="user" name="User" class="edu.nwu.entity.Users">
<!-- collaborators and configuration for this bean go here -->
<property name="name" value="Spring"/>
</bean>
<!-- more bean definitions go here -->
</beans>
编写测试类测试:
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("Application.xml"); // 注意,这里使用的是谁~
System.out.println("====================");
Hello hello = (Hello) context.getBean("hello");
hello.show();
hello.setName("小波");
Hello hello2 = (Hello) context.getBean("hello");
System.out.println(hello == hello2);
hello2.show();
}
调用成功!
6、依赖注入
6.1、构造器注入:
前面的章节中有,这里就不再赘述了!
6.2、Set方式注入:
依赖注入:set注入(只是依赖注入的一种,还有构造器注入等~)
- 依赖:bean对象的创建依赖于容器!(感觉这个依赖写的差点儿味道~)
- 注入:bean对象中的所有属性(包括对象属性),由容器来注入!
环境搭建:
1、复杂类型:
Student类:
package edu.nwu.pojo;
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> list;
private Map<String, String> map;
private Set<String> sets;
private String wife;// null
private Properties info;
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSets() {
return sets;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setWife(String wife) {
this.wife = wife;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address.getAddress() +
", books=" + Arrays.toString(books) +
", list=" + list +
", map=" + map +
", set=" + sets +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
嵌套的属性对象类:Address
package edu.nwu.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2、Student.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student" name="Student" class="edu.nwu.pojo.Student">
<!--第一种,普通值注入,value-->
<property name="name" value="小波"/>
<!--第二种,Bean注入,ref-->
<property name="address" ref="address"/>
<!--数组-->
<property name="books">
<array>
<value>大话数据结构</value>
<value>Hadoop入门</value>
<value>剑指Offer</value>
</array>
</property>
<!--List-->
<property name="list">
<list>
<value>test1</value>
<value>test2</value>
</list>
</property>
<!--Map-->
<property name="map">
<map>
<entry key="key1" value="v1"/>
<entry key="key2" value="v2"/>
</map>
</property>
<!--Set-->
<property name="sets">
<set>
<value>list1</value>
<value>list2</value>
</set>
</property>
<!--null-->
<property name="wife">
<null></null>
</property>
<!--Properties-->
<property name="info">
<props>
<prop key="p1">hello1</prop>
<prop key="p2">hello2</prop>
</props>
</property>
</bean>
<bean id="address" class="edu.nwu.pojo.Address">
<property name="address" value="西安"/>
</bean>
<!-- more bean definitions go here -->
</beans>
Student类:
package edu.nwu.pojo;
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> list;
private Map<String, String> map;
private Set<String> sets;
private String wife;// null
private Properties info;
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSets() {
return sets;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setWife(String wife) {
this.wife = wife;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address.getAddress() +
", books=" + Arrays.toString(books) +
", list=" + list +
", map=" + map +
", set=" + sets +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
测试代码:
@Test
public void test4(){
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("Application.xml");
Student student = (Student) application.getBean("Student");
// System.out.println(student.toString());
System.out.println(student.hashCode());
Student student2 = (Student) application.getBean("Student");
System.out.println(student2.hashCode());
}
测试结果:
调用无参构造函数!(这是因为Application.xml中其他的对象也被构造且才显示的)
调用set方法~(这是因为Application.xml中其他的对象被初始化了才显示的)
Student{name='小波', address=西安, books=[大话数据结构, Hadoop入门, 剑指Offer], list=[test1, test2], map={key1=v1, key2=v2}, set=[list1, list2], wife='null', info={p2=hello2, p1=hello1}}
1107730949
1107730949
6.3、拓展方式注入
我们可以使用 p命令空间和c命令空间进行注入
官方解释:
使用!
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入,可以直接注入属性的值:property-->
<bean id="user" class="com.kuang.pojo.User" p:name="秦疆" p:age="18"/>
<!--c命名空间注入,通过构造器注入:construct-args-->
<bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="狂神"/>
</beans>
测试:
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2", User.class);
System.out.println(user);
}
注意点:p命名和c命名空间不能直接使用,需要导入xml约束!
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
6.4、bean的作用域
-
单例模式 (Spring默认机制)
<bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="狂神" scope="singleton"/>
-
原型模式:每次从容器中get的时候,都会产生一个新对象!
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
-
其余的 request、session、application、这些个只能在web开发中使用到!
7、Bean的自动装配
概念:
- 自动装配是Spring满足bean依赖一种方式!
- Spring会在上下文中自动寻找,并自动给bean中的属性装配其对应的数值!
- 简单来说:我们之前必须将一个对象(Bean标签)中的所有初始值(在配置文件中)都赋予好属性,然后才能让Spring通过setxx的方法进行设置,现在Spring可以自动的找到属性对应的set值了,不用我们在配置文件中写明了。(但仅针对类类型的属性)
7.1、测试环境:
环境:一个人有两个宠物:
Users类:
package edu.nwu.entity;
import org.springframework.beans.factory.annotation.Autowired;
public class Users {
private String name;
private Dog dog;
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Users{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
Dog类:
package edu.nwu.entity;
public class Dog {
public void shout(){
System.out.println("汪~");
}
}
Cat类:
package edu.nwu.entity;
public class Cat {
public void shout(){
System.out.println("喵~");
}
}
7.2、ByName的自动装配:
byName:
- 会自动在容器上下文中查找和自己对象set方法后面的值对应的 beanid!
- 很明显这种自动装配的属性必须是对象类型的,因为只有对象才能在一个Bean中,拥有Beanid
user.xml文件:(对应Users类)
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" name="Dog" class="edu.nwu.entity.Dog"/>
<bean id="cat" name="Cat" class="edu.nwu.entity.Cat"/>
<!-- byName: 会自动在容器上下文中查找,和自己对象set方法后面的值对应的 beanid!-->
<bean id="user" name="User" class="edu.nwu.entity.Users" autowire="byName">
<!-- collaborators and configuration for this bean go here -->
<property name="name" value="Spring"/>
</bean>
<!-- more bean definitions go here -->
</beans>
测试函数:
@Test
public void test5(){
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("Application.xml");
Users user = (Users) application.getBean("User");
System.out.println(user.toString());
user.getCat().shout();
user.getDog().shout();
}
测试结果:(发现其通过名称自动装配成功!)
调用无参构造函数!
调用set方法~
Users{name='Spring', dog=edu.nwu.entity.Dog@61d47554, cat=edu.nwu.entity.Cat@69b794e2}
喵~
汪~
7.3、ByType自动装配
byType:
- Spring会自动在容器上下文中查找,和自己对象属性类型相同的bean!
- 但由于名字重复的可能性比类型重复的可能性低一些, 所以我们一般都用名字(byName)来作为自动装配的。
user.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" name="Dog" class="edu.nwu.entity.Dog"/>
<bean id="cat" name="Cat" class="edu.nwu.entity.Cat"/>
<!--
byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!
-->
<bean id="user" name="User" class="edu.nwu.entity.Users" autowire="byType">
<!-- collaborators and configuration for this bean go here -->
<property name="name" value="Spring"/>
</bean>
<!-- more bean definitions go here -->
</beans>
小结:
- byname的时候,需要保证所有bean的id唯一,并且这个bean的id需要和自动注入的属性的set方法名的值一致!
- bytype的时候,需要保证所有bean的class唯一,并且这个bean的class需要和自动注入的属性的类型一致!
7.4 、使用注解实现自动装配
要使用注解须知:
-
导入约束 : context约束(这个最好在Application.xml中导入,这样就都能够用了)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
-
配置注解的支持 :context:annotation-config/ 【重要!】
在application.xml中:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置支持注解-->
<context:annotation-config/>
<import resource="import.xml"/>
<import resource="user.xml"/>
<import resource="Student.xml"/>
<!--下面还可以导入多个~-->
</beans>
在user.xml中:(什么都不用改动,将可以自动装配成功的属性标签不设置即可!,也不要开启自动装配)
user.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" name="Dog" class="edu.nwu.entity.Dog"/>
<bean id="cat" name="Cat" class="edu.nwu.entity.Cat"/>
<bean id="user" name="User" class="edu.nwu.entity.Users">
<!-- collaborators and configuration for this bean go here -->
<property name="name" value="Spring"/>
</bean>
<!-- more bean definitions go here -->
</beans>
@Autowired 使用方法:
- 直接在属性上(常用)使用即可!也可以在set方法上使用!
- 使用@Autowired注解修饰属性后,我们可以不用编写该属性对应的Set方法了
- 前提:是你这个自动装配的属性在 IOC(Spring)容器中存在,且符合名字byname!(尤其要注意:这个@Autowired模式是通过byType来寻找的,相同的byType下才会使用byName来锁定对应的bean)
Uses类:
package edu.nwu.entity;
import org.springframework.beans.factory.annotation.Autowired;
public class Users {
private String name;
@Autowired
private Dog dog;
@Autowired
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public Cat getCat() {
return cat;
}
@Override
public String toString() {
return "Users{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
测试代码:
@Test
public void test5(){
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("Application.xml");
Users user = (Users) application.getBean("User");
System.out.println(user.toString());
user.getCat().shout();
user.getDog().shout();
}
执行结果:(说明注解也生效了)
调用无参构造函数!
调用set方法~
Users{name='Spring', dog=edu.nwu.entity.Dog@4206a205, cat=edu.nwu.entity.Cat@29ba4338}
喵~
汪~
注意:
-
如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
....... @Autowired(required = false) private Cat cat; .......
-
@Nullable 字段标记了这个注解,说明这个字段可以为null;
-
这两者效果不太一样:
- @Nullable 是说可以让这个属性不实现自动装配,其值可以为null。
- @Autowired(required = false) 是说它去实现自动装配,但找不到对应的属性值恶话,可以为null。(找到了就不为null了)
- 否则的话,Spring在启动的时候就会报错,该对象无法注入成功
复杂情况下使用注解:
情况一:@Autowired无法找到唯一的Bean(属性名和beanid不一致)
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
比如:user.xml中有两个同样类型都为Cat的Bean,其id不同,但都跟属性名不一样,这时候就需要@Qualifier了
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" name="Dog" class="edu.nwu.entity.Dog"/>
<bean id="cat111" name="Cat" class="edu.nwu.entity.Cat"/>
<bean id="cat222" name="Cat2" class="edu.nwu.entity.Cat"/>
<bean id="user" name="User" class="edu.nwu.entity.Users">
<!-- collaborators and configuration for this bean go here -->
<property name="name" value="Spring"/>
</bean>
<!-- more bean definitions go here -->
</beans>
package edu.nwu.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import javax.annotation.Resource;
public class Users {
private String name;
@Autowired
private Dog dog;
@Autowired
@Qualifier(value = "cat111") // 在这里设定其查找的beanid
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public Cat getCat() {
return cat;
}
@Override
public String toString() {
return "Users{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
情况二:使用@Resouce注解(这个@Resource是Java自带的,不是Spring的)
public class People {
@Resource(name = "cat2") // 这里就跟@Qualifier(value="cat111")的效果一样!
private Cat cat;
@Resource
private Dog dog;
}
@Resouce和@Autowired的区别:
相同点:
- 都是用来实现自动的手段,都可以放在属性字段上。
不同点:
-
@Autowired
1、@Autowired注解是默认按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如:@Autowired(required=false)。如果找到多个相同类型的组件,再用属性作为bean的id去查容器。
2、如果我们想使用名称装配可以结合@Qualifier注解进行使用,从而不用属性名
-
@Resouces
如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
2、如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
3、如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
4、如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
8、使用纯注解开发:
环境依赖:
1、在Spring4之后,要使用注解开发,必须要保证 aop的包导入了(好在Maven会自动导入各种依赖,Maven下载的包中已经包含了这些东西了)
2、使用注解需要导入context约束,增加注解的支持!
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--指定要扫描的包,这个包下的注解就会生效(Compoment等等其他字段)-->
<context:component-scan base-package="com.kuang"/>
<!-- 开启用注解实现自动装配的-->
<context:annotation-config/>
</beans>
案例:
1、bean 标签如何自动写入配置文件中?(前提:开启包扫描)
在类名前面加:@Compoment
package edu.nwu.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class Users {
private String name;
@Autowired
private Dog dog;
@Autowired
@Qualifier(value = "cat111")
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public Cat getCat() {
return cat;
}
@Override
public String toString() {
return "Users{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
Compoment的效果:
- 就相当于在配置文件自动注册了一个Bean标签(此处的配置文件是指开启扫描包的配置文件,当然我们程序必须也指定这个扫描包的文件如:application.xml,否则是读取不到这个利用注解自动注册到配置文件中的bean的,如在:student.xml中就无法读取到)中写了一个bean标签。
- 这个自动生成的bean的id就是类名(首字符小写),当然也可以这样写:@Component(“User”),这样就指定了一个User为bean的id。
- 但是bean标签中还没有写明属性的注入值,注入的值需要通过自动装配和设置属性自动注入
2、属性如何自动注入?
@Value:
- 可以直接设置在属性上
- 也可以直接设置在set方法上
package edu.nwu.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class Users {
@Value("小波")
private String name;
@Autowired
private Dog dog;
@Autowired
@Qualifier(value = "cat111")
private Cat cat;
public String getName() {
return name;
}
@Value("小黑")
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public Cat getCat() {
return cat;
}
@Override
public String toString() {
return "Users{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
3、衍生的注解:
@Component 有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
-
dao 【@Repository】
-
service 【@Service】
-
controller 【@Controller】
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean。
4、自动装配设置:
- @Autowired :自动装配先通过类型匹配,类型一样再通过名字匹配。
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value=“xxx”),此处的"xxx"就是在配置文件中已经声明的了的标签。 - @Nullable :字段标记了这个注解,说明这个字段可以为null;
- @Resource (Java提供的自动注入注解):自动装配先通过名字。名字一样再用类型来判断。
5、作用域:
@Scope(“prototype”) :指定该类为原型模式,每个Spring生成的对象都不一样。
package edu.nwu.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Scope("prototype")
public class Users {
@Value("小波")
private String name;
@Autowired
private Dog dog;
@Autowired
@Qualifier(value = "cat111")
private Cat cat;
public String getName() {
return name;
}
@Value("小黑")
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public Cat getCat() {
return cat;
}
@Override
public String toString() {
return "Users{" +
"name='" + name + '\'' +
", dog=" + dog +
", cat=" + cat +
'}';
}
}
此时的user.xml文件:(我们看到,关于Users类的xml配置全都被注释掉了,这里还可以把Cat和Dog类也实现纯注解的形式来注入,这样user.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dog" name="Dog" class="edu.nwu.entity.Dog"/>
<bean id="cat111" name="Cat" class="edu.nwu.entity.Cat"/>
<bean id="cat222" name="Cat2" class="edu.nwu.entity.Cat"/>
<!--<bean id="user" name="User" class="edu.nwu.entity.Users">-->
<!--<!– collaborators and configuration for this bean go here –>-->
<!--<!–<property name="name" value="Spring"/>–>-->
<!--</bean>-->
<!-- more bean definitions go here -->
</beans>
测试代码:
@Test
public void test5(){
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("Application.xml");
Users user = (Users) application.getBean("users");
System.out.println(user.toString());
user.getCat().shout();
user.getDog().shout();
System.out.println("============");
Users user2 = (Users) application.getBean("users");
System.out.println(user == user2);
}
效果:
调用无参构造函数!
调用set方法~
Users{name='小黑', dog=edu.nwu.entity.Dog@2cbb3d47, cat=edu.nwu.entity.Cat@527e5409}
喵~
汪~
============
false
Xml 与 注解:
特点:
- xml:更加万能,适用于任何场合!维护简单方便
- 注解:不是自己类使用不了,维护相对复杂!
xml 与 注解最佳实践:
-
xml 用来管理bean;
-
注解只负责完成属性的注入;
-
我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持(下面二者最好都写在Application.xml中,每一个*.xml文件都注册在Application.xml中,我们使用的时候,只引入Application.xml即可!)
<!--指定要扫描的包,这个包下的注解就会生效--> <context:component-scan base-package="com.kuang"/> <context:annotation-config/>