2019.6.12 Spring_IOC

本文深入解析Spring框架的核心概念,包括IOC控制反转、AOP面向切面编程及依赖注入DI,阐述Spring如何简化Java EE开发,降低API使用难度,并详细介绍Spring注解如@Autowired、@Qualifier、@Resource、@Service的使用方法。

简介

(1)spring是一个分层式轻量级的一站式开源框架,其核心内容是ioc(控制反转)和aop(面向切面)。
(2)spring是一站式框架,由于其在分层开发中每一层都有对应的技术来完成:

ioc配置bean和装配bean的属性(核心点)
1.(传统)基于 XML 文件的方式;(演进)基于注解的方式(基于注解配置 Bean;
基于注解来装配 Bean 的属性
2.组件扫描(component scanning):  Spring 能够从 classpath 下自动扫描,
 侦测和实例化具有特定注解的组件. 
3.当在组件类上使用了特定的注解之后, 还需要在 Spring 的配置文件中
声明 <context:component-scan> 
4.<context:component-scan> 元素还会自动注册 AutowiredAnnotationBeanPostProcessor 实例, 
该实例可以自动装配具有 @Autowired 和 @Resource 、@Inject注解的属性.

spring便捷

  • 方便解耦、简化开发:spring是一个大工厂,可以创建和维护所有对象和其依赖关系,这些都直接交给spring去管理。
  • aop编程的支持:Spring 提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
  • 声明式事务的支持:只需要通过配置就可以完成对事务的管理,而无需手动编程。
  • 方便程序的测试:Spring 对 Junit4 支持,可以通过注解方便的测试 Spring 程序
  • 方便集成各种优秀框架:Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支持。
  • 降低 JavaEE API 的使用难度:Spring 对 JavaEE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等),都提供了封装,使这些 API 应用难度大大降低

ioc(inversion of control 控制反转)

控制反转(ioc)就是将对象的创建权反转给spring。这样的好处是:实现程序的解耦。
接触spring后创建对象的两种方式
(1)spring中可以通过IOC来创建对象(要在xml配置文件中配置bean)。
(2)或者直接传统自己new对象(这样就不用在xml配置文件中配置该类的bean了,省了在xml中配置该类的bean了)

下面分析下控制反转如何实现解耦:
(1)传统我们开发中创建对象是通过面向对象编程:Student stu =new Student()自己new完成对象的创建,但是这样创建方式的拓展性不太好,后期出现面向接口编程。Person per = new Student();这样的好处是person接口可以接收更多具体对象的类型,不仅仅是单一对象类型对象了。
(2)面向接口开发后期 发现,虽然面向接口编程了,但是切换具体实现类,还是需要修改源代码,如学生(Student)对象如果改为老师对象(Teacher)如:Person per = new Student()还是要Person per = new Teacher(),还是要自己手动改源代码。通常如果这句接口代码在多个类中,要一个一个去改变,所以很繁琐,所以这样后面开发还是不能充分解耦接口和具体实现类之间的联系。
(3)工厂模式可以实现接口和具体实现类的松散解耦,还没有完全,因为还要在工厂类中手动修改源代码,但只在工厂类一个类中修改了,不用在所有用到接口和具体类的各个代码模块中一个一个修改了,后面在工厂模式基础上开始加入配置文件和反射技术就彻底完成了接口和具体实现类之间的解耦(工厂模式----反射技术----配置文件),而且和工厂类也解耦了。
(4)ioc底层实现:首先创建一个工厂类,其中获取类对象的方法getBean(String id)------------------然后通过dom4j等解析xml的技术(通过获取标签bean下的元素id对应的class元素的字符串内容,作为类的全路径名称)完成xml配置文件的解析()------------------------最后在工厂类中通过反元素id射技术(Class.forName(“xxx.class”)—然后创建这个字节码文件对应的类对象,返回就完成了类的实例创建

在这里插入图片描述

结合配置文件演示

applicationcontext.xml就是spring的核心配置文件,下面配置的是bean内容。就是(IOC);注意DI(依赖注入),它也是伴随IOC产生的,就是对spring管理的bean类所依赖的属性进行注入管理的(就是类似于在配置文件中给被管理的类的属性赋初始值)。
IOC :控制反转,将对象的创建权交给了 Spring.
DI :Dependency Injection 依赖注入.需要有 IOC 的环境,Spring 创建这个类的过程中,Spring 将类的依赖的属性设置进去,通过property标签。

<?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="user" class="spring.ov.User">
 <property name="str" value="abc"></property>
 <property name="sex" value="男"></property>
 <property name="name" value="孙祥"></property>
 <property name="age" value="27"></property>
</bean>
</beans>

spring 框架的结构

在这里插入图片描述

spring框架中核心对象——工厂(容器)

ApplicationContext工厂接口和BeanFactory工厂接口
(1)这两个都是spring的工厂接口,一个过时一个现在常用的,区别:beanfactory接口是在getbean方法时才生成管理类的实例,而applicationcontext是在加载配置文件applicationcontext.xml的时候就会创建管理的类的实例。

配置文件中参数说明

配置文件中对bean实例的相关设置

<?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="user" class="spring.ov.User" scope="singleton">
 <property name="str" value="abc"></property>
</bean>

</beans>

id属性其中bean标签内id属性是给bean起个唯一标识符的作用。bean下id和name相似,都是给该bean标识作用,但id起的标识符内不能有特殊字符,name可以由特殊字符,其他基本相似。
scope属性表示bean的作用范围。spring默认bean是单例范围,还有**多例范围用:prototype。**不常用的取值:request(web项目中,spring创建的bean对象被存放于request域中)、session(web项目中,bean对象被存放于session域中)。
init-method和destroy-method属性表示bean初始化时执行的方法还有销毁时执行的方法。单例的bean对象销毁是在工厂对象关闭时销毁。

配置文件中创建bean的三种方式

配置文件中对bean实例的相关设置
第一种是类的无参构造方式

<bean id ="user" class = "sprign.vo.User">
</bean>

第二种是工厂类静态方法创建bean

public class Bean2Factory {

	public static Bean2 getBean2(){
		return new Bean2();
	}
}

<!-- 方式二:静态工厂实例化 Bean -->
											 //被spring管理的类是工厂类,但该类方法下创建实例,所以要声明出方法
<bean id="bean2" class="cn.itcast.spring.demo3.Bean2Factory"  
factory-method="getBean2"/>

第三种就是工厂类的普通获取对象的方法获取bean

public class Bean3Factory {

	public Bean3 getBean3(){
		return new Bean3();
	}
}

<bean id="bean3Factory" class="cn.itcast.spring.demo3.Bean3Factory"></bean>

//注意创建上面bean3的实例的话,id标识要以下面bean3为准奥,上面那个id只是负责定位上面那个类,下面才是入口
//通过下面id确定工厂类的id名为bean3Factory,即定位到cn.itcast.spring.demo3.Bean3Factory类,并且定位
//到工厂中factory-method方法。最终就完成bean3对象的定位输出。
<bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>

配置文件中对bean类下属性的管理(设置)

(1)(构造函数方式属性注入,这情况下属性的标识符是 constructor-arg)通过构造方法方式设置属性内容(注入属性内容)

	<bean id="car" class="cn.itcast.spring.demo4.Car">
		<constructor-arg name="name" value="保时捷"/>
		<constructor-arg name="price" value="1000000"/>
	</bean>

(2)(属性的set方法的属性注入,这情况下属性的标识符是 property)通过类中set方法注入属性内容(就是给属性赋值,以前是自己new对象,然后调用set方法给属性赋值,现在通过配置文件设置属性值)

package spring.ov;

public class User {
private String name;
private String sex;
private int age;
public String str;
public String getName() {
	return name;
}
public void setName(String name) {
	this.name = name;
}
public String getSex() {
	return sex;
}
public void setSex(String sex) {
	this.sex = sex;
}
public int getAge() {
	return age;
}
public void setAge(int age) {
	this.age = age;
}
@Override
public String toString() {
	return "User [name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
public String getStr() {
	return str;
}
public void setStr(String str) {
	this.str = str;
}
public void query() {
	
	System.out.println(str);
}

}
===============================================================
//!!!!!!!!!!!!!!将上面类通过控制反转给spring来创建和属性的注入(属性赋值)
	<bean id="user" class="spring.ov.User">
		 <property name="str" value="abc"></property>
		 <property name="sex" value="男"></property>
		 <property name="name" value="孙祥"></property>
		 <property name="age" value="27"></property>
	</bean>

(3)(引用类型的属性的属性注入方式,在property下通过ref属性名来注入)spring中属性注入:就是bean类中属性赋值;属性是引用类型情况

//id 是该bean唯一身份标识奥!
<bean id="user1" class="spring.ov.User">
	<property name="age" value="22"></property>
	<property name="sex" value="女"></property>
</bean>

//person类中属性是引用类型,这时在标签property中设置引用参数和普通参数就不同了。
//普通参数是name=属性名称  value=该属性赋的值。
//引用参数是name = 属性名称  ref = 该引用类型对应的bean的id,告知spring去找到该类型的bean,才能帮创建对象奥!
<bean id = "person" class="spring.ov.person">
	<property name="name" value="person_user"></property>
	<property name="user" ref="user1"></property>
</bean>

(4)复杂类型的属性的属性注入:如数组属性、集合list和map属性

<!-- Spring 的复杂类型的注入===================== -->
<bean id="collectionBean" class="cn.itcast.spring.demo5.CollectionBean">
   <!-- 数组类型的属性注入 -->
	<property name="arrs">
		<list>
			<value>会希</value>
			<value>冠希</value>
			<value>天一</value>
		</list>
	</property>
  <!-- 注入 List 集合的数据 -->
	<property name="list">
		<list>
			<value>芙蓉</value>
			<value>如花</value>
			<value>凤姐</value>
		</list>
	</property>
  <!-- 注入 Map 集合的属性注入 -->
	<property name="map">
		<map>
			<entry key="aaa" value="111"/>
			<entry key="bbb" value="222"/>
			<entry key="ccc" value="333"/>
		</map>
	</property>
  <!-- Properties 的注入 -->
	<property name="properties">
		<props>
			<prop key="username">root</prop>
			<prop key="password">123</prop>
		</props>
	</property>
</bean>

spring的注解

(1)首先使用注解要注解说明一下:<context:component-scan base-package=“xxx” /> 即告诉spring一下我要使用注解了,这样spring会自动扫描xxx路径下的注解spring常用注解说明
注解的本质:spring的ioc就是表示类的创建和属性的设置都要在xml文件中设置,而完成控制反转,最初在xml中设置手动设置,之后出现注解的方式,其本质就是将被标识的类和属性自动装配到spring容器中。免去手动在配置文件中配置ioc的相关内容了。

1.@autowired

(2)@autowired(自动装配):作用是消除java里面的get、set方法与bean配置文件内的property。
(3)通常autowired是注解在java类中属性上面,这样该类中就不用get、set方法来给属性装配内容了,同样在该类的bean中,对于属性的依赖注入也不需要自己写了。
但是如果bean配置文件中属性依赖依旧通过property来完成属性赋值,但是java类中对于该属性没有set方法,而是通过注解@autowired来设置的话,怎么办?property属性注入必须要java类中对应属性有set方法,而autowired又省略了set方法,这样矛盾。spring中会优先xml中property去类中找set方法,没有找到就提示初始化bean报错。
(4)注意!!!注意!!!注解autowired作用:(1)通常只用于属性上。(2)被注解的属性在java类中不用写set/get方法。(3)被注解的属性在bean配置文件中也不用对该属性进行依赖注入了(如property name=“” value=“”等了)。注意只对被注解的属性作用奥,
(5)还有一点autoWired注解的属性,自动装配是由spring在配置文件中(容器中)按照属性的类型在配置文件中查找是否有相同类型的bean,然后才自动装配,将该bean创建对象赋值给该属性变量奥(这才是自动装配)是按类型自动装配奥!

  • 如果对于被注解的属性@autowired(required= false)则表示这个属性是否存在bean关联都无所谓,因为它的存在不是必须的,而是可有可无的。
  • @Autowired默认按类型匹配的方式,就是如果属性是引用类型,那么该引用属性在java中由于无get方法且在bean配置中无property属性依赖注入,那么该引用类型如何由spring自动装配呢,就是通过该属性引用类型在bean配置文件中必须有该类型的bean管理,否则spring无法自动帮你创建。这个过程是容器通过属性类型匹配而查找是否已存在匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的属性中,才帮助自动创建和装配。

举例:

原类中属性没有添加注解的时候:
public class Zoo {
    private Tiger tiger;
    private Monkey monkey;
    
    public Tiger getTiger() {
        return tiger;
    }
    public void setTiger(Tiger tiger) {
        this.tiger = tiger;
    }
    public Monkey getMonkey() {
        return monkey;
    }
    public void setMonkey(Monkey monkey) {
        this.monkey = monkey;
    }
    public String toString(){
        return tiger + "\n" + monkey;
    }
}


//原没有注解的spring配置文件写法:
 <bean id="zoo" class="com.spring.model.Zoo" >
        <property name="tiger" ref="tiger" />        !!!!!!!!!!!!!!!!!!!!!autowired免去了ref(关联到id)的过程
        <property name="monkey" ref="monkey" />
    </bean>
    
    <bean id="tiger" class="com.spring.model.Tiger" />
    <bean id="monkey" class="com.spring.model.Monkey" />
    
//在指定类中属性上添加注解
public class Zoo {
    
    @Autowired
    private Tiger tiger;
    
    @Autowired
    private Monkey monkey;
    
    public String toString(){
        return tiger + "\n" + monkey;
    }
    
}
//添加注解后spring配置文件的写法:
	  <bean id="zoo" class="com.spring.model.Zoo" />
     <bean id="tiger" class="com.spring.model.Tiger" />
     <bean id="monkey" class="com.spring.model.Monkey" />
     
即java中省略了set方法,bean中省略了属性注入的部分了

属性注入的注解分类区别:

在这里插入图片描述

在这里插入图片描述

2.@qualifier

qualify是有资格的意思,该注解作用就是设定那些属性有多个bean对应的时候,不太明确具体哪一个bean情形下。该注解:

 @Autowired
    @Qualifier("bmwCar")
    private ICar car;

由于注解的属性car是一个接口类型,对应有许多实现类,而且其有autowired注解,这样配置文件bean中属性注入也省略了,没有明确具体的ref对应哪个bean,所以可以通过qualifier注解中明确哪个一个bean的id所对应的class类。

3.@resource

该注解作用和@autowired相类似,也是负责属性自动装配的功能,但是autowired是通过属性的类型来装配bean中同类型的bean对象的自动装配的(自动实例化或者赋值)。
而resource是按照属性名称匹配配置文件bean内的id名称,然后将该id对应的class赋值给该属性变量的。完成自动装配过程;且resource注解是javaee的注解,不同于autowired属于spring的注解。所以建议使用@resource注解。

import javax.annotation.Resource;

//resource注解默认按照name去匹配bean,但也可以设置按类型去匹配bean,或者两个同时标准取匹配

public class Zoo1 {
    
    @Resource(name="tiger")
    private Tiger tiger;
    
    @Resource(type=Monkey.class)
    private Monkey monkey;
    
    public String toString(){
        return tiger + "\n" + monkey;
    }
}

4.@service

(1)上面几个注解都是作用于类中属性的。都是简化属性方面的书写,那么有没有简化类的呢?是否尝试想过能不能不用写类的bean配置呢?
bean配置不就是告知spring将指定class对应到一个id,然后让spring通过id来帮助控制反转帮我们创建实例吗?
(2)后面出现@service注解,可以简化类的bean的配置,就是配置文件中不用手动再写类的bean配置了。
(3)@service注解作用:

  • zoo.java 类在bean中的id是“zoo”,即类名首字母小写。
  • @service注解在类上,是声明该类是一个bean
这边在类上@service注解,可以简化Zoo类在配置文件中进行bean配置了,默认配置为id是类名开头小写zoo,也可以设置id@service(“Zoo”),这样id就设置为Zoo了。
@scope注解是声明该类的作用范围,bean默认是单例的这边设置为多例;即每次都会new一个新的实例了。
@Service("Zoo")
@Scope("prototype")
public class Zoo {
    
    @Autowired
    private Tiger tiger;
    
    @Autowired
    private Monkey monkey;
    
    public String toString(){
        return tiger + "\n" + monkey;
    }

}

注意!!! @service注解是作用于类的,表示声明该类是一个bean,或者理解为已经把该类交给spring容器管理了,而且在spring容器中默认存在一个名为类名首字母小写的id的bean了。不用在配置文件中再配置了。

@controller/@controller(“xxx”)/@controller(value=“xxx”)是注解表现层的bean::如果@Controller不指定其value【@Controller】,则默认的bean名字为这个类的类名首字母小写,如果指定value【@Controller(value=“UserAction”)】或者【@Controller(“UserAction”)】,则使用value作为bean的名字
@service/@service(“xxx”)是注解业务层的类(bean):注解是告诉Spring,当Spring要创建UserServiceImpl的的实例时,bean的名字必须叫做"userService"
@repository(value=“xxx”)是注解访问层的bean

(1) @Repository(value=“userDao”)注解是告诉Spring,让Spring创建一个名字叫"userDao"的UserDaoImpl实例。实际上就是省略配置文件中配置< bean id =“userDao” class=“x.x.UserDaoImpl” >的过程。

@Repository(value="userDao")

public class UserDaoImpl extends BaseDaoImpl<User> {
………
}

(2)后面如果当Service层需要使用Spring创建的名字叫"userDao"的UserDaoImpl实例时,就可以使用@Resource(name = “userDao”)注解告诉Spring,让Spring把创建好的userDao注入给Service即可。实际上下面@Resource(name = “userDao”) 注解就是省略了< property name=“属性名(userdao)” ref=“userdao” >的过程。这边ref正好就呼应上面类的bean的id为userdao的过程了,即表示给属性userdao依赖注入为UserDaoImpl类的实例

// 注入userDao,从数据库中根据用户Id取出指定用户时需要用到
@Resource(name = "userDao")   
private BaseDao<User> userDao;

spring中常用注解的汇总

类上注解的内容是:类的id名称;------ @Repository(value=“userDao”)-------< bean id =“userDao” class=“x.x.UserDaoImpl” >
属性上注解的内容是:属性的ref的名称;----------@Resource(name = “userDao”)----------< property name=“属性名(userdao)” ref=“userdao” >
在这里插入图片描述
在这里插入图片描述

IOC小结

在这里插入图片描述

一.组件添加

在这里插入图片描述上图中@conditional和@import这两个注解在springboot底层应用的特别多,一个是条件逻辑和直接导入组件。

二.组件赋值

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值