一. 总概览:
二:Spring的核心包:
2.1:Spring的体系结构
Spring-beans和srping-core提供控制反转和依赖注入
Spring-Context上下文对象,基于beans和core
Spring-spel是Spring提供的一个表达式
三. IOC(控制反转):
3.1 概念:
将创建对象的过程交给Spring中的容器创建,而以前则是通过new关键字创建,创建对象的权限发生了改变。
3.2 传统方式解决问题的过程:
创建IAccountDao接口
public interface IAccountDao {
void save();
}
创建IAccountDaoImpl,模拟mysql数据库去存储实现IAccountDao类
public class IAccountDaoImpl implements IAccountDao {
@Override
public void save() {
System.out.println("通过mysql保存一位用户");
}
}
创建IAccountDaoOracleImpl,模拟Oracle存储用户实现IAccountDao类
public class IAccountOracleDaoImpl implements IAccountDao {
@Override
public void save() {
System.out.println("通过Oracle保存了用户!");
}
}
创建IAccountService接口,模拟业务层
public interface IAccountService {
void saveAccount();
}
创建IAccountServiceImpl实现业务接口。
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new IAccountDaoImpl();
@Override
public void save() {
accountDao.saveAccount();
}
}
测试类:
public class AccountTest {
@Test
public void AccountTest(){
IAccountService accountService = new IAccountServiceImpl();
accountService.saveAccount(); // 输出 通过mysql保存用户
}
}
传统方式创建对象,并调用对应的方法区实现相关的业务,不利于后期的维护,因为如果实现Oracle去保存用户,则实现类的dao层和实现类的业务层都需要更改代码。显然耦合性过高,而如何降低耦合性呢?
3.3 通过反射方法实现自定义IOC容器
在resources目录下创建bean.properties配置文件
# dao接口
accountDao=com.google.dao.Impl.IAccountDaoImpl
# accountDao=com.google.dao.Impl.IAccountOracleDaoImpl
# service接口
accountService=com.google.Service.Impl.IAccountServiceImpl
在创建一个BeanFactory工厂类(利用工厂模式进行解耦)
import java.util.ResourceBundle;
public class BeanFactory {
// 读取配置文件
private static ResourceBundle bundle;
static {
// 加载配置文件的工具类
bundle = ResourceBundle.getBundle("bean");
}
/**
*
* @param key 与bean.properties的key值保持一致,通过此参数去获取对应的实现类
* @param clazz 传入字节码文件,在创建对象时,则无需强制装换
* @param <T> 泛型,可以拓展更多的类使用该工厂。
* @return
*/
public static <T> T getBundle(String key,Class<T> clazz){
try {
// 根据key,获取配置文件的类全名
String className = bundle.getString(key);
// 创建对象
return (T)Class.forName(className).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
修改IAccountServiceImpl中的代码
public class AccountServiceImpl implements IAccountService {
// private IAccountDao accountDao = new IAccountDaoImpl();
// 通过工厂来创建对象
private IAccountDao accountDao = BeanFactory.getBundle("accountDao",IAccountDao.class);
@Override
public void save() {
accountDao.saveAccount();
}
}
测试类:
public class AccountTest {
@Test
public void save(){
IAccountService accountService = BeanFactory.getBundle("accountService",IAccountService.class);
accountService.saveAccount(); // 输出 通过mysql保存用户
}
}
以上代码虽然比传统的多,但是如果需要更换数据库存储用户,如通过Oracle去存储用户,则只要修改配置文件即可:
# dao接口
# accountDao=com.google.dao.Impl.IAccountDaoImpl
accountDao=com.google.dao.Impl.IAccountOracleDaoImpl
# service接口
accountService=com.google.Service.Impl.IAccountServiceImpl
尽管以上代码只能降低了耦合性,便于后期维护,但还是存在很多问题,因此接下来将用Spring实现IOC实现业务
3.4 通过xml方式配置IOC容器
添加依赖:spring-context
创建Account对象:
创建bean.xml配置文件
测试类
当然,以上只是IOC的入门,接下来将记录一些常用的bean配置里面的参数
其中scope设置bean的作用范围,如:
scope为singleton(默认值),在测试类中的代码
测试结果:两者的地址为一样,说明创建对个对象,实际上就是同一个对象。如图:
而如果将scope改为prorotype,则更改配置文件:
测试结果为,两个对象的地址不一致,说明创建了两个对象:如果
而lazy-init的默认值为false,且必须为scope=singleton才能使用,默认的时候测试代码及其效果
如果将其设置为true后:
再次测试将不会输出任何语句,只要将代码改为,才会输出:
3.5 通过容器创建对象的方式(3种):
1. 默认无参构造器,上面所演示的代码,即通过无参构造
2. 通过类的静态方法:
创建普通类:
public class AccountStatic {
public AccountStatic(){
System.out.println("无参构造数被调用");
}
public static AccountStatic accountStatic(){
System.out.println("通过静态方法创建对象");
return new AccountStatic();
}
}
创建配置文件:关键点factory-method,如果没有将会通过默认无参构造器创建对象
测试类
@Test
public void getObjectByStatic(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
// 测试输出:通过静态方法创建对象
// 无参构造数被调用
AccountStatic account = applicationContext.getBean("accountStatic", AccountStatic.class);
}
3. 调用类的实例方法
普通类
public class Account {
// 创建实例方法
public Account getAccount(){
System.out.println("通过实例方法创建对象");
return new Account();
}
}
创建配置文件:先创建工厂对象,再通过工厂对象进行调用创建对象
<!--通过创建工厂对象-->
<bean id="factory" class="com.google.pojo.Account" />
<!--再通过工厂对象调用-->
<bean id="account" factory-bean="factory" factory-method="getAccount" />
测试类:
public class AccountTest {
private ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
@Test
public void accountTest(){
// 输出 创建了Account对象!
Account account = applicationContext.getBean("account", Account.class);
}
四:依赖注入:就是在创建对象之后,给对象属性赋值。
4.1 带参构造函数注入:即通过有参构造函数创建对象
4.1.1: 创建普通类
public class Account {
private String name;
public Account(String name) {
this.name = name;
}
public Account() {
}
@Override
public String toString() {
return "Account{" +
"name='" + name + '\'' +
'}';
}
}
4.1.2 创建配置:name与参数名称一致,value代表赋值
<!--constructor-arg 通过构造函数创建对象、给对象属性赋值
index 表示第几个构造函数参数,从0开始
name 构造函数参数名称,如:public Person(int id123) 中的id123
type 参数类型
value 参数值 (直接给构造函数参数赋值。)
ref 表示引用ioc容器中的对象。(重点)
ref="str" 表示引入容器中bean的id是str的对象,注入方法参数。
-->
<bean id="account" class="com.google.pojo.Account">
<!--name与参数名称一致,value代表赋值-->
<constructor-arg name="name" value="tom" />
</bean>
3.1.3 测试类:
4.2 set方式注入:即通过有set方式创建对象
4.2.1,创建普通类
public class Account {
private String name;
public Account(String name) {
this.name = name;
}
public Account() {
}
// 创建Set方法
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Account{" +
"name='" + name + '\'' +
'}';
}
}
4.2.2,创建配置:其中name与setXxx中的Xxx保持一致并且首字母小写
<!--其中name与setXxx中的Xxx保持一致并且首字母小写-->
<bean id="account" class="com.google.pojo.Account">
<property name="name" value="jerry" />
</bean>
4.2.3:测试类省略
4.3 P名称空间注入:实质还是通过set方式注入
4.3.3:配置
<!--其中name与setXxx中的Xxx保持一致-->
<bean id="account" class="com.google.pojo.Account" p:name="james" />
4.4 集合类型注入:可以百度简单了解下。