IoC—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了
有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
5.2 IoC与DIDI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
看过很多对Spring的Ioc理解的文章,好多人对Ioc和DI的解释都晦涩难懂,反正就是一种说不清,道不明的感觉,读完之后依然是一头雾水,感觉就是开涛这位技术牛人写得特别通俗易懂,他清楚地解释了IoC(控制反转) 和DI(依赖注入)中的每一个字,读完之后给人一种豁然开朗的感觉。我相信对于初学Spring框架的人对Ioc的理解应该是有很大帮助的
5.3 spring常用的注入 5.3.1 单例singleton 多实列 prototype1.Spring中的对象默认都是 单例模式。
2.使用 @Scope(“prototype”) 注解来使对象成为多例模式。
3.通过@Autowired 注入的Service 或者是其他实例其实是单例的。
4.通过 ApplicationContext.getBean(C.class); 获取的实例是多例的。
总结:在存在并发的时候,每个需要被注入的类、对象 都使用@Scope(“prototype”) 注解成为多例,每个需要被获取的对象通过ApplicationContext.getBean(C.class);来获取,确保每个线程获取的对象都是新的。
yyyy年MM月dd日 hh时mm分ss秒
5.3.3 属性注入
集合注入相关
构造方法数组注入
北京
天津
上海
重庆
List集合
private List bks;
张三
张三丰
李四
数组
private String[] addr;
洛阳
郑州
北京
上海
开封
map集合
张三
张三丰
李四
set 集合
Set
aaaa
bbbb
cccc
dddd
Properties 集合
aaa1
bbb2
ccc3
ddd4
-
使用@Value注解,@Autowired 注入
books=php,java,javascript,html,java,java,php db.set=1,2,3,1,2,3 db.arr=1,2,3,1,2,3 db.list=1,2,3,1,2,3 db.map={name:'wangwu',age:28,age:22,age:33,is:true}
/* * Copyright (c) 2006, 2021, webrx.cn All rights reserved. * */ package cn.webrx.entity; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; import java.util.Set; /** *
Project: spring2021 - Student *
Powered by webrx On 2021-11-02 11:02:47 *
Created by IntelliJ IDEA * * @author webrx [webrx@126.com] * @version 1.0 * @since 17 */ @Data @Component public class Student { @Value("1111") private int id; @Value("1.2") private double money; @Value("李青") private String name; @Value("true") private boolean isstu; @Value("java,php,html,mysql,php,html") private String[] arrstr; @Value("10,20,30,40") private int[] arrint; @Value("#{'java,php,html,mysql,php,html'.split(',')}") private Set set; @Value("#{'java,php,html,mysql,php,html'.split(',')}") private List list; @Value("#{{name:'lisi',age:18,is:true}}") private Map map; @Value("#{t1.name}") private String tname; @Value("#{student.id * 2}") private int num; @Autowired private Teacher t3; @Value("${books}") private String[] tarr; @Value("#{'${books}'.split(',')}") private Set tset; @Value("#{${db.map}}") private Map tmap; @Value("#{{t1,t2,teacher}}") private Teacher[] teachers; @Value("#{{t1,t2,teacher}}") private List listts; }
测试程序
package cn.webrx.config; import cn.webrx.entity.Teacher; import cn.webrx.entity.User; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.*; @Configuration("ccc") @ComponentScan("cn.webrx.entity") @Import(DruidConfig.class) @PropertySource("t.properties") public class AppConfig { @Bean(initMethod = "start",destroyMethod = "end") @Scope("singleton") public Teacher t1(){ Teacher t = new Teacher(10,"周老师"); System.out.println(t); return t; } @Bean("t2") @Scope("prototype") public Teacher t2(){ Teacher t = new Teacher(22,"李老师"); return t; } @Bean("t3") @Scope("prototype") public Teacher t2(@Value("88") int id, @Value("王老师") String name){ Teacher t = new Teacher(id,name); return t; } @Bean @Scope("prototype") public User user(){ User u = new User(); u.setId(11); u.setName("李六"); return u; } @Bean("u1") @Scope("prototype") public User user2(){ User u = new User(); u.setId(22); u.setName("李七"); return u; } @Bean("t4") @Scope("prototype") public Teacher t2(@Value("88") int id, @Value("王老师") String name,@Value("#{'a,b,c,c,c,c,f'.split(',')}") String[] books,@Qualifier("u1") User user){ Teacher t = new Teacher(id,name); t.setBooks(books); t.setUser(user); return t; } }
注解: @Resource() @PostConstruct @PreDestroy jdk 9以后不能使用,如果使用,添加如下依赖:
javax.annotation
javax.annotation-api
1.3.2
/*
* Copyright (c) 2006, 2021, webrx.cn All rights reserved.
*
*/
package cn.webrx.entity;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
/**
* Project: spring - Db
*
Powered by webrx On 2021-07-01 17:25:48
*
Created by IntelliJ IDEA
*
* @author webrx [webrx@126.com]
* @version 1.0
* @since 16
*/
@Data
@Component("d3") //
@Scope("singleton") @Lazy(true)
public class Db {
//@Resource(name = "php") 引入一个javax.annotation-api jar
@Autowired @Qualifier("php")
private Book book;
public Db() {
System.out.println("构造方法Db()");
}
//引入一个javax.annotation-api jar
@PostConstruct
public void start() {
System.out.println("start.....");
}
@PostConstruct
public void a1() {
System.out.println("start222...");
}
//引入一个javax.annotation-api jar
@PreDestroy
public void close() {
System.out.println("close........");
}
}
5.5 spring 注解使用
@Service、
@Controller、
@Repository 、
@Component
@Value、@Scope、@Lazy、 @PostConstruct、 @PreDestroy
@Autowired、@Resource(name="book")
id id值
name 名称
class 指定类
init-method 初始化
destroy-method 销毁
scopy = singleton | prototype 多实例,使用时再实例化对象
lazy-init="true" 延迟加载
="false" 立即加载,立即初始实例化类bean
spring容器使用注解
beans.xml 文件加入关联
初始化方法
配置方式
init-method="abc" 容器在构造方法完成后,直接执行abc方法
scope="prototype" 如果是多实例,则是每次使用,都要执行构造方法,同时执行初始化方法
scope="singleton" 单例模式,是默认的,是立即装载 都要执行构造方法,同时执行初始化方法
注解方式
package com.fz.entity;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* Created by webrx on 2017-07-03.
*/
//@Controller("uok")
//@Component("uok")
@Service("uok") //@Scope("prototype")
@Scope("singleton")
@Controller User -> user
userInfo -> userInfo
public class User {
public User(){
System.out.println("构造方法()..");
}
@PostConstruct 初始化方法
public void init(){
System.out.println("init--初始化...");
}
@PreDestroy
public void destory(){
System.out.println("销毁方法");
}
public void show(){
System.out.println("数据显示........");
}
@PostConstruct
public void abc(){
System.out.println("abc..");
}
}
销毁方法
配置方式
destroy-method="destory"
当容器销毁对象时,会自动调用destory方法
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
User u = ctx.getBean("users",User.class);
ctx.close();
注解方式
package com.fz.entity;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* Created by webrx on 2017-07-03.
*/
//@Controller("uok")
//@Component("uok")
@Service("uok") //@Scope("prototype")
@Scope("singleton")
public class User {
public User(){
System.out.println("构造方法()..");
}
@PostConstruct
public void init(){
System.out.println("init--初始化...");
}
@PreDestroy //销毁方法
public void destory(){
System.out.println("销毁方法");
}
public void show(){
System.out.println("数据显示........");
}
@PostConstruct
public void abc(){
System.out.println("abc..");
}
}
@Component("st")
@Controller
@Service("sss")
@Repository
自动装配
byName
byType
default-autowire="byName"
注解功能
1)配置spring容器
2)注解使用
@Component 如果不写,则使用类名小写作为名称
@Component("uuu")
@Data @Component
public class Book {
@Value("100")
private int id;
@Value("《mysql数据库技术》")
private String name;
}
@Autowired 根据类型自动装配
@Autowired //根据类型自动装配 byType
@Autowired @Qualifier("user") //根据名称自动装配 byName
private User user;
@Resource(name="book")
3)注解bean类方法
注解bean类 com.entity.Db类
---------------------------------------------------
@Component("db")
public class Db {
public void show(){
System.out.println("dbshow");
}
}
@Controller("member")
public class Member {
public void save(){
System.out.println("saveok");
}
}
@Service("m")
public class Member {
public void save(){
System.out.println("saveok");
}
}
@Repository("mm")
public class Member {
public void save(){
System.out.println("saveok");
}
}
4)注解单例 多例
---------------------------------------------------
@Repository("mm") @Scope("singleton") 单例默认
public class Member {
}
@Repository("mm") @Scope("prototype") 多实例
public class Member {
}
5)注解初始化方法
import javax.annotation.PostConstruct;
---------------------------------------------------
@PostConstruct
public void a(){
System.out.println("初始化1");
}
@PostConstruct
public void init(){
System.out.println("初始化2");
}
6)注解销毁方法
import javax.annotation.PreDestroy;
---------------------------------------------------
@PreDestroy
public void close(){
System.out.println("退出");
}
@PreDestroy
public void exit(){
System.out.println("销毁");
}
7)属性注入
-------------------------------------------------------
@Data
@Component("book")
@Scope("prototype")
public class Book {
@Value("BS101")
private String bid;
@Value("java书籍")
private String bname;
}
@Data @Repository("mm") @Scope("prototype")
public class Member {
@Resource(name="book") 此处的book 就是指定工厂中的 上的那book名@Component("book")
相当于beans.xml 中的
private Book book;
}
win10
@Data @Repository("mm") @Scope("prototype")
public class Member {
@Autowired 根据类型自动注入对象
private Book book;
}
8)延迟加载
-----------------------------------------------------------------
单例模式是默认的,会立即加载 加上@Lazy 不自动加载,第一次使用时再加载
@Data
@Component @Scope("singletone") @Lazy
public class Teacher {
private String name;
public Teacher() {
System.out.println("空构造方法");
}
public Teacher(String name) {
this.name = name;
System.out.println("有参数name的构造方法");
}
public void info(){
System.out.println("老师姓名:"+this.name);
}
}
@Scope("prototype") 多实例,默认使用的使用时加载
@Data @Repository("mm") @Scope("singleton") @Lazy 延迟加载,在第一次使用时加载
public class Member {
}
@Data @Repository("mm") @Scope("singleton") @Lazy(false) 立即加载初始化
public class Member {
}
beans.xml
所有bean都延迟实例化初始化加载
默认注入相关属性对象
aaa
bbb
ccc
ddd
100
200
300
400
升级jdk9后 @PostConstruct、@Predestroy @Resource(name=“”)找不到
package org.beiyou.entity;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Properties;
/**
* 功能描述:
*
* @author webrx
* @version 1.0
* @date 2020-04-27 9:13
*/
@Data
//@Repository("userbook")
@Component("userbook")
@Scope("prototype") //prototype singleton
@Lazy(true)
public class Book {
@Value("10")
private int id;
@Value("《java项目开发》")
private String name;
@Value("98.92")
private double price;
//@Autowired @Autowired @Qualifier("author")
private Author zsaasdf;
@Autowired
@Qualifier("addr")
private List addrs;
@Autowired
public List show;
@Autowired
@Qualifier("prop")
private Properties prop;
@Resource(name = "my")
public Author myuser;
@PostConstruct
public void init() {
System.out.println("init(...)");
}
@PreDestroy
public void close() {
System.out.println("close()");
}
}
5.6 bean自动装配方式
应用于一个类,相当于建立一个applicationContext.xml文件
2. @ComponentScan相当于xml配置文件中的
相当于xml配置文件中的
/*
* Copyright (c) 2006, 2021, webrx.cn All rights reserved.
*
*/
package cn.webrx.config;
import cn.webrx.service.UserDao;
import org.springframework.context.annotation.*;
/**
* Project: spring2021 - SpringConfig
*
Powered by webrx On 2021-11-02 16:28:20
*
Created by IntelliJ IDEA
*
* @author webrx [webrx@126.com]
* @version 1.0
* @since 17
*/
@Configuration @ComponentScan("cn.webrx.service")
public class SpringConfig {
@Bean(name="uu",initMethod = "init",destroyMethod = "close") @Scope("singleton") @Lazy
public UserDao u1(){
var u = new UserDao();
return u;
}
}
4. @Scope
此注解一般在类上,IoC是否为单例@Scope("singleton")
,相当于配置文件中的
导入其它配置类,相当于配置文件中的
package cn.webrx.config;
import cn.webrx.entity.Teacher;
import org.springframework.context.annotation.*;
@Configuration("ccc")
@ComponentScan("cn.webrx.entity")
@Import(DruidConfig.class)
public class AppConfig {
@Bean(initMethod = "start",destroyMethod = "end") @Scope("singleton")
public Teacher t1(){
Teacher t = new Teacher(10,"周老师");
System.out.println(t);
return t;
}
@Bean("t2") @Scope("prototype")
public Teacher t2(){
Teacher t = new Teacher(22,"李老师");
return t;
}
}
package cn;
import cn.webrx.config.AppConfig;
import cn.webrx.config.DruidConfig;
import cn.webrx.entity.Teacher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Arrays;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfig.class, DruidConfig.class})
public class MyDemo {
@Autowired
private Teacher t1;
@Autowired
private ApplicationContext ctx;
@Test
public void t1() {
System.out.println(t1);
System.out.println(ctx.getBeanDefinitionNames().length);
System.out.println("--------------------");
Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
var tt1 = ctx.getBean("t2");
var tt2 = ctx.getBean("t2");
System.out.println(tt1 == tt2);
}
}
6. @Value
@Value("1111")
private int id;
@Value("1.2")
private double money;
@Value("李青")
private String name;
@Value("true")
private boolean isstu;
@Value("java,php,html,mysql,php,html")
private String[] arrstr;
@Value("10,20,30,40")
private int[] arrint;
@Value("#{'java,php,html,mysql,php,html'.split(',')}")
private Set set;
@Value("#{'java,php,html,mysql,php,html'.split(',')}")
private List list;
@Value("#{{name:'lisi',age:18,is:true}}")
private Map map;
@Value("#{t1.name}")
private String tname;
@Value("#{student.id * 2}")
private int num;
@Autowired
private Teacher t3;
//t.properties books=php,java,javascript,html,java,java,php
@Value("${books}")
private String[] tarr;
@Value("#{'${books}'.split(',')}")
private Set tset;
//t.properties db.map={name:'wangwu',age:28,age:22,age:33,is:true}
@Value("#{${db.map}}")
private Map tmap;
//注入List t1 t2 teacher分别是IoC容器的bean
@Value("#{{t1,t2,teacher}}")
private Teacher[] teachers;
@Value("#{{t1,t2,teacher}}")
private List listts;
7. @Autowired
//如果IoC容器没有,就装配null,不报错
@Autowired (required = false)
//自动装配,先根据类型,如果类型有多,再根据属性的变量名,如果没有变量名,报错。
@Autowired
public User user;
//自动装配,先根据类型,如果类型有多,再根据属性的变量名,如果没有变量名,可以指定 @Qualifier("uu")。
@Autowired @Qualifier("uu")
private UserDao userdao;