IOC是 Inversion of Control 的缩写,即控制反转。
上层模块不应该依赖于下层模块,它们共同依赖于一个抽象
抽象不能依赖于具体实现,具体实现依赖于抽象
IoC 不是什么技术,而是一种设计思想。在 Java 开发中,IoC 意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解 Ioc 呢?理解 Ioc 的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
谁控制谁,控制什么:传统 JavaSE 程序设计,我们直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象;而 IoC 是有专门一个容器来创建这些对象,即由 IoC 容器来控制对象的创建;谁控制谁?当然是 IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
----转载自github的一篇博客。
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了 IoC 容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实 IoC 对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在 IoC/DI 思想中,应用程序就变成被动的了,被动的等待 IoC 容器来创建并注入它所需要的资源了。
IoC 很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由 IoC 容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
----转载自github的一篇博客。
DI,是 Dependency Injection 的缩写,即依赖注入。
依赖注入是 IoC 的最常见形式。
容器全权负责的组件的装配,它会把符合依赖关系的对象通过 JavaBean 属性或者构造函数传递给需要的对象。
DI 是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解 DI 的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
谁依赖于谁:当然是应用程序依赖于 IoC 容器;
为什么需要依赖:应用程序需要 IoC 容器来提供对象需要的外部资源;
谁注入谁:很明显是 IoC 容器注入应用程序某个对象,应用程序依赖的对象;
注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以 2004 年大师级人物 Martin Fowler 又给出了一个新的名字:“依赖注入”,相对 IoC 而言,“依赖注入”明确描述了“被注入对象依赖 IoC 容器配置依赖对象”。
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以 2004 年大师级人物 Martin Fowler 又给出了一个新的名字:“依赖注入”,相对 IoC 而言,“依赖注入”明确描述了“被注入对象依赖 IoC 容器配置依赖对象”。
JavaBean 是一种 JAVA 语言写成的可重用组件。为写成 JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 对外部通过提供 getter / setter 方法来访问其成员。
由 IoC 容器管理的那些组成你应用程序的对象我们就叫它 Bean。Bean 就是由 Spring 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了。那 IoC 怎样确定如何实例化 Bean、管理 Bean 之间的依赖关系以及管理 Bean 呢?这就需要配置元数据,在 Spring 中由 BeanDefinition 代表,后边会详细介绍,配置元数据指定如何实例化 Bean、如何组装 Bean 等。
IoC 容器的配置有三种方式:
首先我们先导入maven的spring ioc依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
首先创建一个spring ioc 的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="UserService" class="com.service.Impl.IUserServiceimpl"> </bean>
<import resource="bean2.xml" />
</beans>
标签说明:
是 Spring 配置文件的根节点。
用来定义一个 JavaBean。id
属性是它的标识,在文件中必须唯一;class
属性是它关联的类。
用来导入其他配置文件的 Bean 定义。这是为了加载多个配置文件。核心容器的两个接口
实例化容器的过程: 定位资源(XML 配置文件) 读取配置信息(Resource) 转化为 Spring 可识别的数据形式(BeanDefinition)。
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"bean1.xml", "bean2.xml"});
组合 xml 配置文件 配置的 Bean 功能各不相同,都放在一个 xml 文件中,不便管理。 Java 设计模式讲究职责单一原则。配置其实也是如此,功能不同的 JavaBean 应该被组织在不同的 xml 文件中。然后使用 import 标签把它们统一导入。
ApplicationContext
是一个接口 有常用的三个实现类
ClassPathXmlApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"bean1.xml", "bean2.xml"});
FileSystemXmlApplicationContext
AnnotationConfigApplicationContext
new AnnotationConfigApplicationContext(Class> ... config);
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
使用容器的方式就是通过getBean获取 IoC 容器中的 JavaBean。
第一种 getBean(String id)
第二种 getBean(String id, Class> object)
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
IUserService iUserService1 = (IUserServiceimpl) applicationContext.getBean("IUserService");
IUserService iUserService2 = applicationContext.getBean("IUserService", IUserService.class);
使用默认构造函数(无参)创建对象
使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
factory-bean
属性为工厂类的id(先给工厂类配bean),factory-method
属性为工厂类创建目标对象的方法(该方法不能为static)使用普通工厂中的静态(static)方法创建对象(使用某个类中的静态(static)方法创建对象,并存入spring容器)
使用默认构造函数(无参)创建对象
<bean id="UserService" class="com.service.Impl.IUserServiceimpl"> </bean>
使用普通工厂中的方法创建对象
<bean id="instanceFactory" class="com.factory.InstanceFactory"></bean>
<bean id="IUserService" factory-bean="instanceFactory" factory-method="getIUserSevice" ></bean>
使用普通工厂中的静态(static)方法创建对象
<bean id="IUserService" class="com.factory.staticFactory" factory-method="getIUserSevice"></bean>
默认情况下 bean对象只创建一次————————单例对象。我们可以通过bean
标签的scope
属性设置bean对象的作用范围。
scope
属性:
1、单例对象:
2、多例对象:
我们可以借助bean标签的init-method属性调用初始化方法和destroy-method调用销毁方法
//init 和 destroy 是类IUserServiceimpl 定义的方法
<bean id="IUserService" class="com.service.Impl.IUserServiceimpl" scope="singleton" init-method="init" destroy-method="destroy"></bean>
<bean id="IUserService" class="com.service.Impl.IUserServiceimpl" >
<constructor-arg name="name" value="xiuyuandashen"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="birthday"></constructor-arg>
</bean>
<bean id="birthday" class="java.util.Date"></bean>
对应的构造函数
public IUserServiceimpl(String name, Date birthday, Integer age) {
this.name = name;
this.birthday = birthday;
this.age = age;
}
首先该对象得Setter方法
复杂类型的注入或者集合结构类型的注入
<bean id="fuzajiegou" class="com.domain.fuzajiegou">
<property name="myArr">
<array>
<value>hello</value>
<value>world</value>
<value>java</value>
<value>javaScript</value>
</array>
</property>
<property name="myList">
<list>
<value>list——a</value>
<value>list——b</value>
<value>list——c</value>
<value>list——e</value>
<value>list——f</value>
</list>
</property>
<property name="mySet">
<set>
<value>set——a</value>
<value>set——b</value>
<value>set——c</value>
<value>set——e</value>
<value>set——f</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="map1" value="1"></entry>
<entry key="map2" value="2"></entry>
<entry key="map3" value="3"></entry>
<entry key="map4">
<value>4</value>
</entry>
</map>
</property>
<property name="myPro">
<props>
<prop key="Properties1">1</prop>
<prop key="Properties2">2</prop>
<prop key="Properties3">3</prop>
<prop key="Properties4">4</prop>
</props>
</property>
</bean>
对应的bean类
public class fuzajiegou {
private String[] myArr;
private List myList;
private Set mySet;
private Properties myPro;
private Map<String,String> myMap;
public void setMyArr(String[] myArr) {
this.myArr = myArr;
}
public void setMyList(List myList) {
this.myList = myList;
}
public void setMySet(Set mySet) {
this.mySet = mySet;
}
public void setMyPro(Properties myPro) {
this.myPro = myPro;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
}
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
final fuzajiegou fuzajiegou = applicationContext.getBean("fuzajiegou", fuzajiegou.class);
System.out.println(fuzajiegou);
}
Spring 默认是不启用注解的。如果想使用注解,需要先在 xml 中启动注解。 启动方式:在 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
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com"/>
</beans>
标签说明:
标签
base-package
表示扫描的位置@Component 注解
value
:用于指定
的id
。当我们不写时默认为当前类名,且首字母小写@Controller、@Service、@Repository 注解
@Component
一样,用这三种注解使三层结构对象更清晰。@Controller
表示表现层@Service
表示业务层@Repository
表示持久层他们的作用就和xml配置文件中的bean标签中写一个
标签的作用是一样的
@Autowired
@Autowired
)上再按照名称注入。value
:用于指定注入bean的id。以上三种注解(@Autowired、@Qualifier、@Resource
)都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述三种注解实现
他们的作用就和bean标签中使用scope属性实现的功能一致。
@Scope
他们的作用就和bean标签中使用scope属性实现的功能一致。
@PreDestroy
@PostConstruct
首先使用注解配置需要解决xml配置文件中注解对
的依赖
我们可以使用@ComponentScan
注解来代替
@Configuration
AnnotationConfigApplicationContext
对象创建的参数时,该注解可以不写。@ComponentScan
.@Autowired
注解的作用是一致的。使用@Bean注解默认是单例的,需要加个@Scope("prototype")
指明为多例对象Class []value
:该数组的值就是需要引入的其他配置类的字节码文件。@Import
注解引入时,该类就不需要用 @Configuration
来指明是配置类.相当于,使用@Import
的配置类为父配置类,引入的类就是子配置类.在使用AnnotationConfigApplicationContext
对象创建IOC容器时只需要引入父配置类即可。classpath
表示类路径下。例如 @PropertySource("classpath:jdbc.properties")
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.14</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
package com.domain;
public class Account {
private Integer id;
private String name;
private double money;
public Account() {
}
public Account(String name, double money) {
this.name = name;
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
package com.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
@Configuration
@ComponentScan(basePackages = {"com.Dao","com.service"})
@Import({jdbcConfig.class})
@PropertySource({"classpath:jdbc.properties"})
public class SpringConfiguration {
}
package com.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
public class jdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.jdbcUrl}")
private String jdbcUrl;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* dbUtils
* @param dataSource
* @return
*/
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource) {
return new QueryRunner(dataSource);
}
/**
* 配置数据源
* @return
*/
@Bean(name = "dataSource")
public DataSource createDataSource() {
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(jdbcUrl);
ds.setUser(username);
ds.setPassword(password);
return ds;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/zlf?serverTimezone=UTC
jdbc.username=root
jdbc.password=feng10.10
package com.Dao;
import com.domain.Account;
import java.util.List;
/**
*账户的持久层接口
*
*/
public interface IAccountDao {
/**
* 查询所有
* @return
*/
List<Account> selAll();
/**
* 查询一个
* @param id
* @return
*/
Account findAccountbyid(int id);
/**
* 保存
* @param account
*/
void saveAccount(Account account);
/**
*更新
* @param account
*/
void updateAccount(Account account);
/**
* 删除
* @param id
*/
void deleteAccount(int id) throws Exception;
}
package com.Dao.Impl;
import com.Dao.IAccountDao;
import com.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 账户的持久层实现类
*/
@Repository("IAccountDao")
public class IAccountDaoimpl implements IAccountDao {
@Autowired
private QueryRunner runner;
public void setRunner(QueryRunner runner) {
this.runner = runner;
}
public List<Account> selAll() {
try{
return runner.query("select *from account",new BeanListHandler<Account>(Account.class));
}catch (Exception e){
throw new RuntimeException(e);
}
}
public Account findAccountbyid(int id) {
try{
return runner.query("select *from account where id=?",new BeanHandler<Account>(Account.class),id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void saveAccount(Account account) {
try{
runner.update("insert into account(name,money) values(?,?)" ,account.getName(),account.getMoney());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void updateAccount(Account account) {
try{
runner.update("update account set name=? ,money=? where id=?" ,account.getName(),account.getMoney(),account.getId());
}catch (Exception e){
throw new RuntimeException(e);
}
}
public void deleteAccount(int id) {
try {
runner.update("delete from account where id=?",id);
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
package com.service;
import com.domain.Account;
import java.util.List;
/**
* 账户的业务层接口
*/
public interface IAccountService {
/**
* 查询所有
* @return
*/
List<Account> selAll();
/**
* 查询一个
* @param id
* @return
*/
Account findAccountbyid(int id);
/**
* 保存
* @param account
*/
void saveAccount(Account account);
/**
*更新
* @param account
*/
void updateAccount(Account account);
/**
* 删除
* @param id
*/
void deleteAccount(int id) throws Exception;
}
package com.service.Impl;
import com.Dao.IAccountDao;
import com.Dao.Impl.IAccountDaoimpl;
import com.domain.Account;
import com.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("IAccountService")
public class IAccountServiceimpl implements IAccountService {
@Autowired
private IAccountDao iAccountDao;
public List<Account> selAll() {
return iAccountDao.selAll();
}
public Account findAccountbyid(int id) {
return iAccountDao.findAccountbyid(id);
}
public void saveAccount(Account account) {
iAccountDao.saveAccount(account);
}
public void updateAccount(Account account) {
iAccountDao.updateAccount(account);
}
public void deleteAccount(int id) throws Exception {
iAccountDao.deleteAccount(id);
}
public void setiAccountDao(IAccountDaoimpl iAccountDao) {
this.iAccountDao=iAccountDao;
}
}
package com.test;
import com.config.SpringConfiguration;
import com.domain.Account;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.service.IAccountService;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.util.List;
public class accountServiceTest {
IAccountService iAccountService = null;
@Before
public void init() throws Exception {
final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
iAccountService = applicationContext.getBean("IAccountService", IAccountService.class);
}
/**
* 查询所有
*/
@Test
public void findAll() {
System.out.println(iAccountService.selAll());
}
/**
* 查询一位
*/
@Test
public void findOne() {
System.out.println(iAccountService.findAccountbyid(2));
}
/**
* 保存
*/
@Test
public void TestSave() {
iAccountService.saveAccount(new Account("小明", 1002.5));
}
/**
* 更新
*/
@Test
public void TestUpadata() {
final Account account = iAccountService.findAccountbyid(4);
account.setMoney(100002.5);
iAccountService.updateAccount(account);
}
/**
* 删除
*
* @throws Exception
*/
@Test
public void Testdalete() throws Exception {
iAccountService.deleteAccount(4);
}
}