一、IOC的理论推导?
我自己的理解:IOC(控制反转):由控制和反转两部分组成;
控制:就是控制权,
反转:将控制权从一个人的手中,转到另一个人手中。
以前一个接口,有多个实现,用户想要访问不同是业务,需要程序员手动调换接口,
现在,通过使用set方法注入实现类的方式,让用户自己管理和访问。
这里就相当于以前用户想要使用业务,业务的控制权在程序员手中,程序员实现了什么接口,用户才能使用什么业务;现在就是,用户自己手中有接口实现类,用户想要使用什么业务就直接注入对应的接口实现类,然后,程序去帮用户创建对应的接口实现类的对象。
-
之前,程序是主动创建对象,控制权在程序员手中,
-
使用set注入之后,程序员不在具有主动性,而变成了被动的接收对象!
这样就将创建对象的控制权从程序员控制转变成用户自己控制。
二、IOC的本质
控制反转IOC(Inversion of Control)是一种设计思想,DI(依赖注入)是一种实现IOC的一种方法,也有人认为DI只是IOC的另一种说法,没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序员自己控制,控制反转后,将对象的创建转移给了第三方,个人认为所谓的控制反转就是,获取依赖对象的方式反转了。
这种思想,本质上解决了问题,我们程序员不用再去管理对象的创建了。系统的耦合性大大降低了,可以更加专注的在业务的实现上。
三、为什么学习Spring的IOC?
现在我们知道了,以前都是程序员在代码中将所需的对象直接new出来,现在通过IOC思想将创建对象这个过程交给了Spring容器来帮我们创建我们自己需要的业务对象。
而我们需要学习的就是Spring如何去帮我们创建的对象。IOC如何实现解耦的过程。
四、Spring中的IOC是如何实现的?
控制反转是一种通过描述(XMl或注解)并通过第三方产生或获取特定对象到的方式,在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection ,DI) 。
依赖注入三种方式:构造器注入,set注入,拓展方式注入
4.1 先学习如何通过xml创建对象
1.下标赋值
//第一种:用下标赋值
<bean id="user" class="com.lx.pojo.User">
<constructor-arg index="0" value="java"/>
</bean>
2.通过类型创建
//第二种方式:通过类型创建,不建议使用
<bean id="user" class="com.lx.pojo.User">
<constructor-arg type="java.lang.String" value="java"/>
</bean>
3.通过参数名
//第三种方式:直接通过参数名设置
<bean id="user" class="com.lx.pojo.User">
<constructor-arg name="name" value="狂神说java"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象已经初始化了!
4.2 通过xml获取指定对象的方式![](https://img-blog.csdnimg.cn/a8c95b7b65b241ceb0398b87225df2a7.png)
采用XML方式配置Bean的时候,Bean的定义信息和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
代码实现:
1.pojo实体类
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
配置类: 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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 使用Spring来创建对象,在Spring这些称为Bean
类型 变量名 = new 类型();
Hello hello=new Hello()
bean=对象 相当于 new Hello();
id = 变量名
class :pojo的全路径
class = new 的对象;
property 相当于给对象的属性设置一个值。
name:起别名,可以取多个别名,只需要用分隔符把他分开(空格,‘,’,‘;’等)
scope:作用域(单例模式,原型模式等)
-->
<bean id="hello" class="com.lx.pojo.Hello" name="hello2 h2,s" scope="singleton">
<property name="str" value="Spring"/>
</bean>
<!--起别名,如果添加了别名,我们也可以使用别名来获取对象-->
<alias name="hello" alias="hello2"/>
</beans>
测试类:通过bean获取对象
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象都在Spring中管理了,我们要使用,直接去里面取出来使用就可以了!!!
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
4.3 在学IOC容器的实现方法:依赖注入
依赖注入的三种方式:构造器注入,set注入,拓展方式注入
构造器注入:就是通过new 构造器()去创建。
Set方式注入【重点】
-
依赖注入:set注入!
-
依赖:bean对象的创建依赖于容器!
-
注入:bean对象中的属性,由容器来注入!
-
代码实现:
1.pojo实体类
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2.真实测试对象
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobby;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
}
3.配置文件: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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="student" class="com.lx.pojo.Student">
<!-- 第一步:普通值-->
<property name="name" value="测试"/>
</bean>
</beans>
4.测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getName());
}
}
上面的测试只是注入了Student的其中name属性,还有其他属性需要注入;
5.完善注入信息:注入的属性对应的是上面的Student实体类
<bean id="address" class="com.lx.pojo.Address">
<property name="address" value="西安"/>
</bean>
<bean id="student" class="com.lx.pojo.Student">
<!-- 第一步:普通值-->
<property name="name" value="测试"/>
<!-- 注入Bean类型-->
<property name="address" ref="address"/>
<!-- 注入数组类型-->
<property name="books">
<array>
<value>西游记</value>
<value>水浒传</value>
<value>红楼梦</value>
<value>三国演义</value>
</array>
</property>
<!-- List注入-->
<property name="hobby">
<list>
<value>打篮球</value>
<value>游泳</value>
</list>
</property>
<!-- Map注入-->
<property name="card">
<map>
<entry key="身份证" value="87879854458756985"/>
<entry key="银行卡" value="648755495654565548"/>
</map>
</property>
<!-- Set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>QQ飞车</value>
</set>
</property>
<!-- String的空指针注入-->
<property name="wife">
<null/>
</property>
<!-- properties-->
<property name="info">
<props>
<prop key="driver">79</prop>
<prop key="url">456</prop>
<prop key="username">123</prop>
<prop key="password">4615</prop>
</props>
</property>
</bean>
以上的是set最原始的注入方式,我们需要了解第三方提供的set注入方式:c命名空间和p命名空间
他们都只是在xml配置文件注入属性对象时使用:
c命名空间:
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 使用p命名空间注入,可以直接注入属性的值,property-->
<bean id="user" class="com.lx.pojo.User" p:name="测试p命名空间" p:age="18"/>
<!-- 使用c命名空间,前条件:实体类有相应的有参构造器,通过构造器注入:Constructor-->
<bean id="user2" class="com.lx.pojo.User" c:name="c命名空间测试" c:age="18"/>
</beans>
4.4 自动装配
自己的理解:现在我们创建一个实体类,需要在xml配置文件中去注入相关属性值,现在出现了一种情景:有三个实体类,有两个实体类(cat,dog)作为另外一个实体类(people)中的属性。这三个实体类都在xml配置文件中创建一个<bean>,但是没有注入属性,但是依然想使用people这个实体类去使用另外两个实体类中的方法,该怎么办?
代码重现情景模式:
1.猫 的pojo实体类
2.狗 的pojo实体类
3.人 的pojo实体类
4.配置文件:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="dog" class="com.lx.pojo.Dog"/>
<bean id="cat" class="com.lx.pojo.Cat"/>
<!--autowire="byType" :手动注解注入-->
<bean id="people" class="com.lx.pojo.People"/>
这个时候就需要用到Bean的自动注入:让Spring容器帮我们去注入属性。
有个前提:你需要让Spring帮你注入属性,前提一定要将这个属性以bean的形式注册到配置文件中,否则会注入失败。
因此我们就要学习bean的自动装配的两种方式:通过xml文件配置和通过注解配置。
xml文件配置:又分两种:byName和byType
1.byName进行自动装配;
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="dog" class="com.lx.pojo.Dog"/>
<bean id="cat" class="com.lx.pojo.Cat"/>
<bean id="people" class="com.lx.pojo.People" autowire="byName"/>
</beans>
2.byType进行自动装配。
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="dog" class="com.lx.pojo.Dog"/>
<bean id="cat" class="com.lx.pojo.Cat"/>
<bean id="people" class="com.lx.pojo.People" autowire="byType"/>
</beans>
小结:
-
byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致。
-
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id
-
byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致。
注解配置
jdk 1.5支持注解,Spring2.5就支持注解了!
要使用注解须知:
1.导入约束:context约束
2.配置注解的支持:<context:annotation-config/>
3.配合@Autowired进行使用
使用Autowired 我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(spring)容器中存在,且符合名字byName!
代码理解:
1.首先在你想要使用注解自动配置的属性或者set方式上直接添加@Autowired注解就行
public class People {
private String name;
@Autowired
private Dog dog;
@Autowired
private Cat cat;
public People(@NonNull String name) {
this.name = name;
}
public People() {
}
public People(String name, Dog dog, Cat cat) {
this.name = name;
this.dog = dog;
this.cat = cat;
}
}
2.在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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 开启注解-->
<context:annotation-config/>
<bean id="dog" class="com.lx.pojo.Dog"/>
<bean id="cat" class="com.lx.pojo.Cat"/>
<!--autowire="byType" :手动注解注入-->
<bean id="people" class="com.lx.pojo.People"/>
</beans>
扩展:
@NonNull 字段标记了这个注解,说明这个字段可以为null,
//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不许为空
@Autowired(required = false)
private Dog dog;
如果@Autowired自动装配环境比较复杂,自动装配无法通过一个注解【@Autowired】完成时,我们可以使用@Qualifier搭配使用。
指定唯一的bean对象注入。
@Autowired
@Qualifier(value = "dog222")
@Resoure 注解和@Autowired效果一致
使用他时,如果bean的id是对应且唯一的,可以有多个bean,或者id不对应,但是后面的class是唯一的也可以使用。
public class People {
private String name;
@Resource(name = "dog222")
private Dog dog;
@Resource
private Cat cat;
}
小结:
@Resoure和@Autowired的区别?
1.@Resoure是java的,@Autowired是来自spring的
2.java的注解,属性较多,type无法分辨时可以用name分辨;
spring的注解,一个属性,type无法分辨时需要借助@Qualifier注解才能使用 使用@Autowired方式最好使用构造函数的方式注入。
3.都是用来自动转配的,都可以放在属性字段上
4.@Autowired 通过byType的方式实现的,而且必须要求这个对象存在!【常用】
@Resoure默认通过byName的方式实现,如果找不到名字,则通过Type实现,如果两个都找不到的情况下,就报错!【常用】
5.执行顺序不同:@Autowired自动装配通过类型,名字
如果Autowired不能唯一的自动装配,则需要通过@Qualifier(value=“ ”)
@Resoure:自动装配通过名字,类型
五、使用注解开发
上面全都需要自己将实体类对应的<bean>一个一个的手动注册到xml配置文件中,现在只需要在实体类前添加一个注解@Component(组件),就相当于在xml配置文件中进行了<bean>注册。大大简化了开发过程。
使用注解开发的前提
在Spring4之后,要使用注解开发,必须要保证aop的包导入了
使用注解开发步骤:
1.使用注解需要导入context约束,添加注解的支持和你注解使用在那个包下,开启包扫描使注解生效完成,<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"
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">
<!-- 1. 开启注解-->
<context:annotation-config/>
<!-- 2. 指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.lx.pojo"/>
</beans>
2.在实体类中添加注解,将实体类交给Spring去管理
//等价于 <bean id="user" class="com.lx.pojo.User"/>
//@Component 组件
@Component
public class User {
//相当于 <property name="name" value="lx"/>
@Value("lx")
public String name ;
public void setName(String name) {
this.name = name;
}
}
测试使用:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user", User.class);
System.out.println(user.name);
}
3.衍生的注解
@Component有几个衍生注解。我们在web开发中,会按照mvc三层架构分层!
-
dao 【@Repository】
-
service 【@Service】
-
controller 【@Controller】
这四个注解功能都是一样的,都是代表某个注解注册到Spring中,装配bean。
小结
xml与注解:
-
xml更加万能,适用于任何场合!维护简单方便
-
注解 不是自己类使用不了,维护相对复杂!
xml与注解最佳实践:
-
xml用来管理bean
-
注解只负责完成属性的注入
-
我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
第一次正式发表博客,如果各位网友发现了有什么问题,可以在评论区进行留言讨论,相互学习。
本博客是我通过观看哔哩哔哩up主的视频进行学习的:如有不懂,可通过链接进行学习:1、Spring简介_哔哩哔哩_bilibili