这里教你怎么使用SpringIOC,你还是不懂SpringIOC?什么是控制反转?

在这里插入图片描述


写在前面: 最近学习Spring告一段落了,这文章可以简单的告诉你怎么使用Spring的IOC控制反转,以及IOC注解开发。 作者是一个学生没有能力写得太深,需要的可以去看看大佬们的手撕Spring源码
本文将告诉你:
IOC控制反转是什么?
如何使用Spring的IOC控制反转?
XML创建bean的三种形式
IOC控制反转的注解开发

如果对你有帮助可以点赞支持一下^ _ ^
公众号:小白编码

什么是Spring?

Spring是分层的Java SE/EE应用 full-stack轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。

Spring能做什么?

  • 方便解耦,简化开发
    通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
  • AOP编程的支持
    通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
  • 声明式事务的支持
    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
  • 方便程序的测试
    可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
  • 方便集成各种优秀框架
    Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
  • 降低JavaEE API的使用难度
    Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
  • Java源码是经典学习范例
    Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。

Spring的体系结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fU2GuDKZ-1590552750288)(C:\Users\JUN\AppData\Roaming\Typora\typora-user-images\image-20200522194850281.png)]

IOC控制反转是什么?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SEdGrKPh-1590552750291)()]

我们都知道Service层需要Dao层的支持,使用Dao获取数据处理业务:(这里需要AccountDao的实例)
在这里插入图片描述
上图使用了工厂获取实例。 目的就是为了解耦。

我获取AccountDao的实例的时候有两种方法:

  • 第一种:通过new AccountDaoImpl() 实例的方式
  • 第二种:通过工厂模式,获取AccountDao的实例

那么问题来了?到底什么是控制反转?

通过下图可知:获取实例的方式转变为从工厂里获取而不是直接new一个实例,它将获取实例的控制权 ,交给了BeanFactory来做,而不是自己直接面对实例资源。让工厂来面对实例资源。这种控制权发生了转变。就叫控制反转。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DEpPUt7q-1590552750296)()]

那么如何使用Spring的IOC控制反转?

开发前准备:

1.导入Maven依赖座标:或者官方下载: http://repo.springsource.org/libs-release-local/org/springframework/spring

<dependency>
            <!--SpringIOC依赖-->
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
</dependency>

依赖的结构图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oVXcKr2F-1590552750299)()]
2.准备好Dao层,以及Service层:

Dao层:(接口自己写)

public class AccountDaoImpl implements IAccountDao {

    @Override
    public void saveAccount() {
        System.out.println("保存一个账户");
    }
}

Service层:(接口自己写)

public class AccountService implements IAccountService {

    private IAccountDao iAccountDao =  new AccountDaoImpl();//这里我还是先使用new的方式

    @Override
    public void saveAccount() {
        iAccountDao.saveAccount();
    }
}

Xml配置IOC容器:

<?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标签:用于配置让spring创建对象,并且存入ioc容器之中 id属性:对象的唯一标识。
 	 class属性:指定要创建对象的全限定类名 -->
    <!--配置AccountService的实现类,放入容器-->
    <bean id="accountService" class="cn.codewhite.service.Impl.AccountService"></bean>
    <!--配置AccountDao的实现类,放入容器-->
    <bean id="accountDao" class="cn.codewhite.dao.Impl.AccountDaoImpl"></bean>

</beans>

测试程序:

public class Clinet {

    public static void main(String[] args) {



        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        
        //        IAccountService iAccountService = new AccountService();
        //在使用SpringIOC控制反转前,我们是通过new 的方式获取Service实例
        // 现在配置了IOC控制反转,new实例交给了Spring管理,根据配置的id获取容器里配置的实现类
        IAccountService accountService = (IAccountService) ac.getBean("accountService");
        
        //        IAccountDao accountDao = new AccountDaoImpl();原先是通过new方式获取Dao实例
        //通过字节码文件,获得运行时类
        IAccountDao accountDao = ac.getBean("accountDao", IAccountDao.class);

        System.out.println(accountService);
        System.out.println(accountDao);
		//测试保存账户
        accountService.saveAccount();

    }
}

运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2wiM2gRi-1590552750300)()]

结论:通过这个简单的案例,我们就知道了,可以配置将实现类,配置到Spring的IOC容器中,在需要获取Dao或者Service等其他实现类的时候,从IOC容器中获取。而不是直接new的方式获取实例。实现了控制反转
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-axbjnWmp-1590552750301)()]

XML创建bean的三种形式:

创建Bean,也就是配置bean之后(放入IOC容器),通过getBean()来获取实例。

第一种方式:

使用默认构造函数创建。

​ 在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
​ 采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数(空参构造器),则对象无法创建。

<bean id="accountService" class="cn.codewhite.service.Impl.AccountServiceImpl"></bean>

AccountService实现类:

public class AccountServiceImpl implements IAccountService {
  	//必须是空参构造器,否则第一种方式无法创建对象
    public AccountServiceImpl() {
        
    }

    @Override
    public void saveAccount() {
        System.out.println("service中的saveAccount()执行了");
    }
}

配置完之后,就可以通过根据配置的id获取实例。

    	//1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取Bean对象,获取实例
        IAccountService accountService1 = (IAccountService) ac.getBean("accountService");

第二种方式:

使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)

<!--配置工厂到容器-->
<bean id="instanceFactory" class="cn.codewhite.factory.InstanceFactory"></bean>
<!--配置accountservice实例,是从工厂的getAccountService里获取的实例-->
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>

工厂类: (AccountServce实现类看上边)

/**
 * 模拟一个工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
 */
public class InstanceFactory {
	//获取AccountService的实例	
    public IAccountService getAccountService() {
        return new AccountServiceImpl();
    }
}

测试: 此时的实例service的实例是从配置过的IOC里的工厂里获取的实例。

public class Clinet {

    public static void main(String[] args) {

        //1.获取核心容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.根据id获取Bean对象
        IAccountService accountService = (IAccountService) ac.getBean("accountService");
        accountService.saveAccount();

    }
}

结果:

service中的saveAccount()执行了

第三种方式:

使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)

xml:(配置工厂里的静态方法)

    <bean id="accountService" class="cn.codewhite.factory.StaticFactory" factory-method="getAccountService"></bean>

工厂类:

public class StaticFactory {
	//静态方法
    public static IAccountService getAccountService() {
		//返回AccontService实例
        return new AccountServiceImpl();
    }
}

测试如第二种方式的结果。

bean标签

作用:用于配置对象让spring来创建的。

默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。

属性:

  • id:给对象在容器中提供一个唯一标识用于获取对象。

  • class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。

  • scope:指定对象的作用范围。常用: singleton :默认值,单例的. prototype :多例的.

  • request :WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中. 作用于web应用的请求范围
  • session :WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中.作用于web应用的会话范围
  • global session :WEB项目中,应用在Portlet环境.如果没有Portlet环境那么globalSession相当于session. 作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
  • init-method:指定类中的初始化方法名称。

  • destroy-method:指定类中销毁方法名称。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-csriD7mt-1590552750302)()]

bean依赖注入

**依赖注入:Dependency Injection。(DI)**它是spring框架核心ioc的具体实现。 简单说就是:(为指定的类里面的属性赋值实例。相当于赋值,)

依赖关系的管理:我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。ioc解耦只是降低他们的依赖关系,但不会消除。

IOC的作用: 降低程序间的耦合(依赖关系)

例如:我们的业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用spring之后,就让spring来维护了。 简单的说,就是坐等**框架把持久层对象传入业务层,而不用我们自己去获取。**在当前类需要用到其他类的对象,我们只需要在配置文件中说明

构造函数注入:

使用的标签:constructor-arg
标签出现的位置:bean标签的内部

标签中的属性:
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
name: ★用于指定给构造函数中指定名称的参数赋值 常用的
以上三个用于指定给构造函数中哪个参数赋值
value: 用于提供基本类型和String类型的数据
ref: 用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象

xml配置:

<!--将Service放入IOC容器,以及配置Service里的属性依赖-->
<bean id="accountService" class="cn.codewhite.service.Impl.AccountServiceImpl">
   		 <!--配置Service里的String name属性:赋值为:小白-->
        <constructor-arg name="name" value="小白"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
    	<!--这种类型需要引入,所以使用ref引入-->
        <constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>

Service:

public class AccountServiceImpl implements IAccountService {

    private String name;
    private Integer age;
    private Date birthday;

    public AccountServiceImpl(String name, Integer age, Date birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }
}

set方法注入(Bean中必须设有Set方法) ★ (更常用)

涉及的标签:property
出现的位置:bean标签的内部

标签的属性:
name: 用于指定注入时所调用的set方法名称
value: 用于提供基本类型和String类型的数据
ref: 用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势: 创建对象时没有明确的限制,可以直接使用默认构造函数
弊端: 如果有某个成员必须有值,则获取对象是有可能set方法没有执行。

xml:

<!--将Service放入IOC容器,以及配置Service里的属性依赖-->
<bean id="accountService2" class="cn.codewhite.service.Impl.AccountServiceImpl2">
    <!--配置Service里的String name属性:通过set方法赋值为:小白-->
    <property name="name" value="小白"></property>
    <property name="age" value="18"></property>
    <property name="birthday" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>

Service: (需要提供set方法)

public class AccountServiceImpl2 implements IAccountService {
	private String name;
	private Integer age;
	private Date birthday;

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

	public void setAge(Integer age) {
	    this.age = age;
	}

	public void setBirthday(Date birthday) {
 	   this.birthday = birthday;
	}
}

复杂类型的注入/集合类型的注入:

用于给List结构集合注入的标签: listarrayset
用于个Map结构集合注入的标签: mapprops
结构相同,标签可以互换

Bean.xml:

<!--将Service放入IOC容器,以及配置Service里的属性依赖-->
<bean id="accountService3" class="cn.codewhite.service.Impl.AccountServiceImpl3">
     <!--以下是List结构-->
    <property name="myStrs">
        <array>
            <value>aaa</value>
        </array>
    </property>
    <property name="myList">
        <list>
            <value>AA</value>
        </list>
    </property>
    <property name="mySet">
        <set>
            <value>AAA</value>
        </set>
    </property>
    <property name="myMap">
        <!--以下是Map结构-->
        <map>
            <entry key="keyA" value="AAA"></entry>
            <entry key="keyB" >
                <value>BBB</value>
            </entry>
            <entry key="keyC" value="CCC"></entry>
        </map>
    </property>
    <property name="myProps">
        <props>
            <prop key="PropA">AAA</prop>
        </props>
    </property>
</bean>

Servcie: (set方法我这里没写!需要自己补充,否则无法注入)

public class AccountServiceImpl3 implements IAccountService {

    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String, String> myMap;
    private Properties myProps;
}

IOC控制反转的注解开发

环境搭建:

Dao层:

AccountDaoImpl:

@Repository("accountDao1")
public class AccountDaoImpl implements IAccountDao {

    @Override
    public void saveAccount() {
        System.out.println("accountDao1中:保存一个账户");
    }
}

AccountDaoImpl2:

@Repository("accountDao2")
public class AccountDaoImpl2 implements IAccountDao {

    @Override
    public void saveAccount() {
        System.out.println("accountDao2中:保存一个账户");
    }
}

Service:

@Service("accountService")
@Scope("singleton")
public class AccountServiceImpl implements IAccountService {

    //    @Autowired
//    @Qualifier("accountDao1")
    @Resource(name = "accountDao2")
    private IAccountDao iAccountDao;

    @PostConstruct
    public void init() {
        System.out.println("init创建。。。。");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("destroy销毁。。。。");
    }


    @Override
    public void saveAccount() {
        iAccountDao.saveAccount();
    }
}

绑定使用注解的包:(非常重要)

<?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">

    <!--配置cn.codewhite包下所有的注解都能被扫描到-->
    <context:component-scan base-package="cn.codewhite"></context:component-scan>
</beans>

用于创建对象的注解

相当于:<bean id="" class="">

@Component

作用: 把资源让spring来管理。相当于在xml中配置一个bean。

属性: value:指定bean的id。 如果不指定value属性,默认bean的id是当前类的类名。首字母小写。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cRVYodN5-1590552750303)()]

@Controller @Service @Repository

他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的 .他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰

@Controller:一般用于表现层的注解。

@Service:一般用于业务层的注解。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4iJsn4BJ-1590552750303)()]

@Repository:一般用于持久层的注解。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cfz2zFsF-1590552750304)()]

细节:如果注解中有且只有一个属性要赋值时,且名称是value,value在赋值是可以不写。

用于注入数据(依赖注入)的注解

相当于:<property name="" ref="">
<property name="" value="">

@Autowired

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y9WfPmY9-1590552750305)(]

作用: 自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
如果ioc容器中没有任何bean的类型和要注入的变量类型匹配则报错。
如果Ioc容器中有多个类型匹配时: 出现位置:可以是变量上,也可以是方法上
细节:在使用注解注入时,set方法就不是必须的了,可以省略。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VZzV8Gjl-1590552750305)()]

@Qualifier

作用:按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。必须和@Autowire一起使用;但是给方法参数注入时,可以独立使用。
属性:
value:用于指定注入bean的id。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QDehRpXi-1590552750306)()]

AccountDao1:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7vsmEoK7-1590552750306)()]
调用Service结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n0ToOng2-1590552750307)()]

给方法传入:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tg3iHo1X-1590552750309)()]

@Resource

作用: 直接按照Bean的id注入。可以独立使用。它也只能注入其他bean类型。

属性: name:指定bean的id。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jdeg0PzU-1590552750310)()]

调用Service测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S64q7O2G-1590552750310)()]
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上q述注解实现。
另外,集合类型的注入只能通过XML来实现。

@Value

作用:用于注入基本类型和String类型的数据 ,
属性:value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
SpEL的写法:${表达式}

注入配置文件中的key:(需要使用@PropertySource(“classpath:jdbcConfig.properties”))引用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9S9LNgPM-1590552750311)()]

配置文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=123

用于改变作用范围的注解

相当于:<bean id="" class="" scope="">

@Scope

作用: 指定bean的作用范围。

属性: value:指定范围的值。

取值:(singleton或prototype 常用)request,session,globalsession

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7FaDEjrM-1590552750311)()]

测试是否单例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mhjncKuA-1590552750312)(C:\Users\JUN\AppData\Roaming\Typora\typora-user-images\image-20200524153228839.png)]

和生命周期相关的注解:(了解)

相当于:<bean id="" class="" init-method="" destroy-method="" />

@PostConstruct

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

@PostConstruct

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oTirMtjh-1590552750313)()]
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vEduFsA9-1590552750313)()]

写在后边:

写得不好,请见谅,如果需要PDF版的可以找我。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章