Spring ——Bean(IOC)容器

Spring框架

      • 一、Spring 框架概述
      • 二、IOC 容器:通过容器统一管理对象的生命周期
        • 1、什么是IOC容器?
        • 2、IOC 底层原理
        • 3、IOC 接口(BeanFactory)
      • 三、Bean管理
        • 1、 Bean 管理(基于xml)
          • (1)基于XML方式创建对象:
          • (2)基于xml 方式注入属性
          • (3)bean的两种类型
          • (4)bean的作用域
          • (5)bean的生命周期
          • (6)xml 自动装配
          • (7)外部属性文件
        • 2、IOC :Bean 管理(基于注解)
          • (1)什么是注解
          • (2)Spring中的常用注解
          • (3)基于注解方式实现对象创建
          • (4)开启组件扫描细节配置
          • (5)基于注解方式实现属性注入
          • (6)完全注解开发
      • 四、AOP
      • 五、JdbcTemplate
      • 六、Spring——事务

一、Spring 框架概述

Spring 是一个主流的 Java Web 开发框架,该框架是一个轻量级的、非入侵式的应用框架,可以解决企业应用开发的复杂性。

Spring 以 IoC(Inverse of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)为内核:

  • IOC:控制反转,把创建对象过程交给Spring 进行管理
  • Aop:面向切面,不修改源代码进行功能增强

Spring 特点:
(1)方便解耦,简化开发
(2)Aop 编程支持
(3)方便程序测试
(4)方便和其他框架进行整合。通常服务器端采用三层体系架构,分别为表现层(web)、业务逻辑层(service)、持久层(dao)。Spring 对每一层框架都提供了技术支持,在表现层提供了与 Struts2 框架的整合,在业务逻辑层可以管理事务和记录日志等,在持久层可以整合Mybatis、 Hibernate 和 JdbcTemplate 等技术。
(5)方便进行事务操作
(6)降低API 开发难度

为什么需要Bean容器?
一般的项目搭建,都需要将各层进行分离以降低代码之间的耦合度。比如我们常用Dao层(数据访问层)、service层来封装和数据库的交互部分,其中Dao层负责具体的增删查改,service层负责调用Dao层的方法来对外提供服务,即用户只需要调用业务层,不需要接触Dao层。例如:
Spring ——Bean(IOC)容器_第1张图片
使用这种方法相当于将对象创建放到了方法中,此时对象对应的变量(局部变量)随着栈帧的创建而出现,随着栈帧的退出而销毁。那么由于没有引用指向它,该对象就会很快变成可回收状态。如果对象的创建初始化较为复杂,那么此处的对象创建就会变得效率低下。所以这种方法存在对象创建销毁的问题,极大的影响了效率,且当Dao层增加代码时,业务层也要随之改变,所以我们应该去寻求一种用户只管调用,业务层无需程序员更改的一种模式。

针对以上问题我们可以用Bean容器来解决,类似于下面的形式,将对象交给Spring来管理:
Spring ——Bean(IOC)容器_第2张图片

Spring相关jar包:
Spring ——Bean(IOC)容器_第3张图片
为了方便使用Spring,我们可以使用maven:我们只需要导入一个spring-webmvc就好了,因为spring-webmvc依赖了很多其他的包。

<dependency>
   <groupId>org.springframeworkgroupId>
   <artifactId>spring-webmvcartifactId>
   <version>5.1.10.RELEASEversion>
dependency>

spring的核心依赖项基本上都包含在spring-webmvc中了。
Spring ——Bean(IOC)容器_第4张图片

官方文档

二、IOC 容器:通过容器统一管理对象的生命周期

1、什么是IOC容器?

(1)控制反转

  • 把对象创建和对象之间的调用过程,交给Spring 进行管理;
  • Spring 通过一个配置文件描述Bean 及Bean 之间的依赖关系,利用Java 语言的反射功能实例化Bean 并建立Bean 之间的依赖关系。
  • Bean对象从程序自行管理的方式,转变为Spring容器管理,控制权发生了反转。简单来讲,IOC就是把对象的生产和使用分离。让我们只需要去使用对象,而不用关心对象具体是怎么来的。

(2)使用IOC 目的:

  • 降低耦合度。
  • 减少对象创建、销毁的步骤,提高了效率:无需每次都在方法中创建对象,退出方法后对象又很快变为可回收状态。
  • 统一管理对象之间的依赖关系。(管理设置不同对象的属性)

(3) Spring 容器高层视图
Spring 启动时读取Bean 配置信息,并在Spring 容器中生成一份相应的Bean 配置注册表,然后根据这张注册表实例化Bean,装配好Bean 之间的依赖关系,为上层应用提供准备就绪的运行环境。其中Bean 缓存池为HashMap 实现.

Spring ——Bean(IOC)容器_第5张图片
Spring容器在初始化时先读取配置文件,根据配置文件或者元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

2、IOC 底层原理

xml 解析、工厂模式、反射
Spring ——Bean(IOC)容器_第6张图片

3、IOC 接口(BeanFactory)

IOC 思想基于IOC 容器完成,IOC 容器底层就是对象工厂

IOC 容器两种对象工厂实现方式:(两个接口)

  • BeanFactory:IOC 容器基本实现,是Spring 内部的使用接口,不提供开发人员进行使用。
  • ApplicationContextBeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。

注意:

  • BeanFactory加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象。
  • ApplicationContext加载配置文件时候就会把在配置文件对象进行创建。

ApplicationContext接口的实现类
Spring ——Bean(IOC)容器_第7张图片

  • ClassPashXmlApplicationContext:需要配置xml文件的磁盘路径
  • ClassPathXmlApplicationContext:需要配置xml文件的类路径

三、Bean管理

  • Spring对象的创建
  • Spring属性的注入

1、 Bean 管理(基于xml)

(1)基于XML方式创建对象:

1)在spring 配置文件中,使用bean 标签,标签里面添加对应属性,就可以实现对象创建
2)在bean 标签有很多属性,介绍常用的属性

  • id 属性:唯一标识
  • class 属性:类全路径(包类路径)

3)创建对象时候,默认执行无参数构造方法完成对象创建

(2)基于xml 方式注入属性

依赖注入(Dependency Injection,DI):注入属性,在Spring中实现控制反转的是Ioc容器,其具体实现方法就是依赖注入。

  • 依赖:bean对象的创建依赖于容器。
  • 注入:bean对象中的所有属性由容器注入。

IOC和DI的区别:

  • IOC:把创建对象交给Spring进行配置
  • DI: 向类里面的属性中赋值

1)第一种注入方式:使用set 方法进行注入
利用对象方法中的set方法进行注入,方法中要包含应该有的setter方法。 如果属性注入时,对应的属性没有set方法,那么xml对应的bean中属性名会报错。

(1)创建类,定义属性和对应的set 方法

public class Book {

//创建属性
private String name; 
private String author;

//创建属性对应的set方法
public void setBname(String name) { 	
	this.name = name; 
}
 public void setBauthor(String author) {
    this.author = author; 
} }

(2)在spring 配置文件配置对象创建,配置属性注入

<bean id="book" class="com.atguigu.spring5.Book">
  <property name="bname" value="易筋经">property> 
  <property name="bauthor" value="达摩老祖">property>
bean>

2)第二种注入方式:使用有参数构造进行注入
使用有参构造函数时,对应属性可以不用写set方法。

(1)创建类,定义属性,创建属性对应有参数构造方法

public class Orders {

//属性
private String oname; 
private String address;

//有参数构造
public Orders(String oname,String address) {
	this.oname = oname; this.address = address;
} 
}

(2)在spring 配置文件中进行配置

<bean id="orders" class="com.atguigu.spring5.Orders"> 
	<constructor-arg name="oname" value="电脑">constructor-arg> 
	<constructor-arg name="address" value="China">constructor-arg> 
bean>

注:有参构造方法有三种传参方式:

下标:
类型:
参数名:

3)第三种注入方式:工厂注入属性
参考博客:Spring工厂注入属性

4)第四种注入方式p命名空间:对应set属性注入

需要在beans的首部加入xmlns:p="http://www.springframework.org/schema/p"
Spring ——Bean(IOC)容器_第8张图片

  传统属性注入
  <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>
    
 P命名空间直接进行属性注入:
 <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

5)第五种注入方式c命名空间:对应构造器注入

需要在beans的首部加入xmlns:c="http://www.springframework.org/schema/c"

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

   传统构造器注入
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="[email protected]"/>
    </bean>

    c命名空间使用:
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
    c:thingThree-ref="beanThree" c:email="[email protected]"/>

注:
p命名和c命名不能直接使用,需要导入xml约束。

7)空值、null 值、特殊符号注入:

空值注入:
<property name="ducks2" value=""/>

null值注入:
<property name="address"> <null/> property>

包含特殊符号的属性值:把带特殊符号内容写到CDATA
 <property name="address"> 
 <value>>]]>value> property>
 
输出:User{address='<<南京>>', age=0}

8)注入属性-外部bean-内部bean-级联赋值

参考博客:https://blog.csdn.net/glpghz/article/details/109727241

9)xml 注入集合属性

数组array:
 		<property name="nums">
            <array>
               <value> 1</value>
               <value> 2</value>
            </list>
        </property>

list:
		<property name="duck">
            <list>
                <ref bean="duck1" />
                <ref bean="duck2" />
            </list>
        </property>

map:        
		<property name="ducks1">
            <map>
                <entry key="" value=""/>
                 <entry key="" value=""/>
            </map>
        </property>

set:
		<property name="ducks2">
            <set>
                <value> 1</value>
               <value> 2</value>
            </set>
        </property>
(3)bean的两种类型

普通bean:在配置文件中定义bean 类型就是返回类型
工厂bean(FactoryBean):在配置文件定义bean 类型可以和返回类型不一样

1)第一步创建类,让这个类作为工厂bean,实现接口FactoryBean
2)第二步实现接口里面的方法,在实现的方法中定义返回的bean 类型

public class MyBean implements FactoryBean<Course> {

//定义返回bean
@Override 
public Course getObject() throws Exception {
   Course course = new Course(); 
   course.setCname("abc");
   return course; 
}

@Override 
public Class<?> getObjectType() {
	return null;
}

@Override 
public boolean isSingleton() {
	return false; 
} 

}

xml配置:

<bean id="myBean" 
class="com.atguigu.spring5.factorybean.MyBean"> bean>

测试:

@Test 
public void test3() { 
	ApplicationContext context = 
			new ClassPathXmlApplicationContext("bean.xml"); 

	Course course = context.getBean("myBean", Course.class); 
	System.out.println(course); 
}
(4)bean的作用域

Bean 定义了5 中作用域,分别为

  • singleton(单例)Spring中的Bean默认为单例的
  • prototype(原型)
  • request
  • session
  • global session

1) singleton:单例模式(多线程下不安全)
Spring IoC 容器中只会存在一个共享的Bean 实例,无论有多少个
Bean 引用它,始终指向同一对象。

2)prototype:原型模式每次使用时创建
每次通过Spring 容器获取prototype 定义的bean 时,容器都将创建
一个新的Bean 实例,每个Bean 实例都有自己的属性和状态,而singleton 全局只有一个对象。根据经验,对有状态的bean 使用prototype 作用域,而对无状态的bean 使用singleton作用域。

注:

  • 有状态对象(Stateful Bean),就是有实例变量的对象 ,可以保存数据,是非线程安全的。
  • 无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
  • 在Spring中,绝大部分Bean都可以声明为singleton作用域。是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因此有状态的Bean就可以在多线程中共享了。
  • scope 值是singleton 时候,加载spring 配置文件时候就会创建单实例对象。
    scope 值是prototype 时候,不是在加载spring 配置文件时候创建对象,在调用
    getBean 方法时候创建多实例对象

3)Request:一次request 一个实例
在同一个Http 请求中,容器会返回该Bean 的同一实例。而对不同的Http 请求则会产生新的Bean,而且该bean 仅在当前Http Request 内有效,当前Http 请求结束,该bean实例也将会被销毁。

(5)bean的生命周期

bean 的后置处理器,bean 生命周期有七步
1)通过构造器创建bean 实例(无参数构造)
2)为bean 的属性设置值和对其他bean 引用(调用set 方法)
3)把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
4)调用bean 的初始化的方法(需要进行配置初始化的方法)
5)把bean 实例传递bean 后置处理器的方法postProcessAfterInitialization
6)bean 可以使用了(对象获取到了)
7)当容器关闭时候,调用bean 的销毁的方法(需要进行配置销毁的方法)

创建后置处理器:创建类,实现接口BeanPostProcessor

public class MyBeanPost implements BeanPostProcessor {

@Override 
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
 System.out.println("在初始化之前执行的方法"); return bean;
}

@Override 
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
  System.out.println("在初始化之后执行的方法"); 
  return bean; 
}
}

当这个类实现了BeanPostProcessor,并注入到Bean容器中后,Spring就会识别出这个后置处理器,并将该后置处理器置于每个Bean的生命周期中。

参考博客:Spring生命周期——简介

(6)xml 自动装配

根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入。

1)注入属性时根据属性名称自动注入
员工类Emp中有一个部门dept属性

<bean id="emp" class="com.atguigu.spring5.autowire.Emp" 
autowire="byName">bean> 

<bean id="dept" class="com.atguigu.spring5.autowire.Dept">bean>

2)注入属性时根据属性类型自动注入
员工类Emp中有一个部门dept属性,类型为Dept

<bean id="emp" class="com.atguigu.spring5.autowire.Emp" 
autowire="byType"> bean>  

<bean id="dept" class="com.atguigu.spring5.autowire.Dept">bean>
(7)外部属性文件

把外部properties 属性文件引入到spring 配置文件中

1)引入context 名称空间
Spring ——Bean(IOC)容器_第9张图片
2)使用context标签引入外部属性文件jdbc.properties

<context:property-placeholder location="classpath:jdbc.properties"/>

 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
	<property name="driverClassName" value="${prop.driverClass}">property>
	<property name="url" value="${prop.url}">property>
	<property name="username" value="${prop.userName}">property>
	<property name="password" value="${prop.password}">property>
 bean>

applicationContext.xml中的常用配置:

  • 用来容纳一些bean对象
  • :用来定义bean对象
  • :用来合并xml,将其他xml中的bean对象导入进来。
  • :用来给bean对象起别名
  • :可以直接在bean中用name起别名,而且可以起多个别名,中间用空格,逗号分割都可以。

2、IOC :Bean 管理(基于注解)

(1)什么是注解

1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
2)使用注解,注解作用在类上面,方法上面,属性上面
3)使用注解目的:简化xml 配置

(2)Spring中的常用注解

1)@Component
可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。

2)@Repository
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

3)@Service
通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

4)@Controller
通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

5)@Configuration:声明当前类是一个配置类,相当于一个Spring配置的xml文件。把一个类作为一个IoC容器和@Bean搭配使用,某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。

6) @Bean:表示注册一个名称为方法名的Bean对象到容器中。此时方法的名称就相当于bean标签中的id属性,方法返回值就相当于bean标签中的class属性。

  • @Bean配置的类的默认id是方法的名称,但是我们可以通过value或者name给这个bean取别名。@Bean(name="aservice")
  • value和name属性不能并存。 而且如果配置了value或者name,那么我们将无法在通过方法名称获取这个bean了。

7)@Import(config.class):可以引入一个配置类,将两个配置类整合到一起。

8) @ComponentScan(com.glp.config) : ComponentScan做的事情就是告诉Spring从哪里找到bean.哪些包需要被扫描, 一旦指定好之后Spring将会将在被指定的包及其下级的包(sub packages)中寻找bean。

@ComponentScan(com.glp.config)
public class config{
}

9) @Scope(“prototype”): 原生模式,不加的话默认就是单例模式。

注意:

  • @Component 、@Repository 、@Service只是用来分层没有具体的含义。 @Controller是用于做客户端请求的层。
  • @Component、@Repository、@Service、@Controller 在类上使用,如果能扫描到该类,则将该类注册到容器中。只会实例化为一个对象
  • @Bean 在方法上使用,根据需要可以注册多个Bean

说明:
在使用@Bean(name=“bean1”)给注册对象起别名时,可以用Bean的name属性,或者@Qualifier.

@Bean(name="bean1")
public A getA() {
     return new A();
 }

@Bean
@Qualifier("bean2")
public A getB() {
     return new A();
}

使用时可以用 @Qualifier通过名称从容器中引入:

 @Autowire
 @Qualifier("bean1")
 A testa; 
(3)基于注解方式实现对象创建

1)第一步 引入依赖
spring-aop-5.2.6.RELEASE.jar

2)第二步 开启组件扫描:将包中含有注解的组件注入到容器中

<context:component-scan base-package="com.atguigu">
context:component-scan>

3)第三步创建类,在类上面添加创建对象注解

@Component(value = "userService")
public class UserService { 
	public void add() { 
		System.out.println("service add......."); 
	} 
}

注意:

  • 在注解里面value属性值可以省略不写,
  • 默认值是类名称,首字母小写——UserService – userService
  • 等同于在xml中配置:
(4)开启组件扫描细节配置

filter context:include-filter ,设置扫描哪些内容

<context:component-scan base-package="com.atguigu" 
use-default-filters="false"> 
	<context:include-filter type="annotation" 
	expression="org.springframework.stereotype.Controller"/> 
context:component-scan>

context:exclude-filter:设置哪些内容不进行扫描

<context:component-scan base-package="com.atguigu"> 
	<context:exclude-filter type="annotation" 
	expression="org.springframework.stereotype.Controller"/> 
context:component-scan>

注意:
use-default-filters="false" 表示现在不使用默认filter,自己配置

(5)基于注解方式实现属性注入

1)@Autowired:根据属性类型进行自动装配
第一步把service 和dao 对象创建,在service 和dao 类添加创建对象注解
第二步在service 注入dao 对象,在service 类添加dao 类型属性,在属性上面使用注解

@Service 
public class UserService {
	@Autowired 
	private UserDao userDao; 
	public void add() { 
		System.out.println("service add......."); 
		userDao.add(); 
} }

注:
不需要添加set方法

2)@Qualifier:根据名称进行注入
@Qualifier 注解需要和上面@Autowired 一起使用

@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;

3)@Resource:可以根据类型注入,可以根据名称注入

//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;

注:
默认根据类型注入

@Bean所在的方法user2,要使用user1,可以用方法参数的方式注入,使用@Qualifier("user1")指明要使用的Bean对象。

	@Bean
    public User user2(@Qualifier("user1") User user1){};

4)@Value:注入普通类型属性

@Value(value = "abc") 
private String name;
@value("glp")
public void setName(String name){
	this.name =name;
}
(6)完全注解开发

1)创建配置类,替代xml配置文件

@Configuration //作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.atguigu"}) 
public class SpringConfig {
    @Bean
    public User user(){
    //定义了一个名称为方法名user1的user对象,并注册到容器中
        User user = new User();
        user.setUsername("烤鸭1");
        user.setPassword("123");
        user.setBirthday(new Date());
        return user;
    }
}

相当于在xml中配置了一个User对象

2)编写测试类

@Test
public void testService2() {
	//加载配置类
	ApplicationContext context = 
	new AnnotationConfigApplicationContext(SpringConfig.class); 
	
	UserService userService = 
	context.getBean("userService", UserService.class); 
	
	System.out.println(userService); 
	userService.add(); 
}

注:
ApplicationContext有两个实现类,一个ClassPathXmlApplicationContext用来获取xml中的Bean,一个AnnotationConfigApplicationContext用来获取注解中的Bean.
Spring ——Bean(IOC)容器_第10张图片

ApplicationContext Context = new AnnotationConfigApplicationContext(Test.class);
User user = (User)Context.getBean("getUser");

四、AOP

参考博客:Spring框架的核心组件——Aop

五、JdbcTemplate

参考博客:Spring——JdbcTemplate

六、Spring——事务

参考博客:Spring——事务管理

你可能感兴趣的:(Spring,spring,java,ioc)