1.Spring
1.1 简介
- Spring:春天 ==>给软件行业带来了春天
- 2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架
- 2004年,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版
- Rod Jahnson是悉尼大学的音乐学博士,专业不是计算机
- Spring理念:使现有技术更加实用,本身就是一个大杂烩,整合现有的框架技术
官方下载地址:repo.spring.io
GitHub:https://github.com/spring-projects
Maven包:
- 使用Spring
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
- Spring整合jdbc
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>
1.2 优点
- Spring是一个开源免费的框架,容器
- Spring是一个轻量级的框架,非侵入式的
- 核心特点:控制反转IOC,面向切面AOP
- 对事务支持,对框架整合的支持
一句话概括:Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器(框架)
1.3 组成
Spring框架是一个分层架构,由7个定义良好的模块组成。
Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理bean的方式。
组成Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
-
**核心容器:**核心容器提供Spring框架的基本功能。核心容器的主要组件是
BeanFactory
,它是工厂模式的实现。BeanFactory
使用控制反转(IOC)模式将应用程序的配置和依赖性规范与世纪的应用程序代码分开。 -
**Spring上下文:**Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,例如JNDI、EJB、电子邮件、国际化、校验和调度功能。
-
**Spring AOP:**通过配置管理特性,Spring AOP模块直接将面向切面的编程功能,继承到了Spring框架中。所以,很容易地使Spring框架管理任何支持AOP的对象。Spring AOP模块为基于Spring的应用程序中的对象提供了事务管理服务。通过使用Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
-
**Spring DAO:**JDBC DAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误信息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO的面向JDBC的异常遵从通用的DAO异常层次结构。
-
**Spring ORM:**Spring框架插入了若干个ORM框架,从而提供了ORM的对象关系工具,其中包括JDO、HIbernate和iBatis SQL Map。所有这些都遵从Spring的通用事务和DAO异常层次结构。
-
**Spring Web模块:**Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。所以,Spring框架支持与Jakarta Struts的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
-
**Spring MVC框架:**MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的,MVC容纳了大量视图技术,其中包括JSP、Velocity、Tiles、iText和POI。
1.4 拓展
Spring Boot:
- 一个快速开发的脚手架;
- 基于Spring Boot可以快速开发单个微服务;
- 使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置;
Spring Cloud:
- Spring Cloud是基于Spring Boot实现的;
- Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;
- Spring Cloud很大的一部分是基于Spring Boot来实现,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。
- Spring Boot在Spring Cloud中起到了承上启下的作用,学习Spring Cloud必须学习Spring Boot.
Spring弊端:发展了太久之后,违背了原来的理念!配置十分的繁琐,人称:“配置地狱!”
2.IOC理论基础
新建一个空白的maven项目
2.1分析实现
1.原来的MVC方式写一段代码
(1)UserDao接口(dao层)
public interface UserDao {
public void getUser();
}
(2)UserDaoImpl实现类
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("获取用户数据");
}
}
(3)UserService接口(service层)
public interface UserService {
public void getUser();
}
(4)UserServiceImpl实现类
public class UserServiceImpl implements UserService {
//在service层关联dao层对象
private UserDao userDao=new UserDaoOracleImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
(5)测试(控制层)
public class MyTest {
@Test
public void getUser(){
UserService userService = new UserServiceImpl();
userService.getUser();
}
}
2.dao层增加接口,产生问题
(1)增加一个UserDao的实现类UserDaoMysqlImpl:
public class UserDaoMysqlImpl implements UserDao{
@Override
public void getUser() {
System.out.println("Mysql获取数据");
}
}
紧接着我们想要使用MySql的话,我们就需要去service实现类中修改对应的实现
public class UserServiceImpl implements UserService {
//在service层关联dao层对象,修改new对象
private UserDao userDao=new UserDaoOracleImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
(2)再假设,我们再增加一个UserDao的实现类UserDaoOracleImpl
public class UserDaoOracleImpl implements UserDao{
@Override
public void getUser() {
System.out.println("Oracle获取数据");
}
}
要想使用Oracle,也需要在service实现类中修改对应的new对象实现
public class UserDaoOracleImpl implements UserService {
//在service层关联dao层对象,修改new对象
private UserDao userDao=new UserDaoOracleImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
问题:
-
如果我们有大量的类似的需求,dao层一个接口有许多实现类,用户访问就需要修改程序(service层引用的dao层实现类)
-
每次变动都需要修改大量的代码,这种设计的耦合性太高了,牵一发而动全身
3.问题解决【重点】
我们可以在service层中用到dao层的地方不去实现它,而是留一个接口,利用set。我们去代码里修改下:
public class UserServiceImpl implements UserService {
//在service层关联dao层对象
private UserDao userDao;
//【革命性变化】在service层中利用到的dao层接口实现类经常变化,所以不去实现它,而是预留一个接口
public void setUserDao(UserDao userDao){
this.userDao=userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
测试:
@Test
public void getUser(){
UserServiceImpl userService = new UserServiceImpl();
//Mysql实现
userService.setUserDao(new UserDaoMysqlImpl());
userService.getUser();
//Oracle实现
userService.setUserDao(new UserDaoOracleImpl());
userService.getUser();
}
**区别:**已经发生了根本性的变化
- 之前程序主动创建对象,控制权在程序员手上(如service层引用实现dao层实现类对象)
- 使用了set注入以后,程序已经不再具有主动性,而是变成了被动的接受对象。把主动权交给了调用者,程序不用去管怎么创建,怎么实现了,它只负责提供一个接口。
这种思想,从本质上解决了问题,我们程序员不再去管理对象的创建了
系统的耦合性大大降低,可以更加专注的在业务上,这也就是IOC的原型!
2.2IOC的本质
- 控制反转IOC(Inversion of Control),是一种思想(不去具体实现,而是提供set接口),DI(依赖注入)是实现IOC的一种方法;
- 没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制;
- 控制反转后,将对象的创建转移给第三方;
- 控制反转(IOC)即使获取依赖对象的方式反转了。
IOC是Spring框架的核心内容,使用多种方式完美的实现了IOC,可以使用XML配置,也可以实用注解,新版本的Spring也可以零配置实现IOC;
- Spring容器在初始化时先读取配置文件,根据配置文件或元数据“创建与组织对象“存入容器中;
- 程序使用时再从IOC容器中取出需要的对象。
-
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的;
-
而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到零配置的目的。
-
控制反转是一种通过描述(XML或者注解),并通过第三方去生产或获取特定对象的方式。
-
在Spring中实现控制反转的是IOC容器,其实现方法时依赖注入(Dependency Injectiion,DI)
3.HelloSpring
3.1导入jar包
注 : spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 。
<!--Spring-webmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
3.2 编写代码
1.编写一个Hello实体类
package com.kuang.pojo;
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+name);
}
}
2.编写我们的spring文件,这里我们命名为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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象,由Spring创建和管理-->
<bean id="hello" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
3.测试
public void test(){
//解析beans.xml文件,生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean:参数即为Spring配置文件中bean的id
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
3.3思考
-
Hello对象时谁创建的?
hello对象是由Spring创建的
-
Hello对象的属性是怎么设置的?
hello对象的属性是由Spring容器设置的,这个过程就叫控制反转
-
控制:谁来控制对象的创建,传统应用程序是由程序本身控制创建的,使用Spring后,对象是由Spring来控制创建的
-
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:就是利用set方法来进行注入的。
IOC是一种编程思想,由主动地编程变成被动的接收
可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。
3.4修改案例一
我们在案例一中,邢增一个Spring配置文件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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象,由Spring创建和管理-->
<bean id="MysqlImpl" class="com.kuang.dao.UserDaoMysqlImpl">
</bean>
<bean id="OracleImpl" class="com.kuang.dao.UserDaoOracleImpl">
</bean>
<bean id="ServiceImpl" class="com.kuang.service.UserServiceImpl">
<!--注意:这里的name并不是属性,而是set方法后面的那部分,首字母小写-->
<!--引用另外一个bean,不是用value 而是用ref;基本类型用value-->
<property name="userDao" ref="MysqlImpl"/>
</bean>
</beans>
重新测试:
@Test
public void getUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
serviceImpl.getUser();
}
- OK,到了现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改。
- 所谓IOC,一句话搞定:对象由Spring来创建,管理,装配。
3.5我的理解
-
最开始dao层一个接口有多个实现类,service层引用接口并具体实现了其中一个实现类,用户(test)要想变更访问不同的dao层实现类,就需要去修改service层对dao层实现类的引用,从而需要修改了程序,主动权在程序手上,当此种需求较大时,程序修改起来很繁琐。
public class UserDaoOracleImpl implements UserService { //在service层关联dao层对象,修改new对象 private UserDao userDao=new UserDaoOracleImpl(); @Override public void getUser() { userDao.getUser(); } }
-
第一次修改:我们将service层对dao层实现类的引用,不去具体实现它,而是将接口变成属性,预留属性set接口。用户(test)想要使用哪个dao层实现类,只需要在test层调用serviceImpl时传入对应的dao层实现类对象,主动权在用户手上。
public class UserServiceImpl implements UserService { //在service层关联dao层对象 private UserDao userDao; //【革命性变化】在service层中利用到的dao层接口实现类经常变化,所以不去实现它,而是预留一个接口 public void setUserDao(UserDao userDao){ this.userDao=userDao; } @Override public void getUser() { userDao.getUser(); } }
@Test public void getUser(){ UserServiceImpl userService = new UserServiceImpl(); //Mysql实现 userService.setUserDao(new UserDaoMysqlImpl()); userService.getUser(); //Oracle实现 userService.setUserDao(new UserDaoOracleImpl()); userService.getUser(); }
-
使用Spring:
- 项目中的每一个类都去beans.xml中注册
- 每个类的属性也都在注册时传入设置(注入),包括基本类型属性和引用类型属性(引用其他的对象)
- 在项目构建的时候,Spring框架就会把所有的类的对象都构建出来,并且整个Spring中所有的对象只保留一份
- 在使用时(test)通过
new ClassPathXmlApplicationContext("beans.xml");
获得Spring创建和管理的所有对象 - 要想修改service层中对dao层对象的引用,可以直接修改beans.xml配置文件,而无需修改程序。
- 控制:所有对象由Spring创建和管理
- 反转:通过beans.xml可以配置每个对象的属性及其引用的其他对象,选择权和主动权在用户手上(假设用户可以间接操作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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象,由Spring创建和管理-->
<bean id="MysqlImpl" class="com.kuang.dao.UserDaoMysqlImpl">
</bean>
<bean id="OracleImpl" class="com.kuang.dao.UserDaoOracleImpl">
</bean>
<bean id="ServiceImpl" class="com.kuang.service.UserServiceImpl">
<!--注意:这里的name并不是属性,而是set方法后面的那部分,首字母小写-->
<!--引用另外一个bean,不是用value 而是用ref;基本类型用value-->
<property name="userDao" ref="MysqlImpl"/>
</bean>
</beans>
test无需修改,只需修改上面beans.xml配置文件
@Test
public void getUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
serviceImpl.getUser();
}
4.IOC创建对象方式
4.1通过无参构造方法来创建
1.实体类User
public class User {
private String name;
public User(){
System.out.println("user无参构造方法");
}
public void setName(String name){
this.name=name;
}
public void show(){
System.out.println("name="+name);
}
}
2.Spring配置文件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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象,由Spring创建和管理-->
<bean id="user" class="com.kuang.pojo.User">
<property name="name" value="kuangshen"/>
</bean>
</beans>
3.断点测试
测试结果:
-
当测试类通过
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
将配置文件beans.xml加载到Spring中执行时,获得Spring的上下文环境。此时已经初始化了所有管理的对象,User对象已经通过无参构造初始化了。
-
该操作中Spring就已经创建和管理了所有在beans.xml中注册配置的对象。
-
对象属性的配置,还是通过属性对应的set方法进行的。
删除User中
setName
方法后,beans.xml配置类的参数会报错。
4.2通过有参构造方法来创建
1.实体类User
public class User {
private String name;
//有参构造方法
public User(String name){
this.name=name;
System.out.println("user有参构造方法");
}
public void setName(String name){
this.name=name;
}
public void show(){
System.out.println("name="+name);
}
}
2.beans.xml的三种方式编写
- 此时不能在通过
<property name="name" value="kuangshen"/>
初始化实体类User了,因为那只有是无参构造方法时才能创建 - 有参构造方法,采用
<constructor-arg name="name" value="kuangshen"/>
配置属性
根据参数名字设置【常用】,后两种不常用
<!-- 第一种根据参数名字设置 【常用】 -->
<bean id="user" class="com.kuang.pojo.User">
<constructor-arg name="name" value="kuangshen"/>
</bean>
<!-- 第二种根据index参数下标设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
<!-- index指构造方法 , 下标从0开始 -->
<constructor-arg index="0" value="kuangshen2"/>
</bean>
<!-- 第三种根据参数类型设置 -->
<bean id="userT" class="com.kuang.pojo.UserT">
<constructor-arg type="java.lang.String" value="kuangshen2"/>
</bean>
3.测试
-
同样也是Spring的context一旦获得,即配置文件加载的时候,所有的实体类都初始化了
-
无论是通过无参构造,此时依靠set方法注入
-
还是通过有参构造,此时通过有参构造直接注入
-
可以某些属性通过有参构造,某些属性通过set注入
<bean id="user" class="com.kuang.pojo.User"> <!--name属性通过有参构造,无需set方法--> <constructor-arg name="name" value="kuangshen"/> <!--age属性通过没有在有参构造中初始化,set方法必须有--> <property name="age" value="12"/> </bean>
5.Spring配置
5.1别名
alias 为bean设置别名,可以设置多个别名
<bean id="user" class="com.kuang.pojo.User">
<property name="name" value="kuangshen"/>
</bean>
<!--为bean设置别名:在测试时获取Bean的时候可以使用别名获取-->
<alias name="user" alias="usernew"/>
5.2Bean的配置
<!--bean就是java对象,由Spring容器创建和管理
id:是bean的唯一标志符,如果没有配置id,name就是默认标志符
class:bean对象所对应的全限定名=包名+类名
name:可以为bean设置多个别名,可以用逗号,分号,空格隔开
-->
<bean id="user" class="com.kuang.pojo.User" name="u1,u2 u3;u4">
<property name="name" value="kuangshen"/>
</bean>
5.3import
- import,一般用于团队开发使用,可以在一个配置文件中导入其他配置文件,从而合并成一个
- beans.xml正式名为
applicationContext.xml
- 假如项目中有多个人开发,不同人负责不同的类的开发,不同的类需要注册在不同的beans.xml中,此时我们可以利用import将所有人的beans.xml合并为一个总的。
- 使用的时候,直接使用总的配置就可以了
如:beans1.xml、beans2.xml、beans3.xml ==> applicationContext.xml
applicationContext.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">
<!--总的xml,合并其他beans.xml文件-->
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
</beans>
测试:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
6.依赖注入(重点)
- **依赖:**bean对象的创建依赖于容器
- 注入: bean对象中的所有属性,由容器注入,简而言之,就是为对象中属性赋值
6.1构造器注入(前面已讲)
无参构造方法注入==》就是利用set方法注入
有参构造方法注入
6.2Set方法注入
set方法注入的类必须要有无参构造方法
测试对象:
Address类:
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
Student类:
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbies;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
//无参构造
public Student() {
}
//有参构造
public Student(String name, Address address, String[] books, List<String> hobbies, Map<String, String> card, Set<String> games, String wife, Properties info) {
this.name = name;
this.address = address;
this.books = books;
this.hobbies = hobbies;
this.card = card;
this.games = games;
this.wife = wife;
this.info = info;
}
//getter和setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
//toString
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbies=" + hobbies +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
beans.xml中类注册和属性注入
注入属性分类:普通注入value、bean注入ref、array、list、map、set、null、properties
<?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">
<!--注册Address类-->
<bean id="address" class="com.kuang.pojo.Address">
<property name="address" value="北京"/>
</bean>
<!--注册Student类-->
<bean id="student" class="com.kuang.pojo.Student">
<!--1 普通注入,value-->
<property name="name" value="小明"/>
<!--2 bean注入,ref-->
<property name="address" ref="address"/>
<!--3 数组,array-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>三国演义</value>
<value>水浒传</value>
</array>
</property>
<!--4 List,list-->
<property name="hobbies">
<list>
<value>听歌</value>
<value>敲代码</value>
<value>看小姐姐</value>
<value>看电影</value>
</list>
</property>
<!--5 Map,map-->
<property name="card">
<map>
<entry key="身份证" value="12345678"/>
<entry key="银行卡" value="87654321"/>
</map>
</property>
<!--6 Set,set-->
<property name="games">
<set>
<value>马里奥</value>
<value>魂斗罗</value>
<value>坦克大战</value>
</set>
</property>
<!--7 null和空字符串不一样-->
<property name="wife">
<null/>
</property>
<!--8 properties-->
<property name="info">
<props>
<prop key="driver">20210710</prop>
<prop key="url">man</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
测试:
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
}
测试结果:
Student{
name='小明',
address=Address{address='北京'},
books=[红楼梦, 西游记, 三国演义, 水浒传],
hobbies=[听歌, 敲代码, 看小姐姐, 看电影],
card={
身份证=12345678,
银行卡=87654321
},
games=[马里奥, 魂斗罗, 坦克大战],
wife='null',
info={
password=123456,
url=man,
driver=20210710,
username=root
}
}
6.3拓展方式注入(p命名空间注入、c命名空间注入)
本质:
- p命名空间注入,还是利用无参构造方法注入(实体类中需要有无参构造方法)
- c命名空间注入,是利用有参构造方法注入(实体类中需要有有参构造方法)
1.P命名空间注入 : 需要在头文件中加上约束文件
导入约束 : xmlns:p="http://www.springframework.org/schema/p"
<!--P(属性:properties)命名空间,属性本质上依然是无参构造方法通过set注入-->
<bean id="student" class="com.kuang.pojo.Student" p:name="小明"/>
2.c命名空间注入 : 需要在头文件中加上约束文件
导入约束 : xmlns:c="http://www.springframework.org/schema/c"
<!--C(构造:Constructor)命名空间,属性本质上依然是有参构造方法注入,可以不需要set方法-->
<bean id="student" class="com.kuang.pojo.Student" p:name="小明"/>
属性注入小结
- 本质上还是无参构造方法属性注入和有参构造方法注入
- **无参构造方法注入:**即通过set方法注入,所以通过set方法注入时类中一定要有无参构造方法,但默认存在
- **有参构造方法注入:**直接通过有参构造方法属性赋值注入,所以必须要重写有参构造方法;注意此时不要忘了显式添加一个无参构造方法,以适应set方法注入
- p命名空间和c命名空间注入,本质上还是使用无参构造方法和有参构造方法注入
6.4bean的作用域
bean为每个类创建的对象,六种作用域:
1.单例模式(Spring默认机制:每次从容器中getBean的时候,拿到的是同一个对象)
beans.xml:
<bean id="user" class="com.kuang.pojo.User" scope="singleton">
test:
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user1 = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user1==user2); //true
}
2.原型模式(每次从容器中getBean的时候,都会产生一个新的对象)
beans.xml:
<bean id="user" class="com.kuang.pojo.User" scope="prototype">
test:
System.out.println(user1==user2); //false
3.其余的request、session、application这些都只能在web开发中使用到!
7.引用的Bean的自动装配
- 自动装配是使用spring满足bean依赖的一种方法
- spring会在应用上下文中为某个bean寻找其依赖的bean依赖,即某个对象中引用的其他对象不用显式注入,spring可以实现自动注入。
Spring中bean中引用bean有三种装配机制,分别是
-
在xml中显式配置;
-
在java中显式配置;
-
隐式的bean发现机制和自动装配(本节研究的)
- 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
- 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IOC/DI
组件扫描和自动装配,使得显式的配置降低到最少。
实际应用中不推荐使用自动装配xml配置,而是使用注解装配。
测试环境搭建(XML显示装配引用bean)
1.新建一个项目
2.新建两个实体类,Cat、Dog都有一个叫的方法
public class Cat {
public void shout() {
System.out.println("miao~");
}
}
public class Dog {
public void shout() {
System.out.println("wang~");
}
}
3.新建一个拥有者类People
package com.kuang.pojo;
import lombok.Data;
public class People {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
4.编写Spring配置文件beans.xml**【在xml中显式配置引用的bean】**
<?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="cat" class="com.kuang.pojo.Cat"></bean>
<bean id="dog" class="com.kuang.pojo.Dog"></bean>
<bean id="people" class="com.kuang.pojo.People">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="name" value="qinjiang"/>
</bean>
</beans>
5.测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people",People.class);
people.getCat().shout();
people.getDog().shout();
}
结果正常输出,环境ok
7.1byName、byType自动装配
1.byName方式自动装配
-
autowire="byName"
按名称自动装配 -
测试:
1.修改bean配置,增加一个属性
autowire="byName"
<bean id="cat" class="com.kuang.pojo.Cat"></bean> <bean id="dog" class="com.kuang.pojo.Dog"></bean> <bean id="people" class="com.kuang.pojo.People" autowire="byName"> <property name="name" value="qinjiang"/> </bean>
2.再次测试,结果依旧成功输出
3.如果将cat的id修改,和people属性名称不一致,此时按名称自动装配失败,空指针异常。
因为byname规则实际上是按people属性的set方法找对应的bean去装配,此时找不到。
<bean id="cat111" class="com.kuang.pojo.Cat"></bean>
-
byName装配规则:
- 查找其类中所有引用的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat
- 去spring容器中寻找是否有此字符串名称id的对象
- 如果有,就取出注入,否则就报空指针异常
2.byType方式自动装配
-
autowire="byType"
按类型自动装配使用时需要保证:同一类型的对象,在spring容器中唯一。否则会报不唯一异常。
-
测试:
-
修改bean配置,增加一个属性
autowire="byType"
-
测试,正常输出
-
此时被引用的bean的id名称无所谓了,可以去掉,因为是按照对象类型来寻找并自动装配的。
<bean id="cat22" class="com.kuang.pojo.Cat"></bean> <bean id="dog22" class="com.kuang.pojo.Dog"></bean> <bean id="people" class="com.kuang.pojo.People" autowire="byType"> <property name="name" value="qinjiang"/> </bean>
-
7.2 使用注解实现自动装配
jdk1.5开始支持注解,spring2.5全面支持注解
准备工作:
-
在beans.xml配置文件中引入context文件头,注意学会修改方式配置文件
xmlns:context="http://www.springframework.org/schema/context" 【注意开头加上context】 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
-
beans.xml中开启属性 注解支持
<context:annotation-config/>
<?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">
<context:annotation-config/>
<bean id="cat" class="com.kuang.pojo.Cat"></bean>
<bean id="dog" class="com.kuang.pojo.Dog"></bean>
<bean id="people" class="com.kuang.pojo.People">
<property name="name" value="qinjiang"/>
</bean>
</beans>
1.在实体类属性上采用@Autowired注解【推荐】
-
@Autowired
是按照类型自动寻找匹配,不支持id名字匹配public class People { @Autowired private Cat cat; @Autowired private Dog dog; private String name; }
-
@Autowired(required=false)
使用,false表示注解标记的对象可以为null,true不能为空必须有对象如在没有注册cat的bean时,people仍能成功注入,只是此时cat引用对象可以为空
@Autowired(required = false) private Cat cat;
-
可以辅助使用
@Qualifier(value = "dog2")
,进而可以通过id名字匹配@Autowired @Qualifier(value = "dog2") private Dog dog;
2.在实体类属性上采用@Resource注解
-
@Resource
:如有指定name属性,按照先按照指定id名称查找匹配@Resource(name = "dog2") private Dog dog;
-
再进行默认的byName方式查找装配
@Resource private Dog dog;
-
以上都不成功,则按byType方式自动装配
@Autowired与@Resource异同:
- @Autowired:
- 默认按类型装配(属于spring规范)
- 默认情况下必须要求依赖对象存在,如果要允许null值,可以设置@Autowired(required=false)
- 可以结合@Qualifier(value = “dog2”),使用按id名称查找装配
- @Resource:
- 默认按名称装配
- 名称可以通过name属性进行指定
- 如果 没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找
- 当找不到与名称匹配的bean时才按照类型进行装配
- 它们作用相同,都是用注解方式注入对象,实现自动装配。但执行顺序不同:@Autowired先byType,@Resource先 byName。
8.使用注解开发注册bean和属性注入
- 在Spring4之后,要是使用注解,必须保证aop的包的导入,导入Spring-webmvc会自动导入aop包
- 使用注解需要导入contex约束,在增加对注解的支持
8.1 传统注解
1.在类中采用@Component
注解取代xml中的<bean>
需要在beans.xml中配置扫描哪些包下的注解
<context:component-scan base-package="com.kuang.pojo"/>
2.普通属性注入采用@Value
3.引用类型属性注入采用@Autowired
-
类对象
Cat:
@Component public class Cat { public void bark(){ System.out.println("miao~"); } }
User:
//类bean注册采用注解替代 @Component public class User { //普通属性注入 @Value("kuangshen") private String name; //引用类型属性 bean注入 @Autowired private Cat cat; }
等价于原来beans.xml中:
<bean id="cat" class="com.kuang.pojo.Cat"/> <bean id="user" class="com.kuang.pojo.User"> <property name="name" value="kuangshen"/> <property name="cat" ref="cat"/> </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" 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"> <context:annotation-config/> <context:component-scan base-package="com.kuang.pojo"/> </beans>
-
测试
public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User user = (User) context.getBean("user"); System.out.println(user.getName()); user.getCat().bark(); }
@Component别名:
开发中我们通常采用MVC三层架构
为了区分在不同的层中对其实现类,在Spring中进行注册装配bean
@Component有一些别名,功能都一样:都是注册bean对象
- @Repository:dao层
- @Service:service层
- @Controller:controller层
写上这些注解,就相当于将这个类交给Spring管理装配了!
4.作用域注解实现@Scope
一般在具体类前@Component
下跟随书写
- singleton:默认的,Spring会采用单例模式创建这个对象
- prototype:原型模式,多例模式
//类bean注册采用注解替代
@Component
@Scope("singleton")
public class User {
...
}
小结 xml与注解比较
xml与注解 实现注册bean和注入属性 比较:
- XML可以适用于任何场景,结构清晰,维护方便。适用于复杂类
- 注解不是自己提供的类使用不了,开发方便。适用于简单类
**xml与注解 整合开发:**推荐做法
- xml注册管理bean
- 注解完成属性注入
- 使用过程中,可以不用扫描,扫描是为了类上的注解
8.2采用Java配置类 取代 beans.xml配置文件
-
JavaConfig原来是基于Spring的一个子项目,它通过Java类的方式提供Bean的定义信息
-
在Spring4的版本,JavaConfig正式成为Spring4的核心功能
-
采用此种配置方法,可以完全摒弃beans.xml配置文件
-
编写实体类User
仍然采用注解注册bean,和进行属性注入
//注解实现bean注入,可以不需要,因为在MyConfig配置类中注册了bean @Component public class User { //属性注入 @Value("kuangshen") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
在java的com.kuang下新建一个config的包,编写一个MyConfig配置类【关键】
//用MyConfig的Java类取代配置文件beans.xml配置文件 @Configuration //注解扫描包声明 @ComponentScan("com.kuang.pojo") public class MyConfig { //用Java返回函数取代beans.xml中类的bean注册。方法名:等于bean中的id @Bean public User user(){ return new User(); } }
-
测试
需要采用
new AnnotationConfigApplicationContext(MyConfig.class);
获取Spring中管理的对象@Test public void test(){ ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); User user = (User) context.getBean("user"); System.out.println(user.getName()); }
这种纯Java的配置模式,在SpringBoot中随处可见!