下一篇:Spring:Spring简介、IOC、AOP【二】
项目地址:https://github.com/hpp3501/spring-demo
文章目录
1. Spring
1.1 简介
- 创始人 Rod Johnson
- 2002年,首次推出了Spring框架的雏形:interface21框架
- 2004年,interface21框架经过不断的优化升级,于2004年3月24发布了1.0正式版Spring框 架。
- spring理念:使现有的技术更容易使用,本身整合了市面上大多的技术
1.2 优点
- spring是要给开源的免费的框架
- spring是轻量级、非入侵式的框架 (在框架中引入spring,不会改变原框架的结构)
- 控制反转(IOC)、面向切面编程(AOP)
- 支持事务的处理,支持对框架的整合
总结:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
1.3 弊端
由于发展的迅速,慢慢的违背了最初的理念!配置变得繁琐。不过后来的SpringBoot解决了配置繁琐的问题。
1.4 组成
1.5 拓展
-
Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速开发单体架构
- 约定大于配置
-
Spring Cloud
- SpringCloud是基于SpringBoot实现的。
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot前需要完全掌握Spring及SpringMVC。
2. 控制反转-IOC
理论推导
控制反转IOC(Inversion of control)不是一种技术,而是一种思想。多种方式都已经很好的实现了IOC,比如xml配置或者注解。
1.UserDao 接口
package com.kuang.dao;
public interface UserDao {
void getUser();
}
2.UserDaoImpl 实现类
package com.kuang.dao;
public class UserDaoImpl implements UserDao {
public void getUser() {
System.out.println("获取默认用户的数据");
}
}
package com.kuang.dao;
public class UserDaoMysqlImpl implements UserDao{
public void getUser() {
System.out.println("Mysql获取默认用户数据");
}
}
3.UserService 业务接口
package com.kuang.service;
public interface UserService {
void getUser();
}
4.UserServiceImpl 业务实现类
package com.kuang.service;
import com.kuang.dao.UserDao;
public class UserServiceImpl implements UserService {
//region 原来的方式,每次增加一个userDao的实现类都需要修改UserServiceImpl
//private UserDao userDao = new UserDaoImpl();
//private UserDao userDao = new UserDaoMysqlImpl();
//private UserDao userDao = new UserDaoOracleImpl();
//endregion
//region新的方式,通过set注入值,将变化放在用户层,而UserServiceImpl则不需要改变
private UserDao userDao;
//利用set进行动态注入值
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
//endregion
public void getUser() {
userDao.getUser();
}
}
在我们之前的业务中,用户的需求可能回影响原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价也会十分的大。
public class UserServiceImpl implements UserService {
//region 原来的方式,每次增加一个userDao的实现类都需要修改UserServiceImpl
//private UserDao userDao = new UserDaoImpl();
//private UserDao userDao = new UserDaoMysqlImpl();
//private UserDao userDao = new UserDaoOracleImpl();
//endregion
//region新的方式,通过set注入值,将变化放在用户层,而UserServiceImpl则不需要改变
private UserDao userDao;
//利用set进行动态注入值
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
//endregion
}
测试类
import com.kuang.dao.UserDaoMysqlImpl;
import com.kuang.service.UserServiceImpl;
public class MyTest {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoMysqlImpl());
userService.getUser();
}
}
- 之前,程序是主动创建对象!控制权在开发人员手中。
- 使用了set注入之后,程序不再具有主动性,而是被动的接收对象。
这种思想,从本质上解决了问题。开发人员不用再去管理对象的创建了,很大的降低了系统的耦合性,开发人员可以更专注于业务的实现。这就是IOC的原型。 、
3. 通过xml配置理解控制反转
创建一个Hello对象
package com.kuang.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
添加一个配置文件
<?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">
<!--使用Spring来创建对象,再Spring这些成为Bean-->
<bean id="hello" class="com.kuang.pojo.Hello">
<property name="str" value="Spring" />
</bean>
</beans>
创建一个测试类
import com.kuang.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//获取spring上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//对象都在spring中管理了,要使用的话直接取出来就可以了
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
传统的程序,对象的创建是由程序控制的。上面的例子中,Hello对象的创建没有直接去new实例化,而是交给Spring去创建,这就是通过xml方式实现的控制反转。再添加新的对象时,不需要去修改程序,只用修改xml配置即可。
4. IOC创建对象的方式
- 使用无参构造创建对象,默认
- 使用有参构造创建对象有三种方式
2.1. 下标赋值
2.2 类型赋值<!--有参构造方式1:通过下标给参数赋值--> <constructor-arg index="0" value="zhagnsanfeng"/>
2.3 直接通过参数名赋值<!--有参构造方式2:通过类型给参数赋值--> <!--<constructor-arg type="java.lang.String" value="zhangsan"/>-->
<!--有参构造方式3:直接通过参数名赋值--> <constructor-arg name="name" value="zhangsanfeng"/>
总结:在配置文件加载的时候,Spring容器中管理的对象就已经被初始化了。默认是单例实现,无论get多少次都只初始化了一次。
5. 配置
5.1 别名
<!--别名,如果添加了别名,我们也可以使用别名获取对象-->
<alias name="user" alias="userNew"/>
User user = (User) context.getBean("user");
//User user = (User) context.getBean("userNew");
5.2 Bean的配置
<!--
id:bean的唯一标识
class:bean对象所对应的全限定名:包名+类名
name:也是别名,而且可以逗号分隔取多个别名
-->
<bean id="user" class="com.kuang.pojo.User" name="user2">
5.3 imort
这个import,一般用于团队开发,它可以将多个配置文件导入合并为一个。
比如将多个xml导入到一个总的applicationContext.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">
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
</beans>
6. DI(Dependency Injection)依赖注入
依赖注入是控制反转的一种实现方式
6.1 构造注入
前面说过了
6.2 set方式注入【常用】
- 依赖注入:set注入
- 依赖:bean对象的创建依赖于spring容器
- 注入:bean对象中的所有属性,由容器来注入
- 各种类型的注入方式
Address类
package com.kuang.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
Student类
package com.kuang.pojo;
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private Properties info;
private String wife;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", card=" + card +
", games=" + games +
", info=" + info +
", wife='" + wife + '\'' +
'}';
}
}
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">
<bean id="address" class="com.kuang.pojo.Address">
<property name="address" value="北京"/>
</bean>
<bean id="student" class="com.kuang.pojo.Student">
<!--普通值注入,直接给value赋值-->
<property name="name" value="张三"/>
<!--类变量注入:ref引入Bean-->
<property name="address" ref="address"/>
<!--数组注入-->
<property name="books">
<array>
<value>JAVA实战</value>
<value>Python入门</value>
<value>深入理解Nginx</value>
</array>
</property>
<!--list注入-->
<property name="hobbys">
<list>
<value>听歌</value>
<value>打游戏</value>
</list>
</property>
<!--map注入-->
<property name="card">
<map>
<entry key="身份证" value="456987451257895461"/>
<entry key="银行卡" value="62254802548115"/>
</map>
</property>
<!--set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>CF</value>
</set>
</property>
<!--null注入-->
<property name="wife">
<null/>
</property>
<!--Properties注入-->
<property name="info">
<props>
<prop key="学号">200510</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
</beans>
测试类
import com.kuang.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student)context.getBean("student");
System.out.println(student);
}
}
打印结果
Student{name='张三', address=Address{address='北京'}, books=[JAVA实战, Python入门, 深入理解Nginx], hobbys=[听歌, 打游戏], card={身份证=456987451257895461, 银行卡=62254802548115}, games=[LOL, CF], info={学号=200510, 性别=男}, wife='null'}
6.3 其它方式
6.3.1 p命名空间注入
需要导入p命名空间xml约束,其实就是对应的set注入,只不过写法不一样。
测试类
6.3.2 c命名空间注入
必须要有有参构造,需要导入c命名空间xml约束,其实就是对应的构造方法注入,只不过写法不一样。
测试类
6.4 Bean作用域
6.4.1 单例模式【默认】
原型模式:scope=“singleton”。默认就是单例模式,无论从容器中获取多少次都只有一个实例。
<bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="李四" />
<bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="李四" scope="singleton" />
以上两段代码是相同的。
6.4.2 原型模式
原型模式:scope=“prototype”。每次从容器中获取的时候都会产生一个新对象。
<!--通过构造器注入:construct-args-->
<bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="李四" scope="prototype" />
6.4.3 其他模式
- request
- session
- application
以上模式只能在web开发中使用。