Spring入门掌握知识

Spring

目录

1.什么是Spring?

 1.1 什么是容器?

1.2 什么是IoC?     

1.2.1 传统模式开发

1.2.2 控制反转开发

1.2.3 总结对比

1.3 Spring IoC 

 2.Spring项目的创建和使用

2.1创建Spring项目

 2.2 存储Bean对象

2.3获取并使用Bean对象

 2.4 Bean对象的注解存储

2.4 对象注入(对象装配)

2.5同一类型多个 @Bean 报错  


1.什么是Spring?

        我们通常所说的 Spring 指的是 Spring FrameworkSpring 框架),它是一个开源框架,有着活跃而庞大的社区,这就是它之所以能长久不衰的原因。Spring 支持广泛的应用场景,它可以让 Java 企业级的应用程序开发起来更简单。

        用一句话概括 Spring:Spring 是包含了众多工具方法的 IoC 容器。

 1.1 什么是容器?

        容器是用来容纳某种物品的(基本)装置。
比如:
        List/Map -> 数据存储容器
        Tomcat -> Web 容器

1.2 什么是IoC?     

        IoC = Inversion of Control 翻译成中文是 控制反转 的意思,也就是说 Spring 是一个 控制反转 的容器

1.2.1 传统模式开发

        正常情况下我们new一个汽车是这样的(如下图),直接Car car = new Car()(轮胎尺寸默认);这种传统开发方式的缺陷在于如果我们要改变底层数据,比如我们想要造出各种轮胎尺寸大小不同的汽车,那么就需要对整个调用链上的代码进行修改,

        默认汽车构造类

public class NewCarExample { 
    public static void main(String[] args) { 
        Car car = new Car(); car.init(); }
        /*** 汽车对象 */ 
    static class Car { 
        public void init() { 
            // 依赖车身 
            Framework framework = new Framework();
            framework.init(); 
        }
    }
    /*** 车身类 */ 
    static class Framework {         
        public void init() { 
            // 依赖底盘 
            Bottom bottom = new Bottom(); 
            bottom.init(); 
        } 
    }
    /*** 底盘类 */ 
    static class Bottom { 
        public void init() { 
            // 依赖轮胎 
            Tire tire = new Tire(); 
            tire.init();
        } 
    }
    /*** 轮胎类 */ 
    static class Tire { 
        // 尺寸 
        private int size = 30; 
        public void init() { 
            System.out.println("轮胎尺寸:" + size); 
        } 
    } 
}

         修改尺寸后

public class NewCarUpdateExample {
    public static void main(String[] args) {
        Car car = new Car(20);
        car.run();
    }

    /*** 汽车对象 */
    static class Car {
        private Framework framework;

        public Car(int size) {
            framework = new Framework(size);
        }

        public void run() { 
            // 依赖车身 
            framework.init(); 
        } 
    }
    /*** 车身类 */ 
    static class Framework { 
        private Bottom bottom; 
        public Framework(int size) { 
            bottom = new Bottom(size); 
        }
        public void init() { 
            // 依赖底盘 
            bottom.init();
        } 
    }
    /*** 底盘类 */ 
    static class Bottom { 
        private Tire tire; 
        public Bottom(int size) { 
            tire = new Tire(size); 
        }
        public void init() {
            // 依赖轮胎 
            tire.init(); 
        } 
    }
    /*** 轮胎类 */ 
    static class Tire { 
        // 尺寸 
        private int size; 
        public Tire(int size) { 
            this.size = size; 
        }
        public void init() { 
            System.out.println("轮胎尺寸:" + size); 
        }
    } 
}

如何解决?

        我们可以尝试不在每个类中自己创建下级类,如果自己创建下级类就会出现当下级类发生改变操作,自 己也要跟着修改。

        此时,我们只需要将原来由自己创建的下级类,改为传递的方式(也就是注入的方式),因为我们不需 要在当前类中创建下级类了,所以下级类即使发生变化(创建或减少参数),当前类本身也无需修改任何代码,这样就完成了程序的解耦。

1.2.2 控制反转开发

    

        基于以上思路,我们把调用汽车的程序示例改造一下,把创建子类的方式,改为注入传递的方式,具体实现代码如下

package demo;

/**
 * @author 是阿秋啊
 * @date 2022/09/27 13:11
 **/

public class IocCarExample {
    public static void main(String[] args) {
        Tire tire = new Tire(20);
        Bottom bottom = new Bottom(tire);
        Framework framework = new Framework(bottom);
        Car car = new Car(framework);
        car.run();
    }

    static class Car {
        private Framework framework;

        public Car(Framework framework) {
            this.framework = framework;
        }

        public void run() {
            framework.init();
        }
    }

    static class Framework {
        private Bottom bottom;

        public Framework(Bottom bottom) {
            this.bottom = bottom;
        }

        public void init() {
            bottom.init();
        }
    }

    static class Bottom {
        private Tire tire;

        public Bottom(Tire tire) {
            this.tire = tire;
        }

        public void init() {
            tire.init();
        }
    }

    static class Tire {
        private int size;

        public Tire(int size) {
            this.size = size;
        }

        public void init() {
            System.out.println("轮胎:" + size);
        }
    }
}

        此时,底层类无论如何变化,整个调用连都不会做任何改变,这样就完成了代码之间的解耦,从而实现了更加灵活、通用的程序设计了

1.2.3 总结对比

在传统的代码中对象创建顺序是:Car -> Framework -> Bottom -> Tire

改进之后解耦的代码的对象创建顺序是:Tire -> Bottom -> Framework -> Car

       通用程序的实现代码,类的创建顺序是反的传统代码是Car控制并创建Framework,Framework 创建并创建了Bottom,依次往下,而改进之后的控制权发生的反转,不再是上级对象创建并控制下级对象了,而是下级对象把注入将当前对象中,下级的控制权不再由上级类控制了,这样即使下级类发生任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想。

1.3 Spring IoC 

        既然 Spring 是一个 IoC(控制反转)容器,重点还在容器二字上,那么它就具备两个最基础的功能: 将对象存入到容器; 从容器中取出对象。

        学Spring 最核心的功能,就是学如何将对象存入到 Spring 中,再从 Spring 中获取对象的过程

       将对象存放到容器中的好处:将对象存储在 IoC 容器相当于将以后可能用的所有工具制作好都放到仓库中,需要的时候直接取就行了,用完再把它放回到仓库。而 new 对象的方式相当于,每次需要工具了,才现做,用完就扔掉了也不会保存,下次再用的时候还得重新做,这就是 IoC 容器和普通程序开发的区别。

        Spring 是一个 IoC 容器,说的是对象的创建和销毁的权利都交给 Spring 来管理了,它本身又具备了存 储对象和获取对象的能力。

 2.Spring项目的创建和使用

2.1创建Spring项目

        使用 Maven 方式来创建一个 Spring 项目,创建 Spring 项目和 Servlet 类似,总共分为以下 3 步:

        1. 创建一个普通 Maven 项目。
        2. 添加 Spring 框架支持( spring-context spring-beans )。.
                pom.xml文件中添加Spring框架支持,配置如下
<dependencies> 
    <dependency> 
        <groupId>org.springframework</groupId> 
        <artifactId>spring-context</artifactId> 
        <version>5.2.3.RELEASE</version>
    </dependency> 
    <dependency> 
        <groupId>org.springframework</groupId> 
        <artifactId>spring-beans</artifactId> 
        <version>5.2.3.RELEASE</version> 
    </dependency> 
</dependencies>
        3. 添加启动类。

        最后在创建好的项目 java 文件夹下创建一个启动类,包含 main 方法即可:

 2.2 存储Bean对象

        1.创建Bean; 所谓的 Bean 就是 Java 语言中的一个普通对象

public class User { 
    public String sayHi(String name) { 
        return name + " hello!"; 
    } 
}

         2.将Bean注册到容器中

        在创建好的项目中添加 Spring 配置文件 spring-config.xml ,将此文件放到 resources 的根目录下

        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>

        再将 User 对象注册到 Spring 中就可以

<beans> 
    <bean id="user" class="com.bit.User"></bean> 
</beans>

2.3获取并使用Bean对象

1. 创建 Spring 上下文

          Spring 上下文对象可使用 ApplicationContext:

// 1.得到 Spring 的上下文对象,创建的时候需要配置 Spring 配置信息 
ApplicationContext context = new ClassPathXmlApplicationContext("spring- config.xml");
         除了 ApplicationContext 之外,我们还可以使用 BeanFactory 来作为 Spring 的上下文
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring- config.xml"));
        ApplicationContext 和 BeanFactory 效果是一样的, ApplicationContext 属于 BeanFactory 的子类,
        PS:而 ClassPathXmlApplicationContext 属于 ApplicationContext 的子类,拥有                 ApplicationContext 的所有功能,是通过 xml 的配置来获取所有的 Bean 容器的。
ApplicationContext VS BeanFactory的区别如下

        继承关系和功能方面来说:Spring 容器有两个顶级的接口:BeanFactory

ApplicationContext 。其中 BeanFactory 提供了基础的访问容器的能力, ApplicationContext
属于 BeanFactory 的子类,它除了继承了 BeanFactory 的所有功能之外,它还拥有独特的特性, 还添加了对国际化支持、资源访问支持、以及事件传播等方面的支持。
        从性能方面来说:ApplicationContext 是一次性加载并初始化所有的 Bean 对象,而 BeanFactory 是需要那个才去加载那个,因此更加轻量。

2.获取Bean对象

// 1.得到 Spring 上下文对象 
ApplicationContext context = new ClassPathXmlApplicationContext("spring- config.xml"); 
// 2.加载某个bean 
User user = (User) context.getBean("user");

        需要注意id要一一对应

         getBean()方法的更多用法:getBean() 方法有很多种重载方法,我们也可以使用其他方式来获取 Bean 对象,比如以下这两种:

        根据类型获取 Bean:UserController user = context.getBean(UserController.class);

        名称 + 类型获取 Bean:UserController user = context.getBean("user", UserController.class);

 二者的区别:当有一个类型被重复注册到 spring-config.xml 中时,只能使用根据名称获取了。

3. Bean的使用

        拿到Bean之后,就可以调用Bean对象中的方法

public class App { 
    public static void main(String[] args) { 
        // 1.得到 Spring 上下文对象 
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");           
        // 2.加载某个 bean
        User user = (User) context.getBean("user"); 
        // 3.调用相应的方法 
        System.out.println(user.sayHi("Java")); 
    } 
}

 2.4 Bean对象的注解存储

 要用注解存储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" 
       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.bit.service"></content:component-scan> 
</beans>

添加注解存储 Bean 对象

在 Spring 中,有两种注解类型可以实现:

        1. 类注解:@Controller(控制器存储)、@Service(服务存储)、@Repository(仓库存储)、@Component(组件存储)、@Configuration(配置存储)
                        @Controller:表示的是业务逻辑层;
                        @Servie:服务层;
                        @Repository:持久层;
                        @Configuration:配置层。

 

        2. 方法注解: @Bean
 举个例子:
        使用 @Controller 存储 bean 的代码如下所示:
@Controller // 将对象存储到 Spring 中 
public class UserController {
    public void sayHi(String name) { 
        System.out.println("Hi," + name); 
    } 
}
        此时我们先使用之前读取对象的方式来读取上面的 UserController 对象,如下代码所示:
public class Application { 
    public static void main(String[] args) { 
        // 1.得到 spring 上下文 
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); 
        // 2.得到 bean 
        UserController userController = (UserController) context.getBean("userController"); 
        // 3.调用 bean 方法 
        userController.sayHi("Bit"); 
    } 
}
        通常我们 bean 使用的都是标准的大驼峰命名,而读取的时候首字母小写就可以获取到 bean

3.方法注解 @Bean

       在 Spring 框架的设计中,方法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,如 下代码所示:
@Component
public class Users { 
    @Bean 
    public User user1() { 
        User user = new User(); 
        user.setId(1); 
        user.setName("Java"); 
        return user; 
    } 
}
        可以通过设置 name 属性给 Bean 对象进行重命名操作: @Bean ( name = { "u1" })

2.4 对象注入(对象装配)

 对象装配(对象注入)的实现方法以下 3 种:

@Autowired

1. 属性注入
        属性注入是使用 @Autowired 实现的,将 Service 类注入到 Controller 类中。
@Controller 
public class UserController { 
    // 注入方法1:属性注入 
    @Autowired 
    private UserService userService; 
    public User getUser(Integer id) { 
        return userService.getUser(id); 
    } 
}
2. 构造方法注入
        构造方法注入是在类的构造方法中实现注入,如下代码所示:
@Controller 
public class UserController2 { 
    // 注入方法2:构造方法注入 
    private UserService userService; 
    @Autowired 
    public UserController2(UserService userService) { 
        this.userService = userService;
    }
    public User getUser(Integer id) { 
        return userService.getUser(id); 
    } 
}
注意事项:如果类只有一个构造方法,那么 @Autowired 注解可以省略;如果类中有多个构造方法,那么需要添加上 @Autowired 来明确指定到底使用哪个构造方法。
3. Setter 注入
        Setter 注入和属性的 Setter 方法实现类似,只不过 在设置 set 方法的时候需要加上 @Autowired 解,如下代码所示:
        
@Controller 
public class UserController3 { 
    // 注入方法3:Setter注入 
    private UserService userService; 
    @Autowired 
    public void setUserService(UserService userService) { 
        this.userService = userService; 
    }
    public User getUser(Integer id) { 
        return userService.getUser(id); 
    } 
}

三种注入方式优缺点分析

        属性注入的优点是简洁,使用方便;缺点是只能用于 IoC 容器,如果是非 IoC 容器不可用,并且只有在使用的时候才会出现 NPE(空指针异常)。

        构造方法注入是 Spring 推荐的注入方式,它的缺点是如果有多个注入会显得比较臃肿,但出现这种情况你应该考虑一下当前类是否符合程序的单一职责的设计模式了,它的优点是通用性,在使用之前一定能把保证注入的类不为空。

        Setter 方式是 Spring 前期版本推荐的注入方式,但通用性不如构造方法,所有 Spring 现版本已经推荐使用构造方法注入的方式来进行类注入了。

 @Resource:另一种注入关键字

        在进行类注入时,除了可以使用 @Autowired 关键字之外,我们还可以使用 @Resource 进行注入,如下代码所示:
@Controller 
public class UserController { 
    // 注入 @Resource 
    private UserService userService; 
    public User getUser(Integer id) { 
        return userService.getUser(id); 
    }     
}
@Autowired @Resource 的区别
  • 出身不同:@Autowired 来自于 Spring,而 @Resource 来自于 JDK 的注解;
  • 使用时设置的参数不同:相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean

2.5同一类型多个 @Bean 报错  

解决同一个类型,多个 bean 的解决方案有以下两个:

  • 使用 @Resource(name="user1") 定义。
  • 使用 @Qualifier 注解定义名称。

1.使用 @Resource(name="XXX")

@Controller 
class UserController4 { 
    // 注入 
    @Resource(name = "user1") 
    private User user; 
    public User getUser() { 
        return user; 
    } 
}

2. 使用 @Qualifier

@Controller 
public class UserController5 { 
    // 注入
    @Autowired 
    @Qualifier(value = "user2") 
    private User user;
    public User getUser() { 
        return user; 
    } 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是啊秋啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值