Spring入门(万字详细附代码讲解)

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")

到这里就结束了,感谢观看 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值