文章目录
- 什么是IOC
- 什么是控制,谁控制谁
- 什么是反转,从什么反转到什么了
- IOC的注解
- 五大类注解
- @Controller注解(控制器存储)
- @Service(服务存储)
- @Repository(仓库存储)
- @Componet(组件存储)
- @Configuration(配置存储)
- 为什么这么多注解?
- 类注解之间的关系
- Bean的命名规则
- 什么是方法注解(@Bean)
- Bean注解
- Bean的重命名
- 什么是DI
- @Autowired注解的局限性
- @Primary注解
- @Primary注解
- @Resource
- DI注入的方式
- 构造方法注入
- Setter方式注入
什么是IOC
IOC中文叫做控制反转,这是一种设计思想,那么什么是控制反转呢?想明白这个概念我们就要搞明白这两个概念
什么是控制,谁控制谁
首先是第一个概念谁控制谁呢?首先我们先搞明白谁是被控制的一方,在这里被控制的一方就是我们的各种对象,那么在传统的javaSE的程序设计中,我们创建对象是通过new来进行创建的,而IOC是有一个专门的容器来进行这些对象的创建和管理。因此这里的控制指的就是对我们创建对象的控制。那么回到那个问题谁控制了谁?:答案很明显了,是IOC容器控制了对象,控制了什么呢?控制了外部资源的获取
什么是反转,从什么反转到什么了
那么什么是反转呢?在传统的应用程序中是由我们自己在对象中主动控制获取依赖对象而反转的含义的就是由容器帮助我们进行对象的创建和存取。那么Spring想要帮我们进行存取对象该怎么做呢?答案是通过注解的形式
IOC的注解
想要将对象存储在spring中有两种方式来实现
1、首先是可以通过类注解的形式
2、可以通过方法注解的形式
五大类注解
首先就是五大类注解,这几个类注解都有哪些呢?分别是如下注解
@Controller注解(控制器存储)
该注解的使用方式如下
package com.example.spring_ioc;
import org.springframework.stereotype.Controller;
@Controller
public class UserControl {
public void doUserControl(){
System.out.println("doUserControl ....");
}
}
首先我们先写出一个被controller注解的类,然后我们来进行获取bean
package com.example.spring_ioc;
import com.example.spring_ioc.Control.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
//然后在程序上下文中进行调用。
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
ApplicationContext context=SpringApplication.run(SpringIocApplication.class, args);
UserControl userControl=context.getBean(UserControl.class);
userControl.doUserControl();
}
}
那么上面就是我们通过@Controller注解进行修饰之后得到的程序,这里我们来进行运行一下来看看
这里我们发现我们的程序运行起来了。
@Service(服务存储)
在用法上大致相同我们来看一下代码
package com.example.spring_ioc;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void doUser(){
System.out.println("doUser ....");
}
}
那么有了上面的用列我想大家也能推到出来其余的三个注解在使用上也是大差不差的。
@Repository(仓库存储)
package com.example.spring_ioc;
import org.springframework.stereotype.Repository;
@Repository
public class ResponceUser {
public void doRespository(){
System.out.println("doRepository ....");
}
}
@Componet(组件存储)
package com.example.spring_ioc;
import org.springframework.stereotype.Component;
@Component
public class ComponetUser {
public void doComponet(){
System.out.println("doComponetuser .....");
}
}
@Configuration(配置存储)
package com.example.spring_ioc;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConfigurationUser {
public void doConfiguration(){
System.out.println("@Configuration ...");
}
}
为什么这么多注解?
相信看到这里很多人会对这里有个疑惑,既然用法和作用都差不多为什么还要有这么多注解?直接就弄一个不就可以了吗?其实这是因为这个东西就跟我们的车牌号是一个道理的,在我们车牌号中我们不同的城市我们的车牌号的前面两个字符是不一样的,那么为什么这样做呢?这样做除了说是节约号码之外还有一个好处就是我们可以通过车牌迅速的确定这个车辆是哪个地方的,比如说你看到了豫A那么你第一个想到的就是河南郑州。这是类似的,有了这些注解也可以帮助我们开发者人员迅速知道这块代码是哪一层的。作用是什么。那么说到这里就涉及到了我们的工程分层。分层图如下所示
类注解之间的关系
通过查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:这些注解都有一个@Component注解。说明他们本身就属于@Component的子类。
Bean的命名规则
这里为什么要提到Bean的命名规则呢?因为我们在获取Bean对象的时候其实我们也是可以通过类的名称来进行获取的而名称获取就需要一定的规则来规范你所命名的名称了。
如下
首先我们有图中的这些类,这些类的命名呢都是首字母大写那么我们根据这些类的名称去获取的时候我们应该怎么遵循什么规则去获取呢?那么代码如下
package com.example.spring_ioc;
import com.example.spring_ioc.Control.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
ApplicationContext context=SpringApplication.run(SpringIocApplication.class, args);
ConfigurationUser configurationUser=(ConfigurationUser)context.getBean("configurationUser");
configurationUser.doConfiguration();
}
}
这里我们可以看到我们需要给首字母大写转换为小写,此外假如说你的类名称前两个首字母都是大写的话那就不需要转换了,转换后甚至有可能出粗呢。
什么是方法注解(@Bean)
讲完上面的五大注解之后我们来谈一下什么是bean,bean的作用是什么。我们上面说了五大注解是加在了类上的因此叫做类注解,而bean则是方法上的。那么他们直接有什么区别呢?这里先思考一个问题,在上面我们通过类注解去进行了获取对象等,那么这里我们获取到的对象是同一个对象吗?也就是假如说我们对同一个类获取了两次对象那么这两个对象是同一个吗?我们来看一下吧。
package com.example.spring_ioc;
import com.example.spring_ioc.Control.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
ApplicationContext context=SpringApplication.run(SpringIocApplication.class, args);
ConfigurationUser configurationUser=(ConfigurationUser) context.getBean("configurationUser");
ConfigurationUser configurationUser11=context.getBean(ConfigurationUser.class);
System.out.println(configurationUser11==configurationUser);
}
}
那么我们来看一下这个代码的运行结果吧
很明显结果是为true也就是说这里创建出来的两个对象是同一个对象,可是我们在实际开发中有很多情况肯定是不希望这样的那么这时候就需要用到bean注解了。
Bean注解
首先我们先来看一下代码如何操作
package com.example.spring_ioc.Control;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class TestControl {
public void doCOntrol(){
System.out.println("docontrol .....");
}
@Bean
public TestControl control(){
TestControl control=new TestControl();
return control;
}
@Bean
public TestControl control1(){
TestControl control1=new TestControl();
return control1;
}
}
然后我们来看一下如何获取到对象
package com.example.spring_ioc;
import com.example.spring_ioc.Control.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
ApplicationContext context=SpringApplication.run(SpringIocApplication.class, args);
TestControl control=context.getBean(TestControl.class);
control.doCOntrol();
}
}
这里我们可以发现我们想要继续通过之前的方法去获取对象是不行的了,那么他报错的原因也很简单那就是他不知道该获取哪个对象,就像一个班级里面有很多个男生,你直接就喊出来一个男生,这时候肯定会懵一下感到疑惑你找谁啊?所以代码也是这样的那么该怎么办呢?答案就是通过名称获取
package com.example.spring_ioc;
import com.example.spring_ioc.Control.*;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
public class SpringIocApplication {
public static void main(String[] args) {
ApplicationContext context=SpringApplication.run(SpringIocApplication.class, args);
TestControl control=(TestControl) context.getBean("control");
TestControl control1=(TestControl)context.getBean("control1");
System.out.println(control1==control);
control.doCOntrol();
control1.doCOntrol();
}
}
那么我们来看一下运行的结果如何
我们发现这里的运行结果表明我们创建出来的两个对象是不同的两个对象
Bean的重命名
使用Bean注解是可以对类进行重命名的,那么如何进行重命名呢?我们来看一下代码
package com.example.spring_ioc.Control;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class TestBean {
@Bean("u1")
public TestBean testBean1(){
TestBean testBean=new TestBean();
return testBean;
}
public void run(){
System.out.println("runTestBean");
}
}
那么这时候我们想要获取对象就可以通过重命名之后的名字进行获取了
什么是DI
那么接下来什么是DI呢?DI也叫做属性注入,什么是属性注入?我们来看一下下面的这个代码
TestDI
package com.example.spring_ioc.Control;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
@Configuration
public class TestDI {
@Autowired
Person person;
@Bean
public TestDI t(){
TestDI testDI=new TestDI();
return testDI;
}
public void run(){
System.out.println("+++");
}
}
Person
package com.example.spring_ioc.Control;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
@ToString
@Controller
public class Person {
public String name="zyfcl";
public Integer age=20;
@Bean
public Person p(){
Person person=new Person();
return person;
}
}
从上面两个代码也许就能看出来,首先Person是我们的一个Controller类,也就是已经注入了spring容器中了并且使用Bean将p方法进行注解,此时就相当于告诉了spring我们这个类的对象调用的是我们的这个方法进行获取,然后在TestDI类中我们使用了person类创建的person对象,并且这个对象使用了Autowired注解,也就是会进行注入。
@Autowired注解的局限性
首先我们要明白@Autowired注解的匹配原则首先他是先按照类型进行匹配的但是这样势必会有下面的这个问题那就是假如说类型匹配上了但是我们使用了@Bean注解注解了多个方法去进行创造这个对象,那么到底该返回哪一个呢?如下代码
package com.example.spring_ioc.Control;
import lombok.ToString;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ToString
@Configuration
public class Person {
public String name;
public Integer age;
@Bean
Person person1(){
Person person2=new Person();
person2.age=21;
person2.name="cl";
return person2;
}
@Bean
public Person person2(){
Person person1=new Person();
person1.name="zyf";
person1.age=19;
return person1;
}
}
package com.example.spring_ioc.Control;
import lombok.ToString;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ToString
@Configuration
public class Person {
public String name;
public Integer age;
@Bean
Person person1(){
Person person2=new Person();
person2.age=21;
person2.name="cl";
return person2;
}
@Bean
public Person person2(){
Person person1=new Person();
person1.name="zyf";
person1.age=19;
return person1;
}
}
package com.example.spring_ioc.Control;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TestDI {
@Autowired
UserInfo userInfo;
@Autowired
Person person;
@Bean
public TestDI t(){
TestDI testDI=new TestDI();
return testDI;
}
public void run(){
System.out.println("+++");
System.out.println(person);
}
public void run2(){
System.out.println(userInfo);
}
}
那么结合上面的两段代码我们来想一想,这里会不会报错呢?这个person该实例化为哪个呢?其实这里spring也不知道,那么最终怎么办呢?有些情况下可能会报错,有些情况下可能会给你初始为空。我这边是初始为空的。
那么上面的情况怎么解决呢?那就是下面三个注解
首先第一个
@Primary注解
这个注解可以理解为默认的意思也就是出现了上面的情况的话那么就会默认为被这个注解修饰的那种方法。
@Primary注解
第二种就是这样的注解,表示你想要将其使用哪个对象,在这里面写入你希望的方法名称即可
@Resource
第三种就是Resource注解它的用法和primary是差不多的都是自己指定。
DI注入的方式
上面只讲了属性注入实际上我们还有别的方式进行注入
构造方法注入
这里就是构造的方式注入
Setter方式注入
这里也就相当于写了一个set方法进行了注入。