1.Spring介绍
Spring其实就是一种开源框架,指的是Spring Framework,具有良好的生态,这也是Spring经久不衰的原因
用一句话概括,Spring就是一个集成了众多工具和方法的IOC容器
2.IOC容器
什么是IOC容器呢?
IOC的中文翻译过来就是控制反转,IOC容器其实就是控制反转容器
那什么是控制反转呢?
这个词听起来好像很高大上,其实通俗来理解就是,本来我们写java类的时候,创建对象,是通过自己去new的,但是当我们控制反转之后,我们就把对象的创建和一系列读取等操作交给了Spring,Spring就帮我们完成这些操作 ,这样我们需要做的操作就两个,1.把对象存到Spring中去,2.从Spring中去取对象
3.控制反转(IOC)的好处
我们以一部手机为例,就简易的手机,假如我们需要的有CPU,线路板,电阻来组成
这样我们传统的代码开发,就要先new一个手机,但是我们想要完成一部手机,要有一个CPU,所以再调用CPU类组装CPU,但是CPU想要组装,就必须要先组装线路板,但是线路板想要组装,就必须依靠电阻,这样我们的程序就非常耦合,我们通过一张图,更加直观的来理解
可以看到,我们想要造一部手机,耦合度非常的高,如果我们的电阻这一栏,我们加一个电容属性,那么,我们上面的线路板中也需要把调用电阻的那一块进行修改,从而导致我们整个的代码都需要进行一个更新,这个时候,IOC的优势便体现出来了
我们只需要进行一个改进,就可以大大降低耦合度,我们可以在main函数中,将每一个对象都进行创建,然后我们可以在每一个类中,创建一个属性,用来接收下一级的对象,这样在我们进行对底层修改的时候,只需要对修改的代码进行修改和main函数中的参数进行修改即可,大大降低了带啊吗的耦合,我们也用一个图来理解
可以明显的看出来IOC容器的优势
4.DI
DI是一种设计模式,就是依赖注入,在我们想要运行A时,A需要依赖数据库来完成,这时候我们不会像传统开发一样,去搭建DBUtile或者去newDBUtile的对象,而是将依赖关系配置到xml文件中去,由Spring去管理,这样就可以动态的去依赖关系注入到对象之中,从而实现解耦合
2.Spring使用
这里就不去教大家如何创建Spring项目了,我们如何使用Spring呢?,需要在xml文件中去添加Spring的依赖即可
这里我们具体讲一下如何存取对象
1.较为古老的用法
1.1添加启动类
我们需要在java包中进行创建一个启动类,只要包含main方法即可,类似这样
public class App {
public static void main(String[] args) {
}
}
1.2创建Bean
什么是Bean呢?
Bean对象就是java中的普通对象,这里其实本质是一个类的写法
类似下面
public class User {
public String Hi(String s){
return "Hi " + s;
}
}
1.3存储Bean
创建好Bean之后,我们需要将其注册到Spring中去
这里我们使用最原始的方法
首先创建一个xml文件,在resource目录下,如下
这里面先将Spring的固定文件放进去,这个是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>
<bean id = "user" class="com.ganzhi.User"></bean>
</beans>
</beans>
然后我们只需要将User对象注入到Spring中即可
1.4获取Bean
我们创建Bean的目的就是为了拿来用,那我们要如何取到Bean中的属性呢
首先我们需要得到Spring的上下文对象
因为我们的Bean是交给Spring来管理的,所以需要用一个Spring 的上下文来进行操作,可以用如下的代码来实现
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
}
}
不仅如此,我们还可以利用Bean Factory来作为Spring的上下文,代码如下
public class App {
public static void main(String[] args) {
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("Spring-config.xml"));
}
}
这里BeanFactory其实和上面的ApplicationContext效果是一样的,后者是前者的子类
常见面试题:
BeanFactory 和 ApplicationContext的区别
1.二者都是Spring的顶级容器,并且ApplicationContext是BeanFactory的子类,他除了继承了BeanFactory的所有功能外,还增加了国际化支持,资源访问支持,事件传播等方面的支持
2.从性能方面,ApplicationContext属于饿汉加载(也就是一次性加载并且初始化所有的Bean对象)而BeanFactory是懒汉加载(用到哪个Bean对象就加载哪个),所以我们可以看出来,BeanFactory比ApplicationContext更加轻量
拓展:这里还有个ClassPathXmlApplicationContext类,他是Application的子类
1.5获取指定的Bean对象
这里可以通过这种方法来获取Bean对象
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
// BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("Spring-config.xml"));
User user = (User) applicationContext.getBean("user");
System.out.println(user.Hi("张三"));
}
}
注意:这里getBean中的"user"必须和xml中的Beanid保存一致
了解getBean()的重载方法
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
// BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("Spring-config.xml"));
User user = (User) applicationContext.getBean(User.class);
System.out.println(user.Hi("张三"));
}
}
这里也可以直接用类名来获取
或者id和类名一起使用
package com.ganzhi;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
/**
* Created with IntelliJ IDEA.
* Description:
* User:
* Date: 2023-04-05
* Time: 17:55
*/
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
// BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("Spring-config.xml"));
User user = (User) applicationContext.getBean("user",User.class);
System.out.println(user.Hi("张三"));
}
}
这里用一张图,带大家了解一下具体的流程
2.现在的 方法
1.配置扫描路径
这里我们需要告诉Spring去哪里扫描类,并存储,所以是非常重要的步骤
这里我们附上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:content="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 https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="com.ganzhi"></content:component-scan>
</beans>
其中的content一行就是我们要扫描的路径
2.添加类注解
2.1@Controller(控制器存储)
我们使用可以用@Controller来让对象存入到Spring,代码示例如下
@Controller
public class User {
public String Hi(String s){
return "Hi " + s;
}
}
这样我们就可以扫描到这个对象了,利用上述的App类中的main,依然可以执行
代码再次附上
package com.ganzhi;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
/**
* Created with IntelliJ IDEA.
* Description:
* User:
* Date: 2023-04-05
* Time: 17:55
*/
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
// BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("Spring-config.xml"));
User user = (User) applicationContext.getBean("user");
System.out.println(user.Hi("张三"));
}
}
2.2.@Service(服务存储)
代码如下
@Service
public class User {
public String Hi(String s){
return "Hi " + s;
}
}
2.3.@Repository(仓库存储)
代码如下
@Repository
public class User {
public String Hi(String s){
return "Hi " + s;
}
}
其他两个是@Component(组件存储) 和@Configuration(配置存储)使用方法都是和上面一样得到
4.类注解类型多的原因
既然我们的类注解好像功能都是一样的,那为什么我们还要分那么多类型呢?
其实还是为了区分不同注解的用途,但是都能用,这里就显示出我们Spring的宗旨了(约定大于配置)
@Controller是表示的是业务逻辑层,用于检查参数的有效性
@Service是服务层,调用持久化类实现的一些功能
@Repository是持久层的,也就是持久存储数据的,直接和数据库进行交互
@Configuration是配置层的,用于配置当前项目的一些信息
@Component是归属于公共工具类
不同的注解,调用流程是不同的,是Controller->Service->Repository
而Component是他们四个的父类
5.Bean的命名规则
5.51.类名的首字母大写,第二个字母小写
这种就是我们一般类的命名规则,这里我们直接小写首字母即可完成读取
这种也是我们最常见的
5.2.第一个和第二个字母都是大写
这里需要用原类名来读取
5.3.源码概读
这里我们可以直接去源码查找命名规则
这里我们直接唤醒idea的搜索,去搜索beanName,可以找到下面这个类
然后直接点进去,找到下面的方法
很明显,这里返回值里面调用的方法就是我们想要找的命名规则
之间按住ctrl点击这个方法,去进行查看,就会得到下面的方法
从这个方法我们
可以看到,如果我们传入的名字是null或者长度为0,他会直接返回,如果不是,那下面第一个if语句的语义就是,如果第一个字母和第二个字母都是大写的话,就直接返回名字,如果不是,则走下面的,也就是其他情况,那么我们的做法就是第一个字母小写
从这里也可以推导出,如果第一个字母小写,那么我们获取类的时候也需要第一个字母小写,也就是不需要更改
6.方法注解@Bean
方法注解就是放到某些方法上面的注解,但是需要注意,方法注解要配合类注解一起使用才有效
而且Bean注解有多种用法,这是第一种,也是最简单的一种
类似下面
6.1Bean常规的使用
有一个User类,里面存放User对象
package com;
import org.springframework.stereotype.Controller;
/**
* Created with IntelliJ IDEA.
* Description:
* com.User:
* Date: 2023-04-05
* Time: 17:59
*/
@Controller
public class User {
private int Id;
private String Name;
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
@Override
public String toString() {
return "User{" +
"Id=" + Id +
", Name='" + Name + '\'' +
'}';
}
}
然后我们弄一个Users来存放User对象,顺便使用并@Bean注解
package com.ganzhi;
import com.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
/**
* Created with IntelliJ IDEA.
* Description:
* User:
* Date: 2023-04-06
* Time: 21:17
*/
@Controller
public class Users {
@Bean
public User getUser(){
User user = new User();
user.setId(1);
user.setName("zhangsan");
return user;
}
}
随后我们在App类中去运行
package com;
import com.ganzhi.UserController;
import com.ganzhi.UserTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created with IntelliJ IDEA.
* Description:
* com.User:
* Date: 2023-04-05
* Time: 17:55
*/
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
User user = (User) applicationContext.getBean("getUser");
System.out.println(user);
}
}
注意:这里的Bean命名规则和上面的命名规则是一致的,运行效果如下
注意:!!!!这里被Bean存入和获取的是不可以有参数的方法,因为这种方法存取参数是无法进行传递的
证明了我们,可以正常去获得到Bean
6.2.这里我们还可以重命名Bean
如下
package com.ganzhi;
import com.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
/**
* Created with IntelliJ IDEA.
* Description:
* User:
* Date: 2023-04-06
* Time: 21:17
*/
@Controller
public class Users {
@Bean(name = {"u1"})
public User getUser(){
User user = new User();
user.setId(1);
user.setName("zhangsan");
return user;
}
}
package com;
import com.ganzhi.UserController;
import com.ganzhi.UserTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created with IntelliJ IDEA.
* Description:
* com.User:
* Date: 2023-04-05
* Time: 17:55
*/
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
User user = (User) applicationContext.getBean("u1");
System.out.println(user);
}
}
这里我们就给Bean重新起了一个叫u1的名字
注意:这里重命名的{}内其实是一个数组,可以存放多个Bean名,而且在读取的时候只要按照正确的方式,完全可以正常存取
7.对象装配
在上面我们都是通过applicationContext来拿到Bean的,那我们怎么样在非运行类中赋值给变量呢?
这里我们就会使用到对象装配,这里也叫注入,有三种方式:属性注入,set注入,构造方法注入
7.1.属性注入
这是实现起来最简单的,只需要在需要赋值的变量上面加个@Autowired即可
我们为了更好理解,就新建两个类,一个UserController,一个UserTest
UserController:
这个类是为了让我们拿到user对象,并且返回出去,便于UserTest调用
package com.ganzhi;
import com.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
/**
* Created with IntelliJ IDEA.
* Description:
* com.User:
* Date: 2023-04-05
* Time: 21:18
*/
@Controller
public class UserController {
public User getUserById(Integer id){
User user = new User();
user.setId(id);
user.setName("zhangsan");
return user;
}
}
UserTest:
这个类是把上面存入的UserController 对象取出来,并且调用里面的getUserById方法,拿到User,返回给运行类,这里使用了属性注解,将Spring中的UserController 进行装配
package com.ganzhi;
import com.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
/**
* Created with IntelliJ IDEA.
* Description:
* User: 王
* Date: 2023-04-06
* Time: 21:33
*/
@Controller
public class UserTest {
@Autowired
private UserController userController;
public User getUserById(Integer id){
return userController.getUserById(id);
}
}
然后我们通过App类进行执行
package com;
import com.ganzhi.UserController;
import com.ganzhi.UserTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created with IntelliJ IDEA.
* Description:
* com.User:
* Date: 2023-04-05
* Time: 17:55
*/
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
// BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("Spring-config.xml"))
UserTest userTest = applicationContext.getBean(UserTest.class);
System.out.println(userTest.getUser(5).toString());
}
}
这里需要注意,我们拿到的是UserTest对象,而UserTest被注入了UserController对象,然而通过UserController的getUserById可以拿到一个User对象,再通过UserTest的getUserById返回给运行类,即可拿到User,我们这里模拟的是从库中取数据,所以反复调用,繁琐一些
执行结果如下
7.2.构造方法注入
我们这里只改变UserTest类
package com.ganzhi;
import com.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
/**
* Created with IntelliJ IDEA.
* Description:
* User:
* Date: 2023-04-06
* Time: 21:33
*/
@Controller
public class UserTest {
private UserController userController;
@Autowired
public UserTest(UserController userController){
this.userController = userController;
}
public User getUser(Integer id){
return userController.getUserById(id);
}
}
这里我们通过构造方法进行构造,我们 通过Autowired注解,给参数UserController进行赋值,然后通过构造方法中的赋值进行注入,也可以正确得到结果,这里就不赘述
注意:当当前类只有一个构造方法时,可以省略@Autowired
7.3.set注入
这里我们还是更改UserTest方法
package com.ganzhi;
import com.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
/**
* Created with IntelliJ IDEA.
* Description:
* User: 王久实
* Date: 2023-04-06
* Time: 21:33
*/
@Controller
public class UserTest {
private UserController userController;
@Autowired
public void setUserController(UserController userController){
this.userController = userController;
}
public User getUser(Integer id){
return userController.getUserById(id);
}
}
这里和上面的逻辑基本差不多,但是这里不可以省略@Autowired
7.4.三种方法比较(面试题)
重要
属性注入:
优势:使用简单,方便
劣势:只适用于IOC容器,只有在使用当前属性时,才会触发空指针异常
构造方法注入:
优势:Spring官方推荐,通用,在使用之前,可以保证传入的类不为空,更符合单一设计原则
劣势:写法比较麻烦
set注入:
优势:基本无
劣势:不如属性注入简介,不如构造方法通用
7.5.Resource注解关键字
我们进行类注入时,不仅可以使用Autowired还可以使用Resource,这里基本使用方法和上面一致
我们主要说一下不同的地方
1.Resource是来自于JDK的注解,而Autowired来自于Spring
2.Resource不适用于构造方法注入
3.Resource可以支持Bean设置更多参数,如name,代码如下
Users:
package com.ganzhi;
import com.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
/**
* Created with IntelliJ IDEA.
* Description:
* User:
* Date: 2023-04-06
* Time: 21:17
*/
@Controller
public class Users {
@Bean
public User getUser1(){
User user = new User();
user.setId(1);
user.setName("zhangsan");
return user;
}
@Bean
public User getUser2(){
User user = new User();
user.setId(2);
user.setName("lisi");
return user;
}
}
这里我们弄了两个Bean注解,那么我们就需要通过方法名来找了,但是Autowired却没有name参数,这时候,我们就可以使用Resource注解
UserResource:
package com.ganzhi;
import com.User;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
/**
* Created with IntelliJ IDEA.
* Description:
* User:
* Date: 2023-04-08
* Time: 17:29
*/
@Controller
public class UserResource {
@Resource(name = "getUser2")
private User user;
public void Hi(){
System.out.println(user.toString());
}
}
这里我们去执行
package com;
import com.ganzhi.UserController;
import com.ganzhi.UserResource;
import com.ganzhi.UserTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.annotation.Resource;
/**
* Created with IntelliJ IDEA.
* Description:
* com.User:
* Date: 2023-04-05
* Time: 17:55
*/
public class App {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring-config.xml");
UserResource userResource = applicationContext.getBean("userResource", UserResource.class);
userResource.Hi();
}
}
就可以得到下面的结果
7.6.@Qualifier
如果我们实在想用Autowired就需要多添加一个这个注解来进行设置参数
对UserResource进行修改
package com.ganzhi;
import com.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
/**
* Created with IntelliJ IDEA.
* Description:
* User:
* Date: 2023-04-08
* Time: 17:29
*/
@Controller
public class UserResource {
// @Resource(name = "getUser2")
@Autowired
@Qualifier(value = "getUser2")
private User user;
public void Hi(){
System.out.println(user.toString());
}
}
可以得到相同的结果
这里的value可以省略,直接写@Qualifier("getUser2")
到这里就结束了,感谢观看