Spring介绍及Ioc原理探究及使用

一、Spring

官网 : http://spring.io/

官方下载地址 : https://repo.spring.io/libs-release-local/org/springframework/spring/

GitHub : https://github.com/spring-projects

1. Spring概述

Spring是一个支持快速开发Java EE应用程序的框架。它提供了一系列底层容器和基础设施,并可以和大量常用的开源框架无缝集成,可以说是开发Java EE应用程序的必备。

java-spring

Spring最早是由Rod Johnson这哥们在他的《Expert One-on-One J2EE Development without EJB》一书中提出的用来取代EJB的轻量级框架。随后这哥们又开始专心开发这个基础框架,并起名为Spring Framework。

随着Spring越来越受欢迎,在Spring Framework基础上,又诞生了Spring Boot、Spring Cloud、Spring Data、Spring Security等一系列基于Spring Framework的项目。

简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术

image-20210312205537001

2. spring模块组成(了解)

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式

image-20210312210321586

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

3. Spring优点

方便解耦,简化开发:

  • Spring就是一个大工厂,专门负责生成Bean,可以将所有对象创建和依赖关系维护由Spring管理

AOP编程的支持:

  • Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能

声明式事务的支持:

  • 只需要通过配置就可以完成对事务的管理,而无需手动编程

方便程序的测试:

  • Spring对Junit4支持,可以通过注解方便的测试Spring程序

方便集成各种优秀框架:

  • Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的支持

降低JavaEE API的使用难度Spring:

  • 对JavaEE开发中一些难用的API(JDBC、JavaMail、远程调webservice用等),都提供了封装,使这些API应用难度大大降低

二、IOC容器

在学习Spring框架时,我们遇到的第一个也是最核心的概念就是容器。

什么是容器?容器是一种为某种特定组件的运行提供必要支持的一个软件环境。例如,Tomcat就是一个Servlet容器,它可以为Servlet的运行提供运行环境。类似Docker这样的软件也是一个容器,它提供了必要的Linux环境以便运行一个特定的Linux进程。

通常来说,使用容器运行组件,除了提供一个组件运行环境之外,容器还提供了许多底层服务。例如,Servlet容器底层实现了TCP连接,解析HTTP协议等非常复杂的服务,如果没有容器来提供这些服务,我们就无法编写像Servlet这样代码简单,功能强大的组件。早期的JavaEE服务器提供的EJB容器最重要的功能就是通过声明式事务服务,使得EJB组件的开发人员不必自己编写冗长的事务处理代码,所以极大地简化了事务处理。

Spring的核心就是提供了一个IoC容器,它可以管理所有轻量级的JavaBean组件,提供的底层服务包括组件的生命周期管理、配置和组装服务、AOP支持,以及建立在AOP基础上的声明式事务服务等。

1. IOC原理

Spring提供的容器又称为IoC容器,什么是IoC?

IoC全称Inversion of Control,直译为控制反转。那么何谓IoC?在理解IoC之前,我们先看看通常的Java组件是如何协作的。

  1. 创建一个UserDao接口
public interface UserDao {

    /**
     *功能描述: 获取用户信息
     * @author jiaoqianjin
     * @date 2021/3/12
    */
    void getUser();
}
  1. 创建Dao的实现类
public class UserDaoImpl implements UserDao {

    public void getUser() {
        System.out.println("获取用户数据");
    }
}
  1. 编写UserService的接口
public interface UserService {

    /**
     *功能描述:获取用户信息
     * @author jiaoqianjin
     * @date 2021/3/12
    */
    void getUser();
}
  1. 编写Service的实现类
public class UserServiceImpl implements UserService {
    UserMysqlImpl userDao = new UserMysqlImpl();
    /**
     * 功能描述:获取用户信息
     *
     * @author jiaoqianjin
     * @date 2021/3/12
     */
    public void getUser() {
        userDao.getUser();
    }
}
  1. 测试
public class UserTest {

    @Test
    public void testUser(){
        UserService service = new UserServiceImpl();
        service.getUser();
    }
}

上述的逻辑是正常的一个获取一个用户信息的流程,现在我们修改一下,再去根据UserDao接口实现一个类,返回不同的信息

public class UserMysqlImpl implements UserDao {
    public void getUser() {
        System.out.println("从mysql中获取数据");
    }
}

紧接着我们要去使用MySql的话 , 我们就需要去service实现类里面修改对应的实现

public class UserServiceImpl implements UserService {
//    UserDaoImpl userDao = new UserDaoImpl();
    UserMysqlImpl userDao = new UserMysqlImpl();
    /**
     * 功能描述:获取用户信息
     *
     * @author jiaoqianjin
     * @date 2021/3/12
     */
    public void getUser() {
        userDao.getUser();
    }
}

在假设, 我们再增加一个Userdao的实现类

public class UserDaoOracleImpl implements UserDao {
    /**
     * 功能描述: 获取用户信息
     *
     * @author jiaoqianjin
     * @date 2021/3/12
     */
    public void getUser() {
        System.out.println("从Oracle获取数据");
    }
}

那么我们要使用Oracle , 又需要去service实现类里面修改对应的实现 . 假设我们的这种需求非常大 , 这种方式就根本不适用了

从上面的例子可以看出,如果一个系统有大量的组件,其生命周期和相互之间的依赖关系如果由组件自身来维护,不但大大增加了系统的复杂度,而且会导致组件之间极为紧密的耦合,继而给测试和维护带来了极大的困难。

解决这一问题的核心方案就是IoC。

传统的应用程序中,控制权在程序本身,程序的控制流程完全由开发者控制,例如:

UserServiceImpl创建了UserMysqlImpl组件,使用Oracle实现的时候又需要创建UserDaoOracleImpl组件,这种模式的缺点是,一个组件如果要使用另一个组件,必须先知道如何正确地创建它。

如何解决?

例如,UserServiceImpl自己并不会创建userDao,而是等待外部通过setUserDao()方法来注入一个userDao

我们可以在需要用到它的地方 , 不去实现它 , 而是留出一个接口 , 利用set , 我们去代码里修改下

public class UserServiceImpl implements UserService {
    //    UserDaoImpl userDao = new UserDaoImpl();
//    UserMysqlImpl userDao = new UserMysqlImpl();
    
    private UserDao userDao;

    /**
     * 功能描述: 利用set注入
     *
     * @param userDao 接口实现类
     * @author jiaoqianjin
     * @date 2021/3/12
     */
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    /**
     * 功能描述:获取用户信息
     *
     * @author jiaoqianjin
     * @date 2021/3/12
     */
    public void getUser() {
        userDao.getUser();
    }
}

测试

@Test
    public void testUser1() {
        UserServiceImpl service = new UserServiceImpl();
        // 使用mysql实现
        service.setUserDao(new UserMysqlImpl());
        service.getUser();
        //那我们现在又想用Oracle去实现呢
        service.setUserDao(new UserDaoOracleImpl());
        service.getUser();
    }

不直接new一个UserDao,而是注入一个UserDao,这个小小的改动虽然简单,却带来了一系列好处:

以前所有东西都是由程序去进行控制创建 , 而现在是由我们自行控制创建对象 , 把主动权交给了调用者 . 程序不用去管怎么创建,怎么实现了 . 它只负责提供一个接口 .

这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 这也就是IOC的原型 !

三、Spring入门

1. 入门案例

1. 导入Maven依赖
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>5.1.10.RELEASE</version>
</dependency>
2. 编写一个User实体类
public class User {
    private Integer id;

    private String name;

    private String phone;

    public void show(){
        System.out.println("Hello,"+ name );
    }
}
3. 编写一个Spring文件

beans.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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

   <!--bean就是java对象 , 由Spring创建和管理-->
   <bean id="user" class="com.shida.entity.User">
       <property name="name" value="焦前进"/>
   </bean>

</beans>
4. 测试
    @Test
    public void test(){
        //解析beans.xml文件 , 生成管理相应的Bean对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //getBean : 参数即为spring配置文件中bean的id .
        User user = (User) context.getBean("user");
        user.show();
    }

回到我们的IOC原理案例

新增一个Spring配置文件beans.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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="MysqlImpl" class="com.shida.dao.impl.UserMysqlImpl"/>
    <bean id="OracleImpl" class="com.shida.dao.impl.UserDaoOracleImpl"/>

    <bean id="ServiceImpl" class="com.shida.service.impl.UserServiceImpl">
        <!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写-->
        <!--引用另外一个bean , 不是用value 而是用 ref-->
        <property name="userDao" ref="OracleImpl"/>
    </bean>
    
</beans>

测试

    @Test
    public void testUser2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
        serviceImpl.getUser();
    }

OK , 到了现在 , 我们彻底不用再程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !

2. IOC创建对象方式

1. 通过无参构造方法创建
1. User实体类
public class User {
    private Integer id;

    private String name;

    private String phone;

    public User() {
        System.out.println("user无参构造方法");
    }

    public void setName(String name) {
        this.name = name;
    }

    public void show(){
        System.out.println("Hello,"+ name );
    }
}
2. beans.xml
<bean id="user" class="com.shida.entity.User">
    <property name="name" value="焦前进"/>
</bean>
3. 测试类
@Test
public void test(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   //在执行getBean的时候, user已经创建好了 , 通过无参构造
   User user = (User) context.getBean("user");
   //调用对象的方法 .
   user.show();
}

结果可以发现,在调用show方法之前,User对象已经通过无参构造初始化了

2. 通过有参构造创建
1. User实体类
public class User {
    private Integer id;

    private String name;

    private String phone;

//    public User() {
//        System.out.println("user无参构造方法");
//    }
    public User(String name) {
        System.out.println("user有参构造方法");
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void show(){
        System.out.println("Hello,"+ name );
    }
}
2. beans.xml

①根据index参数下标设置

<!-- 第一种根据index参数下标设置 -->
<bean id="user" class="com.shida.entity.User">
    <!-- index指构造方法 , 下标从0开始 -->
    <constructor-arg index="0" value="焦前进"/>
</bean>```

②根据参数名字设置

<!-- 第二种根据参数名字设置 -->
<bean id="user" class="com.shida.entity.User">
    <!-- name指参数名 -->
    <constructor-arg name="name" value="焦前进"/>
</bean>

③根据参数类型设置

<!-- 第三种根据参数类型设置 -->
<bean id="user" class="com.shida.entity.User">
    <constructor-arg type="java.lang.String" value="焦前进"/>
</bean>

3. Spring配置

1. 别名
<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="user" alias="userNew"/>
2. Bean的配置
<!--bean就是java对象,由Spring创建和管理-->

<!--
   id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
   如果配置id,又配置了name,那么name是别名
   name可以设置多个别名,可以用逗号,分号,空格隔开
   如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;

class是bean的全限定名=包名+类名
-->
<bean id="user" name="user" class="com.shida.entity.User">
   <property name="name" value="焦前进"/>
</bean>

四、依赖注入(手动装配)

依赖注入(Dependency Injection,DI)。

  • 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
  • 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

1. 构造器注入

2. Set方法注入

1. 创建Address与Student实体类
public class Address {

    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}
public class Student {

    private String name;

    private Address address;

    private String[] books;

    private List<String> hobbys;

    private Map<String,String> card;

    private Set<String> games;

    private String wife;

    private Properties info;
    // set get toString()
}
2.注入 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="addr" class="com.shida.entity.Address">
        <property name="address" value="河南新乡"/>
    </bean>


    <bean id="student" class="com.shida.entity.Student">
        <!--    1. 常量注入-->
        <property name="name" value="焦前进"/>

        <!--    2. bean注入-->
        <property name="address" ref="addr"/>

        <!--    3. 数组注入-->
        <property name="books">
            <array>
                <value>Java入门很简单</value>
                <value>Mysql入门很简单</value>
            </array>
        </property>

        <!--    4. 集合注入-->
        <property name="hobbys">
            <list>
                <value>打篮球</value>
                <value>写bug</value>
            </list>
        </property>

        <!--    5. map注入-->
        <property name="card">
            <map>
                <entry key="邮政" value="123456"></entry>
                <entry key="建设" value="654321"></entry>
            </map>
        </property>

        <!--    6. set注入-->
        <property name="games">
            <set>
                <value>王者荣耀</value>
                <value>和平精英</value>
            </set>
        </property>

        <!--    7. null注入-->
        <property name="wife"><null/></property>

        <!--    8. properties注入-->
        <property name="info">
            <props>
                <prop key="学号">20170624215</prop>
                <prop key="姓名">焦前进</prop>
            </props>
        </property>
    </bean>
</beans>

3. 测试
public class StudentTest {

    @Test
    public void test1() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) applicationContext.getBean("student");

        System.out.println(JSONUtil.toJsonPrettyStr(student));
    }
}

推荐hutool工具类

HuTool使用文档

<!--    hutool的java开发工具包    -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.6.3</version>
        </dependency>

3. 注解注入

1、在spring配置文件中引入context文件头

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

五、注解注入

1. 新建dog、cat、people实体类

@ToString
public class Cat {
    public void shout() {
        System.out.println("miao~");
    }
}
@ToString
public class Dog {
    public void shout() {
        System.out.println("wang~");
    }
}
@ToString
public class User {
    private Cat cat;
    
    private Dog dog;
    
    private String str;
    
    // set get
    
}

2. beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dog" class="com.shida.entity.Dog"/>
    <bean id="cat" class="com.shida.entity.Cat"/>

    <bean id="user" class="com.shida.entity.User">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
        <property name="str" value="焦前进"/>
    </bean>
</beans>

3. 测试

public class UserTest {
    @Test
    public void test() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        User user = applicationContext.getBean("user", User.class);
        user.getCat().shout();
        user.getDog().shout();

    }

4. 使用注解 修改

将User类中的set方法去掉,使用@Autowired注解

@ToString
public class User {
    @Autowired
    private Cat cat;

    @Autowired
    private Dog dog;
    private String str;

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }

    public String getStr() {
        return str;
    }
}

在spring配置文件中引入context文件头

xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

完整的spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

	<!--开启属性注解支持!-->
    <context:annotation-config/>

    <bean id="dog" class="com.shida.entity.Dog"/>
    <bean id="cat" class="com.shida.entity.Cat"/>
    <bean id="user" class="com.shida.entity.User"/>

</beans>

六、使用注解开发

1. 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"
      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">

</beans>

我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!

2、配置扫描哪些包下的注解

<!--指定注解扫描包-->
    <context:component-scan base-package="com.shida.entity"/>

3、在指定包下编写类,增加注解

@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {}

4. 属性注入

使用注解注入属性

1、可以不用提供set方法,直接在直接名上添加@value(“值”)

image-20210313221901813

2、如果提供了set方法,在set方法上添加@value(“值”);

image-20210313222113414

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

5. 总结

XML与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,开发简单方便

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean
  • 注解完成属性注入
  • 使用过程中, 可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>  

作用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值