1、IOC(控制反转)
即控制反转,它不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
(1)控制了什么?
是 bean 的创建、管理的权利,控制 bean 的整个生命周期。
(2)反转了什么?
把这个权利交给了 Spring 容器,而不是自己去控制,就是反转。由之前的自己主动创建对象,变成现在被动接收别人给我们的对象的过程,这就是反转。
(3)bean 又是什么?
Bean 其实就是包装了的 Object,无论是控制反转还是依赖注入,它们的主语都是 object,而 bean 就是由第三方包装好了的 object。
2、DI(依赖注入)
即依赖注入:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
(1)依赖什么?
程序运行需要依赖外部的资源,提供程序内对象的所需要的数据、资源。
(2)注入什么?
配置文件把资源从外部注入到内部,容器加载了外部的文件、对象、数据,然后把这些资源注入给程序内的对象,维护了程序内外对象之间的依赖关系。
3、IOC和DI总结
控制反转是通过依赖注入实现的。但是它们是有差别的,像是从不同角度描述的同一件事。
-
IoC 是设计思想,DI 是具体的实现方式;
-
IoC 是理论,DI 是实践;
从而实现对象之间的解藕。
当然,IoC 也可以通过其他的方式来实现,而 DI 只是 Spring 的选择。IoC 和 DI 也并非 Spring 框架提出来的,Spring 只是应用了这个设计思想和理念到自己的框架里去。
(1)为什么使用IOC
解藕。
它把对象之间的依赖关系转成用配置文件来管理,由 Spring IoC Container 来管理。在项目中,底层的实现都是由很多个对象组成的,对象之间彼此合作实现项目的业务逻辑。但是,很多很多对象紧密结合在一起,一旦有一方出问题了,必然会对其他对象有所影响,所以才有了解藕的这种设计思想。
优质博客:依赖注入和控制反转的理解,写的太好了。-CSDN博客
4、代码实现
(1)使用xml实现注入
-
目录结构
-
Bean
Address类:@Data @AllArgsConstructor @NoArgsConstructor public class Address { String name; }
User类(包含address类)
@Data @AllArgsConstructor @NoArgsConstructor public class User { private String name; private Address address; private String[] book; private List<String> hobby; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; }
-
xml
其中bean标签的作用是把对应的类注入容器,id是类的唯一标识,name是类的别名,都可以以此为根据实例化。
property标签是类的属性,name和对应属性名相同,如果类型是String就可以用value直接指定值,如果是另外一个已经注入容器的Bean就可以使用ref指定Bean的id,其他类型的注入如下<?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 https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <bean id="user" class="com.zhangxin.spring.pojo.User" name="U,u"> <property name="name" value="zhangxin"/> <property name="address" ref="address"/> <property name="book"> <array> <value>《红楼梦》</value> <value>《水浒传》</value> <value>《三国演义》</value> </array> </property> <property name="hobby"> <list> <value>唱歌</value> <value>跳舞</value> </list> </property> <property name="card"> <map> <entry key="身份证" value="pwd1"/> <entry key="银行卡" value="pwd2"/> </map> </property> <property name="games"> <set> <value> cf</value> <value> 王者</value> <value> cf</value> </set> </property> <property name="wife"> <null/> </property> <property name="info"> <props> <prop key="学号">20210222</prop> <prop key="专业">软件工程</prop> </props> </property> </bean> <bean id="address" class="com.zhangxin.spring.pojo.Address" > <property name="name" value="郑州"/> </bean> </beans>
-
Test
public class MyTest { @Test public void testUser(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); User user = context.getBean("u",User.class); System.out.println(user); } }
测试结果:
(2)使用注解实现注入
-
Bean
Dog类@Component public class Dog { public void shut(){ System.out.println("狗叫!"); } }
Cat类
@Component public class Cat { public void shut(){ System.out.println("猫叫!"); } }
People类
@Data @AllArgsConstructor @NoArgsConstructor @Component public class People { @Value("张三") String name; @Autowired Cat cat; @Autowired Dog dog; }
-
Test
public class MyTest { @Test public void test1(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); People person = context.getBean("people", People.class); person.getCat().shut(); person.getDog().shut(); System.out.println(person); } }
-
xml
这个xml文件的核心就是 <context:component-scan base-package=“com.zhangxin.pojo”/> 这一行,下面可以使用定义SpringConfig配置类实现无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: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 https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> <!-- 扫描包,下包下的所有注解生效--> <context:component-scan base-package="com.zhangxin.pojo"/> </beans>
-
Spring的Config类
@Configuration 指定这个是配置类,@ComponentScan(“com.zhangxin.pojo”) 指定需要扫描bean的包使其注解生效。@Configuration @ComponentScan("com.zhangxin.pojo") public class MyConfig { }
-
xml和config配置类从容器中取出bean的区别
xml,需要使用spring配置文件ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); People person = context.getBean("people", People.class);
config,需要对应config的Class
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); People people = context.getBean("people", People.class);
-
注解解释
@Component
表示这个类被spring接管了,即这个类已经注册为bean了
@Componen的衍生注解:
@Repository 该注解用于数据访问层的bean定义,数据访问层也就是我们常写的dao层、mapper层
@Service 该注解用于业务逻辑层的bean定义,即service层
@Controller 该注解用于表现层,控制层的bean定义,即controlle层等
这四个注解的功能一样的都是把类注入spring,装配bean@Configuration
表示这个类是一个配置类,也会注入spring,因为也是一个@Component@ComponentScan(“com.zhangxin.pojo”)
表示要扫描的包com.zhangxin.pojo下的所有注解,不然这个包的注解不生效,相当于xml的 < context:component-scan base-package=“com.zhangxin.pojo”/> ,该注解只能使用1次,如果有多个需要扫描的包,使用下述方式实现@ComponentScan({"com.zhangxin.dao","com.zhangxin.service"})
@Bean
用于方法上,通常是是以一个方法的返回值的形式注入一个Bean,当然前提是这个方法对应的类是一个组件,比如下面的代码就是成功注入了Cat类,Cat类并没有使用@Component标签
@Component , @Repository , @ Controller , @Service 这些注解只局限于自己编写的类,而@Bean注解能把第三方库中的类实例加入IOC容器中并交给spring管理public class Cat { public void shut(){ System.out.println("猫叫!"); } } @Component class A{ @Bean Cat cat(){ return new Cat(); } }
@Value
普通类型直接用value赋值,直接在属性上面@Resource(name=" ")
如果没有指定name属性,当注解写在字段上时,默认取字段名,按照名称查找。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。@Autowired
默认是byType可以使用@Qualifier指定Name,@Resource默认ByName如果找不到则ByType@Autowired可以和@Override配合使用
比如某个属性类是个接口但是有多个实现类,默认情况是@Autowired标签会自动加载其中的一个实现类,然后使用这个实现类。这时候就需要特定加载某个类了- 第一步将实现类注册为一个Bean(@Service、@Repository标签都可以将类注册为bean)
- 第二步使用@Qualifier(“beanId”)标签将bean的Id作为参数使用,加载特定的类。Id一般默认是bead名称首字母小写
@Component // 指明bean名称 @Qualifier("fooFormatter") public class FooFormatter implements Formatter { public String format() { return "foo"; } } @Component // 指明bean名称 @Qualifier("barFormatter") public class BarFormatter implements Formatter { public String format() { return "bar"; } } @Component public class FooService { @Autowired // 同样使用@Qualifier进行指明注入 @Qualifier("fooFormatter") private Formatter formatter; }
@Import(ConfigB.class)
可以在一个配置类引入另一个配置类