1

Java安全--Spring框架基础学习

 2 years ago
source link: https://shu1l.github.io/2021/02/12/java-an-quan-spring-ji-chu-xue-xi/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Spring–IOC

spring的核心思想是IOC和AOP,IoC - Inversion of Control, 控制反转,通俗点说就是把创建和管理bean的过程转移给了第三方。而这个第三方就是我们说的IOC容器。

Spring容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。控制着bean的依赖注入。

那么这里为什么要叫做控制反转呢?

我们先来对比下两种不同的获取外部对象的资源的方法。

  • 主动去创建相关对象然后再组合。
  • IOC/DI容器中

在 Spring 中,类的实例化、依赖的实例化、依赖的传入都交由 Spring Bean 容器控制,而不是用new方式实例化对象、通过非构造函数方法传入依赖等常规方式。实质的控制权已经交由程序管理,而不是程序员管理,所以叫做控制反转。

有一个Book类:

public class Book {
private String name ; //书名
private int money; /价格

public Book(String name, int money) {
this.name = name;
this.money = money;
}
}

Person类依赖于Book类,

public class Person {
private Book book;

public Person(Book book) {
this.book = book;
}
}

​ 因为IoC容器要负责实例化所有的组件,因此,有必要告诉容器如何创建组件,以及各组件的依赖关系。一种最简单的配置是通过XML文件来实现,例如:

<beans>
<bean id="dataSource" class="HikariDataSource" />
<bean id="bookService" class="BookService">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="userService" class="UserService">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>

​ 上述XML配置文件指示IoC容器创建3个JavaBean组件,并把id为dataSource的组件通过属性dataSource(即调用setDataSource()方法)注入到另外两个组件中。

在Spring的IoC容器中,我们把所有组件统称为JavaBean,即配置一个组件就是配置一个Bean,控制反转通过依赖注入(DI)方式实现对象之间的松耦合关系。

Spring–Bean

​ spring官方文档对bean的解释是:在spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean。bea是一个由Spring IoC容器实例化、组装和管理的对象。

bean主要包含以下几个概念:

  • Bean容器,或称spring ioc容器,主要用来管理对象和依赖,以及依赖的注入。
  • bean是一个java对象,根据bean规范编写出来的类,并由bean容器生成的对象就是一个bean。
  • bean规范

Bean实例化

1.普通构造方法创建

使用比较多的一种创建方式,可以直接配置bean节点,例如:

public class Demo {
public void add() {
System.out.println("add()---------");
}
}

在xml中简单配置一个bean节点,如下:

<bean class="org.test.Demo" id="test"/>

通过以下代码进行简单的测试:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Demo test = (Demo) context.getBean("test");
System.out.println(test);
2.静态工厂创建

通过静态构造方法来创建一个bean的实例,如下:

public class Demo2 {
public void add() {
System.out.println("add2()---------");
}
}

创建一个静态工厂,如下:

public class Demo2Factory {
public static Demo2 getInstance() {
return new Demo2();
}
}

该工厂中有一个静态方法,该静态方法返回一个的实例,通过Spring的配置文件生成Demo2的实例:

<bean id="demo2" class="org.test.demo2Factory" factory-method="getInstance"/>

factory-method属性,该属性指明该类中的静态工厂方法名为getInstance,spring框架根据属性来调用方法来获取Demo2的实例了,测试代码如下:

@Test
public void test2() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Demo2 demo = (Demo2) context.getBean("demo2");
demo.add();
}
实例工厂创建

通过实例工厂来创建bean实例,例如:

public class Demo3 {
public void add() {
System.out.println("add3()---------");
}
}

同时有一个工厂方法,如下:

public class Demo3Factory {
public Demo3 getDemo3() {
return new Demo3();
}
}

在Demo3Factory类中有一个getDemo3的方法,该方法返回一个Demo3类的实例,Spring的配置文件如下:

<bean class="org.test.Demo3Factory" id="demo3Factory"/>
<bean id="demo3" factory-bean="demo3Factory" factory-method="getDemo3"/>

第一个bean用来获取demo3Factory的实例,第二个bean则根据demo3Factory的实例,然后指定factory-method,通过getDemo3方法来获取Demo3的实例。
测试代码如下:

@Test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Demo3 demo = (Demo3) context.getBean("demo3");
demo.add();
}

Bean作用域

spring bean 一共有五种作用域。

1.Singleton (缺省作用域、单例类型)

​ 容器中只存在一个共享的Bean,只要id与Bean定义相匹配,那就会是同一个Bean。在容器启动(实例化)时Bean就实例化和初始化(可以通过lazy-init=”true”来设置使得Bean被调用时才初始化)。

2.Prototype (原型类型)

​ 对有状态的Bean建议使用Prototype,对无状态建议使用Singleton。
容器启动时并没有实例化Bean,只有获取Bean时才会被创建,并且每一次都是新建一个对象。

3.request(web的Spring ApplicationContext)

​ 每个HTTP 都会有自己的Bean,当处理结束时,Bean销毁。

4.session(web的Spring ApplicationContext)

​ 每一个Http session有自己的Bean

5.global session(web的Spring ApplicationContext)

​ global session作用域类似于标准的HTTP Session作用域,不过仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。

Bean生命周期

  Spring Bean的完整生命周期从创建Spring容器开始,直到最终Spring容器销毁Bean,这其中包含了一系列关键点。

单例管理的对象

Bean在容器启动时就会实例化和初始化,但是可以通过Bean的设置来设置对象的初始化时机
第一种:通过设置

<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" lazy-init="true"/>

第二种:通过设置来修改所有bean默认方式的初始化时机

<beans default-lazy-init="true">
非单例管理的对象

Spring读取xml文件的时候,并不会立刻创建对象,而是在第一次请求该bean时才初始化(如调用getBean方法时)。容器只会实例化和初始化Bean,不会销毁Bean。清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。在初始化后交由调用者管理

Spring–依赖注入(DI)

依赖注入(Dependency Injection,DI),和控制反转含义相同,它们是从两个角度描述的同一个概念。

​ 当某个java实例(调用者)需要另一个java实例(被调用者),我们通常采用的方法是由调用者来创建被调用者的实例。(使用new关键字获得被调用者的实例)。

​ 在spring中,spring在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,调用者通过spring容器获得被调用者实例,被称为依赖注入。

注入方式(bean装配)

​ 依赖注入的本质就是装配,装配是依赖注入的具体行为。在Spring中,注入依赖对象可以采用手工装配或自动装配。

手工装配一般分为两种方式

  • 一种是在XML文件中,通过在bean节点下配置;比如使用属性的setter方法注入依赖对象或者使用构造方法注入。

  • 一种就是在java代码中使用注解的方式进行装配,在代码中加入@Resource或者@Autowired。

setter方法注入

​ 由于setter注入方式具有可选择性和灵活性高的特点,因此它也是实际开发中最常用的注入方式。setter方法更加直观,我们来看一下spring的配置文件:

<!-- 使用spring管理对象的创建,还有对象的依赖关系 -->    
<bean id="userDao4Mysql" class="com.tgb.spring.dao.UserDao4MysqlImpl"/>
<bean id="userDao4Oracle" class="com.tgb.spring.dao.UserDao4OracleImpl"/>
<bean id="userManager" class="com.tgb.spring.manager.UserManagerImpl">
<!-- (1)userManager使用了userDao,Ioc是自动创建相应的UserDao实现,都是由容器管理-->
<!-- (2)在UserManager中提供构造函数,让spring将UserDao实现注入(DI)过来 -->
<!-- (3)让spring管理我们对象的创建和依赖关系,必须将依赖关系配置到spring的核心配置文件中 -->
<property name="userDao" ref="userDao4Oracle"></property>
</bean>

接着我们来看一下,setter表示依赖关系的写法

import com.tgb.spring.dao.UserDao;    
public class UserManagerImpl implements UserManager{
private UserDao userDao;
//使用设值方式赋值
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser(String userName, String password) {
userDao.addUser(userName, password);
}
}
构造函数注入

​ 构造函数注入是除setter注入之外的另一种常用的注入方式,它可以保证一些必要的属性在bean实例化时就得到了设置,并在实例化后就可以使用。

使用构造函数注入的前提是: bean必须提供带参的构造函数。

对于构造函数的注入,配置文件可以有以下几种方式:

  • 按类型匹配入参
  • 按索引匹配入参
  • 联合使用类型和索引匹配入参
  • 通过自身类型反射匹配入参

我们看一下spring的配置文件:

    <!-- 使用spring管理对象的创建,还有对象的依赖关系 -->    
<bean id="userDao4Mysql" class="com.tgb.spring.dao.UserDao4MysqlImpl"/>
<bean id="userDao4Oracle" class="com.tgb.spring.dao.UserDao4OracleImpl"/>
<bean id="userManager" class="com.tgb.spring.manager.UserManagerImpl">
<!-- (1)userManager使用了userDao,Ioc是自动创建相应的UserDao实现,都是由容器管理-->
<!-- (2)在UserManager中提供构造函数,让spring将UserDao实现注入(DI)过来 -->
<!-- (3)让spring管理我们对象的创建和依赖关系,必须将依赖关系配置到spring的核心配置文件中 -->
<constructor-arg ref="userDao4Oracle"/>
</bean>
</beans>

我们再来看一下,构造器表示依赖关系的写法,代码如下所示:

import com.tgb.spring.dao.UserDao;    
public class UserManagerImpl implements UserManager{
private UserDao userDao;
//使用构造方式赋值
public UserManagerImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser(String userName, String password) {
userDao.addUser(userName, password);
}
}

基于注解

​ 使用注解注入依赖对象不用再在代码中写依赖对象的setter方法或者该类的构造方法,并且不用再配置文件中配置大量的依赖对象,使代码更加简洁,清晰,易于维护。

在Spring IOC编程的实际开发中推荐使用注解的方式进行依赖注入。

  • Autowired是自动注入,自动从spring的上下文找到合适的bean来注入
  • Resource用来指定名称注入
  • Qualifier和Autowired配合使用,指定bean的名称,如
@Autowired  
@Qualifier("userDAO")
private UserDAO userDAO;

Spring容器的配置文件applicationContext.Xml文件中配置以下信息,是一个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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
">
</beans>

Spring配置隐式的注册了多个对注释进行解析的处理器,例如:

  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor

在配置文件中打开context:annotation-config节点,告诉Spring容器可以用注解的方式注入依赖对象;其在配置文件中的代码如下:

<beans><context:annotation-config></context:annotation-config></beans>

在配置文件中配置bean对象,如下:

<bean id="userDao" class="com.springtest.dao.impl.UserDAOImpl"></bean>  
<bean id="userBiz" class="com.springtest.biz.impl.UserBizImpl"></bean>

最后,在需要依赖注入的类中,声明一个依赖对象,不用生成该依赖对象的setter方法,并且为该对象添加注解:

public class UserBizImpl implements UserBiz {  
@Resource(name="userDao")
private UserDAO userDao = null;
public void addUser() {
this.userDao.addUser();
}
}

@Autowired默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean时,才会按类型装配。

控制反转与依赖注入的区别

依赖注入和控制反转是对同一件事情的不同描述,

  • 依赖注入是从应用程序的角度在描述:应用程序依赖容器创建并注入它所需要的外部资源;

  • 控制反转是从容器的角度在描述:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

Spring注入Bean的几种方式 (juejin.cn)

详解Spring中bean的几种注入方式 - 编程语言 - 亿速云 (yisu.com)

https://www.w3cschool.cn/wkspring

(15条消息) Spring Bean自动装配和注解注入_似水流年-CSDN博客

小白也看得懂的 Spring IoC 核心流程介绍 - 知乎 (zhihu.com)

Spring IOC原理总结 - 简书 (jianshu.com)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK