Spring详解(二)Ioc的使用

1.什么是程序的耦合

前面我们了解到通过 Spring提供的 IoC容器,可以将对象间的依赖关系交由 Spring进行控制,避免硬编码所造成的过度程序耦合。那么什么是程序的耦合呢?

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。 在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个 准则就是高内聚低耦合。

我们在开发中,有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。

public class AccountServiceImpl implements IAccountService { 
	 private IAccountDao accountDao = new AccountDaoImpl();
  }

上面的代码表示:
业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种编译期依赖关系,应该在我们开发中杜绝。我们需要优化代码解决。

 public static void main(String[] args) throws Exception {  
  //1.注册驱动   
  //DriverManager.registerDriver(new com.mysql.jdbc.Driver());   	      
  Class.forName("com.mysql.jdbc.Driver"); 
  //2.获取连接  
   //3.获取预处理 sql 语句对象   
   //4.获取结果集   
   //5.遍历结果集  } 

在JDBC 操作中,注册驱动时,我们为什么不使用 DriverManager 的 register 方法,而是采 用 Class.forName 的方式? 原因就是: 我们的类依赖了数据库的具体驱动类(MySQL),如果这时候更换了数据库品牌(比如 Oracle),需要 修改源码来重新数据库驱动。这显然不是我们想要的。

2. 解决程序耦合的思路

当是我们讲解 jdbc 时,是通过反射来注册驱动的,代码如下:

Class.forName("com.mysql.jdbc.Driver");//此处只是一个字符串 

此时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译(运行就不要想了,没有驱动不可能运行成功的)。 同时,也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。 解决这个问题也很简单,使用配置文件配置。
解耦思路总结:
1.使用反射来创建对象,而避免使用new关键字。
2.通过读取配置文件来获取要创建类的全限定类名。

3.工厂模式解耦

在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。 那么,这个读取配置文件,创建和获取三层对象的类就是工厂。

4.Ioc的概念和作用

1、对象存哪去?
分析:由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。 到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。

所以我们的答案就是 在应用加载时,创建一个 Map,用于存放三层对象。我们把这个 map 称之为容器。

2、什么是工厂?
工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了变。

原来我们在获取对象时,都是采用 new 的方式。是主动的。
在这里插入图片描述
现在:我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。
在这里插入图片描述
这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。

3.Ioc的概念
控制反转IoC(Inversion of Control),是一种设计思想,把创建对象的权利交给框架。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,所谓控制反转就是:获得依赖对象的方式反转了。
4. Ioc的作用
削减计算机程序的耦合(解除我们代码中的依赖关系)。

5.Ioc的实现方式

IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。

Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

在这里插入图片描述
Ioc初体验
1.导入spring相关依赖

 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

2.创建实体类

public class User {
    private String username;
    private String password;
    private int age;

    public void show(){
        System.out.println("show方法执行了.....");
    }
}

3.在类路径下创建配置文件applicationContext.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:context="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
        http://www.springframework.org/schema/context/spring-context.xsd">
        
    <!-- bean标签:用于配置让spring创建对象,并且存入ioc容器之中 
    id属性:对象的唯一标识。  class 属性:指定要创建对象的全限定类名 
    此处默认调用无参构造函数-->
    
    <bean id="user" class="com.zua.domain.User">
    </bean>
</beans>

4.获取对象并调用方法

    //1.加载配置文件
        ApplicationContext ac=  new ClassPathXmlApplicationContext("applicationContext.xml");
       //2.从容器中获取bean
        User user = ac.getBean("user", User.class);
        //3。调用方法
        user.show();

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

6.Spring 基于 xml 的 Ioc 细节
6.1spring中工厂的结构图

在这里插入图片描述
在这里插入图片描述
6.1.1 BeanFactory 和ApplicationContext 的区别

BeanFactory 才是Spring 容器中的顶层接口。 ApplicationContext 是它的子接口。
BeanFactory 和ApplicationContext 的区别:创建对象的时间点不一样。
ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
BeanFactory:什么使用什么时候创建对象。

6.1.2 ApplicationContext 接口的实现类

ClassPathXmlApplicationContext: 它是从类的根路径下加载配置文件 推荐使用这种
FileSystemXmlApplicationContext: 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
AnnotationConfigApplicationContext: 当我们使用注解配置容器对象时,需要使用此类来创建spring容器。它用来读取注解。

6.2 IOC中bean标签和管理对象细节

6.2.1 bean标签

作用: 用于配置对象让spring来创建的。 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。

属性:

属性作用
id给对象在容器中提供一个唯一标识。用于获取对象。
class指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
scope指定对象的作用范围。
init-method指定类中的初始化方法名称。
destroy-method指定类中销毁方法名称。

scope的属性

singleton :默认值,单例的
prototype :多例的
request :WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中.
session :WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中.
global session:WEB项目中,应用在Portlet环境.如果没有Portlet环境那么globalSession相当于session.

6.2.2 bean的作用范围和生命周期
单例对象:scope=“singleton” 一个应用只有一个对象的实例。它的作用范围就是整个引用。
生命周期:
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
多例对象:scope=“prototype” 每次访问对象时,都会重新创建对象实例。
生命周期:
对象出生:当使用对象时,创建新的对象实例。
对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被java的垃圾回收器回收了。

 <bean id="user" class="com.zua.domain.User"  scope="prototype">
 //1.加载配置文件
        ApplicationContext ac=  new ClassPathXmlApplicationContext("applicationContext.xml");
       //2.从容器中获取bean
        User user = ac.getBean("user", User.class);
        User user1 = ac.getBean("user", User.class);

        System.out.println(user+"  "+user1);

在这里插入图片描述
6.2.3 实例化Bean的三种方式

1.第一种方式:使用默认无参构造函数。在默认情况下: 它会根据默认无参构造函数来创建类对象。如果bean中没有默认无参构造函数,将会创建失败。

 <bean id="user" class="com.zua.domain.User" >

2.Spring管理静态工厂-使用静态工厂的方法创建对象


```java
/**
 * 模拟一个静态工厂,创建用户
 */
public class staticFactory {

    public static User getUser(){
        return  new User();
    }
}
使用StaticFactory类中的静态方法getUser创建对象,并存入spring容器 
id属性:指定bean的id,用于从容器中获取 
class属性:指定静态工厂的全限定类名 
factory-method属性:指定生产对象的静态方法
```java
<bean id="staticFactory" class="com.zua.factory.staticFactory" factory-method="getUser"></bean>

对象的获取

 //2.从容器中获取bean
        User user = ac.getBean("staticFactory",User.class);

3.spring管理实例工厂-使用实例工厂的方法创建对象

/**
 * 模拟一个实例工厂,创建对象
 * 此工厂创建对象,必须先有工厂实例对象,在调用方法
 */
public class staticFactory {

    public User getUser(){
        return  new User();
    }
}

此种方式是: 先把工厂的创建交给spring来管理。 然后在使用工厂的bean来调用里面的方法
factory-bean属性:用于指定实例工厂bean的id。 factory-method属性:用于指定实例工厂中创建对象的方法。

 <bean id="instanceFactory" class="com.zua.factory.instanceFactory"></bean>
 <bean id="user" factory-bean="instanceFactory" factory-method="getUser"></bean>
 //1.加载配置文件
  ApplicationContext ac=  newClassPathXmlApplicationContext("applicationContext.xml");
 //2.从容器中获取bean
  User user = ac.getBean("user",User.class
7.依赖注入(DI)
7.1依赖注入的概念

依赖注入:Dependency Injection。它是spring框架核心ioc的具体实现。
我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。ioc解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用spring之后,就让spring来维护了。 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

7.2构造函数注入

顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让spring框架来为我们注入。
1.无参构造函数注入

前面已经介绍过。

2.有参构造函数

User实体类

    private String username;
    private String password;
    private int age;

    public User(String username, String password, int age) {
        this.username = username;
        this.password = password;
        this.age = age;
    }

2.配置文件
使用构造函数的方式,给User中的属性传值
要求: 类中需要提供一个对应参数列表的构造函数。
涉及的标签:

constructor-arg
属性:
index:指定参数在构造函数参数列表的索引位置
type:指定参数在构造函数中的数据类型
name:指定参数在构造函数中的名称 用这个找给谁赋值

上面三个都是找给谁赋值,下面两个指的是赋什么值的

value:它能赋的值是基本数据类型和String类型
ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean

    <bean id="user" class="com.zua.domain.User">
            <constructor-arg name="username" value="依赖注入"/>
            <constructor-arg name="password" value="DI"/>
            <constructor-arg name="age" value="5"/>
        </bean>

3.调用

  //1.加载配置文件
 ApplicationContext ac=  new ClassPathXmlApplicationContext("applicationContext.xml");
 //2.从容器中获取bean
  User user = ac.getBean("user",User.class);
  System.out.println(user);
  //User{username='依赖注入', password='DI', age=5}
7.3 Set方法注入

在类中需要注入成员的set方法
1.实体类

private String username;
    private int age;
    private Date birthday;
    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myProps;
      public void setUsername(String username) {
        this.username = username;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public void setMyStrs(String[] myStrs) {
        this.myStrs = myStrs;
    }
    public void setMyList(List<String> myList) {
        this.myList = myList;
    }
    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }
    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }
    public void setMyProps(Properties myProps) {
        this.myProps = myProps;
    }

2.配置文件

1.配置基本数据类型

property name="username"  value="依赖注入"/>
 <property name="age" value="15"/>

2.配置其它bean对象

<property name="birthday" ref="now"/>
<bean id="now" class="java.util.Date"></bean>

3.配置数组

<property name="myStrs" >
                <array>
                    <value>ioc</value>
                    <value>aop</value>
                </array>
 </property>

4.配置集合(list set)

  <property name="myList">
  	<list>
           <value>ioc</value>
           <value>aop</value>
	</list>
 </property>
<property name="mySet">
                <set>
                    <value>ioc</value>
                    <value>aop</value>
                </set>
 </property>

5.配置map properties

<property name="myMap">
      <map>
            <entry key="spring" value="ioc"></entry>
              <entry key="spring1" value="aop"></entry>
      </map>
</property>
<property name="myProps">
    <props>
        <prop key="ioc">控制反转</prop>
         <prop key="aop">面向切面</prop>
    </props>
 </property>

在注入集合数据时,只要结构相同,标签可以互换

List结构的: array, list,set
Map结构的 map,entry,props,prop

4.获取对象

//1.加载配置文件
 ApplicationContext ac=  new ClassPathXmlApplicationContext("applicationContext.xml");     
 //2.从容器中获取bean
 User user = ac.getBean("user",User.class);
 System.out.println(user);
 //User{username='依赖注入', age=15, birthday=Fri Jun 26 21:55:52 CST 2020,
 // myStrs=[ioc, aop], myList=[ioc, aop], mySet=[ioc, aop], 
 //myMap={spring=ioc, spring1=aop}, myProps={aop=面向切面, ioc=控制反转}}
8.基于注解的Ioc配置
8.1环境搭建

1.导入依赖

 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
</dependency>

2.使用注解管理配置的资源
模拟三层架构保存用户
目录结构
在这里插入图片描述
dao

public interface UserDao {

     void saveUser();
}
@Component
public class UserDaoImpl implements UserDao {
    public void saveUser() {
        System.out.println("持久层模拟保存用户.....");
    }
}

Service

public interface  UserService {
    void saveUser();
}
@Component
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    public void saveUser() {
        userDao.saveUser();
    }
}

3.创建spring的xml配置文件并开启对注解的支持

<!-- 告知 spring 创建容器时要扫描的包 -->
 <context:component-scan base-package="com.zua"></context:component-scan>

4.测试

public class UserTest {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        UserService service = ac.getBean("userServiceImpl", UserService.class);
        service.saveUser();
    }
}

5.结果
在这里插入图片描述

8.2常用注解的介绍
8.2.1用于创建对象

相当于xml配置中bean标签

<bean id="userService" class="com.zua.service.impl.UserServiceImpl"></bean>

@Component
作用: 把资源让spring来管理。相当于在xml中配置一个bean。
属性: value:指定bean的id。如果不指定value属性,默认bean的id是当前类的类名。首字母小写。

@Controller @Service @Repository
他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。
他们只不过是提供了更加明确的语义化。

@Controller:一般用于表现层的注解。
@Service:一般用于业务层的注解。
@Repository:一般用于持久层的注解。
细节:如果注解中有且只有一个属性要赋值时,且名称是value,value在赋值是可以不写。

8.2.2用于注入数据的

相当于xml property标签

<property name="userDao" ref="userDaoImpl"></property>

@Autowired
作用: 自动按照类型注入。
当使用注解注入属性时,set方法可以省略。它只能注入其他bean类型。当有多个类型匹配时,使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功。找不到就报错。
在这里插入图片描述
@Qualifier
作用: 在自动按照类型注入的基础之上,再按照Bean的id注入。它在给字段注入时不能独立使用,必须和@Autowire一起使用;但是给方法参数注入时,可以独立使用。
属性: value:指定bean的id。

@Resource
作用: 直接按照Bean的id注入。它也只能注入其他bean类型。
属性: name:指定bean的id。

@Value
作用: 注入基本数据类型和String类型数据的
属性: value:用于指定值

8.3 用于改变作用范围

相当于xml scope属性

bean id="userService" 
class="com.zua.service.impl.UserServiceImpl" scope="singleton">
<property name="userDao" ref="userDaoImpl"></property>
 </bean>

@Scope
作用: 指定bean的作用范围。
属性: value:指定范围的值。
取值:singleton 、 prototype、 request 、session 、globalsession

8.4 和生命周期相关的

@PostConstruct
作用: 用于指定初始化方法。

@PreDestroy
作用: 用于指定销毁方法。

9.Spring注解和xml的选择

注解的优势:
配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
XML的优势:
修改时,不用改源码。不涉及重新编译和部署。
在这里插入图片描述

10.Spring的纯注解配置

我们发现,之所以我们现在离不开xml配置文件,是因为我们有一句很关键的配置。

<!-- 告知spring框架在,读取配置文件,创建容器时,
扫描注解,依据注解创建对象,并存入容器中 --> 
<context:component-scan base-package="com.itheima">
</context:component-scan>

如果他要也能用注解配置,那么我们就离脱离xml文件又进了一步。

@Configuration
作用: 用于指定当前类是一个spring配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(有@Configuration注解的类.class)。
属性: value:用于指定配置类的字节码

@ComponentScan
作用: 用于指定spring在初始化容器时要扫描的包。作用和在spring的xml配置文件中的: <context:component-scan base-package=“com.itheima”/>是一样的。

属性: basePackages:用于指定要扫描的包。和该注解中的value属性作用一样。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值