Spring学习记录(二):Spring IOC学习
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
上一篇概括了Spring的概念问题、优势以及框架结构,现在正式进入IOC的整理。
一、IOC是什么?
控制反装不是一种技术,而是设计思想。他的目的很明确,为设计出更加松耦合的程序。其中控制指的是程序中对象的控制权限,反转指的是对象的控制权由原来的开发者转移到Spring容器控制。
二、IOC容器的自定义
1.引入库
<dependencies>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2.传统方法编写
编写Dao层代码:
public interface UserDao {
void save();
}
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("保存成功");
}
}
编写service层:
public interface UserService {
void save();
}
public class UserServiceImpl implements UserService {
@Override
public void save() {
UserDao userDao = new UserDaoImpl();
userDao.save();
}
}
编写测试代码:
public class SpringTest {
@Test
public void test1(){
UserService userService=new UserServiceImpl();
userService.save();
}
}
我们可以看到,传统方式调用存在编译期依赖,耦合过高,而且每次new对象会造成服务器压力过大,我们最重要的就是去掉这个new对象的过程完成解耦合。
3.改造传统方法,降低耦合度(自定义IOC容器)
使用反射去掉new关键字:
public class UserServiceImpl implements UserService {
@Override
public void save() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//UserDao userDao = new UserDaoImpl();
//使用反射
UserDao userDao = (UserDao) Class.forName("com.ssm.dao.impl.UserDaoImpl").newInstance();
userDao.save();
}
}
在编译期就算没有Dao也不会报错,解决编译期依赖问题。但是存在硬编码问题。改造思路:反射+配置文件。
改造步骤分析:
1.准备一个配置文件:beans.xml
<beans>
<!--id:标识 class:存的全路径-->
<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl"></bean>
</beans>
2.编写一个工厂工具类,工厂类使用dom4j来解析配置文件,获取到类全路径。
3.使用反射生成对应类的实例对象,存到map中(IOC容器)。
public class BeanFactory {
private static Map<String, Object> iocMap = new HashMap<>();
//程序启动时,初始化对象实例
static {
//1.读取配置文件
InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
//2.解析xml(使用dom4j)
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(resourceAsStream);
//3.编写xpath表达式
String xpath = "//bean";
//4.获取到所有bean标签
List<Element> list = document.selectNodes(xpath);
//5.遍历并使用反射创建对象实例,存到map中(充当IOC容器)
for (Element element : list) {
String id = element.attributeValue("id");
String className = element.attributeValue("class");
//6.使用反射生成实例对象
Object o = Class.forName(className).newInstance();
//7.存入map中 key:id value:o
iocMap.put(id, o);
}
} catch (DocumentException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
public static Object getBean(String beanId){
return iocMap.get(beanId);
}
}
4.调用。
public class UserServiceImpl implements UserService {
@Override
public void save() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//UserDao userDao = new UserDaoImpl();
//使用反射
//UserDao userDao = (UserDao) Class.forName("com.ssm.dao.impl.UserDaoImpl").newInstance();
//利用工厂类进行获取
UserDao userDao = (UserDao)BeanFactory.getBean("userDao");
userDao.save();
}
}
4.总结
BeanFactory就是一个简单的IOC容器,之前我们需要一个UserDao实例来new对象,现在我们只需要交给Bean工厂(IOC)控制,达到解耦合的目的。
三、利用Spring进行快速入门
1.引入库
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
2.编写核心配置文件
依旧利用上文的Dao形式,创建配置文件applicationContext.xml(不要忘记约束头)
<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl"></bean>
3.利用SpringAPI获取
public class SpringTest {
@Test
public void test1(){
//获取spring上下文对象,借助上下文对象可以获取到IOC容器中的bean对象
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//使用上下文对象,从IOC容器中获取Bean对象
UserDao userDao = (UserDao) applicationContext.getBean("UserDao");
userDao.sava();
}
}
四、Spring相关API
主要关注BeanFactory和ApplicationContext
1.BeanFactory
BeanFactory是IOC容器的核心接口,定义了IOC基本功能。
public void test2(){
//核心接口,但是不会创建bean对象存入容器中
BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
//在getBean的时候才真正创建bean对象
UserDao userDao = (UserDao)xmlBeanFactory.getBean("UserDao");
userDao.sava();
}
2.ApplicationContext
代表应用上下文对象,获得IOC容器的Bean对象。
特点:启动时就创建并加载所有实例(和BeanFactory的不同之处)。
常用实现类:
//当使用注解配置容器对象时,创建Spring容器读取注释
AnnotationConfigApplicationContext
//从磁盘路径加载配置文件
FileSystemXmlApplicationContext
//从类的根路径加载配置文件
ClassPathXmlApplicationContext
常用的getBean有三个:
//根据Bean的id从容器中获得Bean实例,需要强转
Object getBean(String var1) throws BeansException;
//根据类型从容器中获取Bean实例,当有多个相同类型时,会报错
<T> T getBean(Class<T> var1) throws BeansException;
//根据Bean的id和类型获取Bean实例,结合上面两种方法
<T> T getBean(String var1, Class<T> var2) throws BeansException;
PS:getBean方法来自BeanFactory接口。
五、Spring配置文件
1.基本配置
<bean id="" class="" scope=""></bean>
#默认调用类中的无参构造
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |
2.Bean的生命周期
<bean id="" class="" scope="" init-method="" destroy-method=""></bean>
init-method="" 初始化。
destroy-method="" 销毁时。
六、Bean实例化三种方式
无参构造实例化
工厂静态方法实例化
工厂普通方法实例化
1.工厂静态方法实例化
假设依赖jar包中有个A类,A类中有静态方法a1,其返回值是一个B对象,如果我们频繁调用B对象,那我们可以将创建权交给SpringIOC进行处理。
public class StaticFactoryBean {
static UserDao createUserDao(){
return new UserDaoImpl();
}
}
<bean id="userDao" class="com.ssm.dao.StaticFactoryBean" factory-method="createUserDao"></bean>
2.工厂普通方法实例化
假设依赖jar包中有个A类,A类中有普通方法a1,其返回值是一个B对象,如果我们频繁调用B对象,那我们可以将创建权交给SpringIOC进行处理。
public class DynamicFactoryBean {
public UserDao createUserDao() {
return new UserDaoImpl();
}
}
<bean id="dynamicFactoryBean" class="com.ssm.dao.DynamicFactoryBean"></bean>
<bean id="userDao" factory-bean="dynamicFactoryBean" factory-method="createUserDao"></bean>
七、Bean依赖注入
1.依赖注入概述
他是SpringIOC的具体实现,通过控制反转后,通过框架把持久层对象传入业务层,而不用我们自己去获取。
2.Bean依赖注入的方式
2.1 构造方法
在UserServiceImpl中创建有参构造
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.sava();
}
}
配置xml
<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.ssm.service.impl.UserServiceImpl">
<constructor-arg index="0" type="com.ssm.dao.UserDao" ref="UserDao"></constructor-arg>
</bean>
测试
@Test
public void test3(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService)applicationContext.getBean("userService");
userService.save();
}
2.1 set方法(比较常用)
在UserServiceImpl中创建有参构造(把有参构造删除)
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.sava();
}
}
配置xml
<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.ssm.service.impl.UserServiceImpl">
<property name="userDao" ref="UserDao"></property>
</bean>
测试
@Test
public void test3(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService)applicationContext.getBean("userService");
userService.save();
}
3.Bean依赖注入的数据类型
3.1 普通数据类型
改造一下UserDaoImpl
public class UserDaoImpl implements UserDao {
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public void sava() {
System.out.println(username);
System.out.println(password);
System.out.println("保存成功");
}
}
配置xml
<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl">
<property name="username" value="admin"></property>
<property name="password" value="123456"></property>
</bean>
<bean id="userService" class="com.ssm.service.impl.UserServiceImpl">
<property name="userDao" ref="UserDao"></property>
</bean>
测试一下~
3.2 集合数据类型
利用上述普通类型注入创建一个User对象并注入容器(练习一下)
改造一下UserDaoImpl
public class UserDaoImpl implements UserDao {
private List<Object> list;
private Set<Object> set;
private Object[] array;
private Map<String,Object> map;
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public void setMap(Map<String, Object> map) {
this.map = map;
}
public void setArray(Object[] array) {
this.array = array;
}
public void setSet(Set<Object> set) {
this.set = set;
}
public void setList(List<Object> list) {
this.list = list;
}
@Override
public void sava() {
System.out.println(list);
System.out.println(set);
System.out.println(Arrays.toString(array));
System.out.println(map);
System.out.println(properties);
System.out.println("保存成功");
}
}
配置xml
<bean id="User" class="com.ssm.damain.User">
<property name="username" value="admin"></property>
<property name="password" value="123456"></property>
</bean>
<bean id="UserDao" class="com.ssm.dao.impl.UserDaoImpl">
<property name="list">
<list>
<value>simon</value>
<ref bean="User"></ref>
</list>
</property>
<property name="set">
<set>
<value>simon2</value>
<ref bean="User"></ref>
</set>
</property>
<property name="array">
<array>
<value>simon3</value>
<ref bean="User"></ref>
</array>
</property>
<property name="map">
<map>
<entry key="1" value="simon4"></entry>
<entry key="2" value-ref="User"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="k1">v1</prop>
<prop key="k2">v2</prop>
</props>
</property>
</bean>
<bean id="userService" class="com.ssm.service.impl.UserServiceImpl">
<property name="userDao" ref="UserDao"></property>
</bean>
测试一下~
八、Spring注解开发
1.常用注解介绍(IOC)
注解 | 说明 |
---|---|
@Component | 使用在类上用于实例化Bean |
@Controller | 对应表现层的Bean |
@ Service | 对应的是业务层Bean |
@ Repository | 对应数据访问层Bean |
@Autowired | 使用在字段上根据类型进行依赖注入 |
只要根据对应业务层中加入相关注解即可代替xml中的相关bean标签,如Service层改写:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.sava();
}
}