写在前面: 最近学习Spring告一段落了,这文章可以简单的告诉你怎么使用Spring的IOC控制反转,以及IOC注解开发。 作者是一个学生,没有能力写得太深,需要的可以去看看大佬们的手撕Spring源码。
本文将告诉你:
IOC控制反转是什么?
如何使用Spring的IOC控制反转?
XML创建bean的三种形式
IOC控制反转的注解开发
如果对你有帮助可以点赞支持一下^ _ ^
公众号:小白编码
Spring是分层的Java SE/EE应用 full-stack轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。
我们都知道Service层需要Dao层的支持,使用Dao获取数据处理业务:(这里需要AccountDao的实例)
上图使用了工厂获取实例。 目的就是为了解耦。
我获取AccountDao的实例的时候有两种方法:
那么问题来了?到底什么是控制反转?
通过下图可知:获取实例的方式转变为从工厂里获取 ,而不是直接new一个实例,它将获取实例的控制权 ,交给了BeanFactory来做,而不是自己直接面对实例资源。让工厂来面对实例资源。这种控制权发生了转变。就叫控制反转。
开发前准备:
1.导入Maven依赖坐标:或者官方下载: http://repo.springsource.org/libs-release-local/org/springframework/spring
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.2.RELEASEversion>
dependency>
Dao层:(接口自己写)
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存一个账户");
}
}
Service层:(接口自己写)
public class AccountService implements IAccountService {
private IAccountDao iAccountDao = new AccountDaoImpl();//这里我还是先使用new的方式
@Override
public void saveAccount() {
iAccountDao.saveAccount();
}
}
Xml配置IOC容器:
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- bean标签:用于配置让spring创建对象,并且存入ioc容器之中 id属性:对象的唯一标识。
class属性:指定要创建对象的全限定类名 -->
<!--配置AccountService的实现类,放入容器-->
<bean id="accountService" class="cn.codewhite.service.Impl.AccountService"></bean>
<!--配置AccountDao的实现类,放入容器-->
<bean id="accountDao" class="cn.codewhite.dao.Impl.AccountDaoImpl"></bean>
</beans>
测试程序:
public class Clinet {
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
// IAccountService iAccountService = new AccountService();
//在使用SpringIOC控制反转前,我们是通过new 的方式获取Service实例
// 现在配置了IOC控制反转,new实例交给了Spring管理,根据配置的id获取容器里配置的实现类
IAccountService accountService = (IAccountService) ac.getBean("accountService");
// IAccountDao accountDao = new AccountDaoImpl();原先是通过new方式获取Dao实例
//通过字节码文件,获得运行时类
IAccountDao accountDao = ac.getBean("accountDao", IAccountDao.class);
System.out.println(accountService);
System.out.println(accountDao);
//测试保存账户
accountService.saveAccount();
}
}
结论:通过这个简单的案例,我们就知道了,可以配置将实现类,配置到Spring的IOC容器中,在需要获取Dao或者Service等其他实现类的时候,从IOC容器中获取。而不是直接new的方式获取实例。实现了控制反转
创建Bean,也就是配置bean之后(放入IOC容器),通过getBean()来获取实例。
使用默认构造函数创建。
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数(空参构造器),则对象无法创建。
<bean id="accountService" class="cn.codewhite.service.Impl.AccountServiceImpl">bean>
AccountService实现类:
public class AccountServiceImpl implements IAccountService {
//必须是空参构造器,否则第一种方式无法创建对象
public AccountServiceImpl() {
}
@Override
public void saveAccount() {
System.out.println("service中的saveAccount()执行了");
}
}
配置完之后,就可以通过根据配置的id获取实例。
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据id获取Bean对象,获取实例
IAccountService accountService1 = (IAccountService) ac.getBean("accountService");
使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
<bean id="instanceFactory" class="cn.codewhite.factory.InstanceFactory">bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService">bean>
工厂类: (AccountServce实现类看上边)
/**
* 模拟一个工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
*/
public class InstanceFactory {
//获取AccountService的实例
public IAccountService getAccountService() {
return new AccountServiceImpl();
}
}
测试: 此时的实例service的实例是从配置过的IOC里的工厂里获取的实例。
public class Clinet {
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据id获取Bean对象
IAccountService accountService = (IAccountService) ac.getBean("accountService");
accountService.saveAccount();
}
}
结果:
service中的saveAccount()执行了
使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
xml:(配置工厂里的静态方法)
<bean id="accountService" class="cn.codewhite.factory.StaticFactory" factory-method="getAccountService">bean>
工厂类:
public class StaticFactory {
//静态方法
public static IAccountService getAccountService() {
//返回AccontService实例
return new AccountServiceImpl();
}
}
测试如第二种方式的结果。
作用:用于配置对象让spring来创建的。
默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
属性:
id:给对象在容器中提供一个唯一标识。用于获取对象。
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
scope:指定对象的作用范围。常用: singleton :默认值,单例的. prototype :多例的.
**依赖注入:Dependency Injection。(DI)**它是spring框架核心ioc的具体实现。 简单说就是:(为指定的类里面的属性赋值实例。相当于赋值,)
依赖关系的管理:我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出现没有依赖的情况。ioc解耦只是降低他们的依赖关系,但不会消除。
IOC的作用: 降低程序间的耦合(依赖关系)
例如:我们的业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用spring之后,就让spring来维护了。 简单的说,就是坐等**框架把持久层对象传入业务层,而不用我们自己去获取。**在当前类需要用到其他类的对象,我们只需要在配置文件中说明
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性:
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
name: ★用于指定给构造函数中指定名称的参数赋值 常用的
以上三个用于指定给构造函数中哪个参数赋值
value: 用于提供基本类型和String类型的数据
ref: 用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
xml配置:
<bean id="accountService" class="cn.codewhite.service.Impl.AccountServiceImpl">
<constructor-arg name="name" value="小白">constructor-arg>
<constructor-arg name="age" value="18">constructor-arg>
<constructor-arg name="birthday" ref="now">constructor-arg>
bean>
<bean id="now" class="java.util.Date">bean>
Service:
public class AccountServiceImpl implements IAccountService {
private String name;
private Integer age;
private Date birthday;
public AccountServiceImpl(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
}
涉及的标签:property
出现的位置:bean标签的内部
标签的属性:
name: 用于指定注入时所调用的set方法名称
value: 用于提供基本类型和String类型的数据
ref: 用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势: 创建对象时没有明确的限制,可以直接使用默认构造函数
弊端: 如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
xml:
<bean id="accountService2" class="cn.codewhite.service.Impl.AccountServiceImpl2">
<property name="name" value="小白">property>
<property name="age" value="18">property>
<property name="birthday" ref="now">property>
bean>
<bean id="now" class="java.util.Date">bean>
Service: (需要提供set方法)
public class AccountServiceImpl2 implements IAccountService {
private String name;
private Integer age;
private Date birthday;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
用于给List结构集合注入的标签: list
、array
、set
用于个Map结构集合注入的标签: map
、 props
结构相同,标签可以互换
Bean.xml:
<bean id="accountService3" class="cn.codewhite.service.Impl.AccountServiceImpl3">
<property name="myStrs">
<array>
<value>aaavalue>
array>
property>
<property name="myList">
<list>
<value>AAvalue>
list>
property>
<property name="mySet">
<set>
<value>AAAvalue>
set>
property>
<property name="myMap">
<map>
<entry key="keyA" value="AAA">entry>
<entry key="keyB" >
<value>BBBvalue>
entry>
<entry key="keyC" value="CCC">entry>
map>
property>
<property name="myProps">
<props>
<prop key="PropA">AAAprop>
props>
property>
bean>
Servcie: (set方法我这里没写!需要自己补充,否则无法注入)
public class AccountServiceImpl3 implements IAccountService {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String, String> myMap;
private Properties myProps;
}
Dao层:
AccountDaoImpl:
@Repository("accountDao1")
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("accountDao1中:保存一个账户");
}
}
AccountDaoImpl2:
@Repository("accountDao2")
public class AccountDaoImpl2 implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("accountDao2中:保存一个账户");
}
}
Service:
@Service("accountService")
@Scope("singleton")
public class AccountServiceImpl implements IAccountService {
// @Autowired
// @Qualifier("accountDao1")
@Resource(name = "accountDao2")
private IAccountDao iAccountDao;
@PostConstruct
public void init() {
System.out.println("init创建。。。。");
}
@PreDestroy
public void destroy() {
System.out.println("destroy销毁。。。。");
}
@Override
public void saveAccount() {
iAccountDao.saveAccount();
}
}
绑定使用注解的包:(非常重要)
<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">
<context:component-scan base-package="cn.codewhite">context:component-scan>
beans>
相当于:
作用: 把资源让spring来管理。相当于在xml中配置一个bean。
属性: value:指定bean的id。 如果不指定value属性,默认bean的id是当前类的类名。首字母小写。
他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的 .他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
@Controller:一般用于表现层的注解。
@Service:一般用于业务层的注解。
@Repository:一般用于持久层的注解。
细节:如果注解中有且只有一个属性要赋值时,且名称是value,value在赋值是可以不写。
相当于:
作用: 自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
如果Ioc容器中有多个类型匹配时: 出现位置:可以是变量上,也可以是方法上
细节:在使用注解注入时,set方法就不是必须的了,可以省略。
作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。必须和@Autowire一起使用;但是给方法参数注入时,可以独立使用。
属性:
value:用于指定注入bean的id。
作用: 直接按照Bean的id注入。可以独立使用。它也只能注入其他bean类型。
属性: name:指定bean的id。
调用Service测试结果:
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上q述注解实现。
另外,集合类型的注入只能通过XML来实现。
作用:用于注入基本类型和String类型的数据 ,
属性:value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
SpEL的写法:${表达式}
注入配置文件中的key:(需要使用@PropertySource(“classpath:jdbcConfig.properties”))引用
配置文件:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=123
相当于:
作用: 指定bean的作用范围。
属性: value:指定范围的值。
取值:(singleton或prototype 常用)request,session,globalsession
测试是否单例:
相当于:
作用: 用于指定初始化方法。
作用: 用于指定销毁方法。
写得不好,请见谅,如果需要PDF版的可以找我。