Spring框架(Ioc+DI)
一、MVC思想
M:Model 数据模型(User.java)
V:view 视图(页面jsp,html...)
C:Controller控制器(控制业务逻辑)
框架介绍:
| bean管理 | Spring框架 |
| ---- | --- |
|持久层操作|MyBatis Hibernate Spring Data Jpa|
|servlet开发 | SpringMVC框架 Struts2|
|微服务|Dubbo (阿里) Spring Boot和Spring Cloud|
|缓存| Redis ...|
二、Spring框架概念
spring 是众多开源java项目中的一员,基于分层的 javaEE 应用一站式轻量级开源框架,主要核心是 Ioc(控制反转/依赖注入)与Aop(面向切面)两大技术,实现项目在开发过程中的轻松解耦,提高项目的开发效率。 在项目中引入spring,立即可以带来下面的好处:降低组件之间的耦合度,实现软件各层之间的解耦。可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播。 容器提供单例模式支持,开发人员不再需要自己 编写实现代码。 容器提供了 AOP 技术,利用它很容易实现如权限拦截、运行 期监控等功能。
核心点:Ioc+DI+Aop
SSM:Spring + SpringMVC + MyBatis
三、Spring框架环境搭建
创建maven项目
添加 spring 框架依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
- 编写 bean和测试代码
package com.shsxt.service;
public class HelloService {
public void hello(){ System.out.println("hello spring");
}
}
- Spring配置文件的编写
在 src 下新建xml文件,并拷贝官网文档提供的模板内容到 xml 中,配置 bean 到xml中,把对应bean 纳入到 spring 容器来管理
<?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">
<!--
xmlns 即 xml namespace xml使用的命名空间
xmlns:xsi 即xml schema instance xml 遵守的具体规范
xsi:schemaLocation 本文档xml遵守的规范 官方指定
-->
<bean id="helloService" class="com.shsxt.dao.HelloService"></bean>
</beans>
- 验证Spring框架是否搭建成功
测试文件:
/***
* 1. 加载配置文件
* 2. 获取HelloService的Bean对象
* 3.使用bean
* */
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
HelloService helloService = (HelloService) context.getBean("helloService");
helloService.hello();
四、简单模拟Ioc容器
- 引入xml解析dom4j依赖
<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>
2.工厂接口定义
package com.shsxt;
/**
* Created by xlf on 2019/7/4.
*/
public interface ApplicationContext {
public Object getBean(String id);
}
- bean属性对象定义
package com.shsxt;
/**
* Created by xlf on 2019/7/4.
*/
public class SxtBean {
private String id;
private String clazz;
public SxtBean() {
}
public SxtBean(String id, String clazz) {
this.id = id;
this.clazz = clazz;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}
- 实现类
package com.shsxt;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by xlf on 2019/7/4.
*/
public class SxtApplicationContext implements ApplicationContext {
/***
* 1. 读取xml并解析
* 2. 反射生成bean对象
* 3. 返回执行bean
* */
private String xmlPath;// xml配置文件路径
private List<SxtBean> sxtBeanList = new ArrayList<>();// 存储解析结果
private Map<String, Object> beanMap = new HashMap<>();// 存储反射结果
public SxtApplicationContext(String xmlPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
this.xmlPath = xmlPath;
// 解析xml
parseXml(xmlPath);
// 反射生成bean
initBean();
}
/**
* 初始化Bean
*/
private void initBean() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
if(sxtBeanList.size()>0){
for(SxtBean bean : sxtBeanList){
String id = bean.getId();
String clazz = bean.getClazz();
beanMap.put(id, Class.forName(clazz).newInstance());
}
}
}
/**
* 解析xml
* @param xmlPath
*/
private void parseXml(String xmlPath) {
if(null!=xmlPath && !"".equals(xmlPath)){
// dom4j
SAXReader reader = new SAXReader();
try {
// 1. 拿到xml
URL url = this.getClass().getClassLoader().getResource(xmlPath);
Document document = reader.read(url);
// 2. 解析xml
List<Element> list = document.selectNodes("/beans/bean");
// 3. 获取属性值
for (Element e : list){
// System.out.println("id: "+e.attributeValue("id")+" class: "+e.attributeValue("class"));
String id = e.attributeValue("id");
String clazz = e.attributeValue("class");
sxtBeanList.add(new SxtBean(id, clazz));
}
} catch (DocumentException e) {
e.printStackTrace();
}
}else{
System.err.println("文件不存在");
}
}
@Override
public Object getBean(String id) {
return beanMap.get(id);
}
}
五、多文件加载情况
- 编写多个配置文件
如:Dao.xml,Service.xml,Controller.xml
- 测试有两种方式
方式一:
ApplicationContext context = new ClassPathXmlApplicationContext("Dao.xml","Service.xml","Controller.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
userDao.show();
UserService userService = (UserService) context.getBean("userService");
userService.show();
UserController userController = (UserController) context.getBean("userController");
userController.show();
方式二:
将多个xml文件导入spring.xml中:
<import resource="dao.xml"/>
<import resource="service.xml"/>
<import resource="controller.xml"/>
测试:
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
userDao.show();
......
六、Spring Ioc实例化bean对象的三种方式
1、构造器
类中必须有空构造
<!-- 1. 通过构造器 -->
<bean id="helloService" class="com.shsxt.service.HelloService"></bean>
2、静态工厂方法
可以控制bean的初始化,但是要保证工厂方法必须是静态方法
配置
<!-- 2. 静态工厂, 初始化bean -->
<bean id="personService" class="com.shsxt.factory.StaticFactory" factory-method="getPersonService"></bean>
静态工厂类
package com.shsxt.factory;
import com.shsxt.service.PersonService;
/**
* Created by xlf on 2019/7/4.
*/
public class StaticFactory {
public static PersonService getPersonService(){
return new PersonService("sxt");
}
}
3、实例化工厂
不局限于静态方法。多用于框架整合。
配置
<!-- 3. 实例化工厂 -->
<bean id="initFactory" class="com.shsxt.factory.InitFactory"></bean>
<bean id="accountService" factory-bean="initFactory" factory-method="getAccountService"></bean>
工厂类
package com.shsxt.factory;
import com.shsxt.service.AccountService;
import com.shsxt.service.PersonService;
/**
* Created by xlf on 2019/7/4.
*/
public class InitFactory {
public AccountService getAccountService(){
return new AccountService();
}
}
三种方式的比较
方式一:通过 bean 的缺省构造函数创建,当各个 bean 的业务逻辑相互比较独立的时 候或者和外界关联较少的时候可以使用。
方式二:利用静态 factory 方法创建,可以统一管理各个 bean 的创建,如各个 bean 在 创建之前需要相同的初始化处理,则可用这个 factory 方法进行统一的处理等等。
方式三:利用实例化 factory 方法创建,即将 factory 方法也作为了业务 bean 来控制, 1 可用于集成其他框架的 bean 创建管理方法,2 能够使 bean 和 factory 的角色互换。
开发中项目一般使用一种方式实例化 bean,项目开发基本采用第一种方式,交 给 spring 托管,使用时直接拿来使用即可。
七、依赖注入
依赖注入:控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
注入方式
1、set注入(最常用的方式)
spring.xml配置
<!-- 1. set注入; 条件必须存在setter方法 -->
<bean id="userService" class="com.shsxt.service.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
java类
package com.shsxt.service;
import com.shsxt.dao.UserDao;
/**
* Created by xlf on 2019/7/4.
*/
public class UserService {
// spring的依赖注入
private UserDao userDao;
public void test(){
userDao.addUser();
}
// 必须存在
public void setUserDao(UserDao userDao) {
System.out.println("setUserDao...");
this.userDao = userDao;
}
}
基本数据类型set注入
<bean id="userServiceImpl" class="com.shsxt.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
<property name="userName" value="sxt"> </property>
<property name="price" value="123"> </property>
</bean> 同时对应 Service 提供对应属性字段 以及 get 、set 方法即可
2、构造器注入
xml配置
<!-- 2. 构造注入 -->
<bean id="accountService" class="com.shsxt.service.AccountService">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
java类
package com.shsxt.service;
import com.shsxt.dao.UserDao;
/**
* Created by xlf on 2019/7/4.
*/
public class AccountService {
// spring的依赖注入
private UserDao userDao;
public AccountService(UserDao userDao) {
this.userDao = userDao;
}
public void test(){
userDao.addUser();
}
}
3、静态工厂注入
xml配置
<!-- 3. 静态工厂注入 -->
<bean id="userDao" class="com.shsxt.factory.StaticFactory" factory-method="createUserDao"></bean>
<bean id="userService" class="com.shsxt.service.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
静态工厂类同上
4、实例化工厂注入
xml配置
<!-- 4. 实例工厂注入 -->
<bean id="instanceFactory" class="com.shsxt.factory.InstanceFactory"></bean>
<bean id="userDao" factory-bean="instanceFactory" factory-method="createUserDao"></bean>
<bean id="userService" class="com.shsxt.service.UserService">
<property name="userDao" ref="userDao"></property>
</bean>
工厂类同上
5、p标签和c标签
<!-- p标签 -->
<bean id="userService" class="com.shsxt.service.UserService"
p:userDao-ref="userDao">
</bean>
<!-- c标签 -->
<bean id="accountService" class="com.shsxt.service.AccountService"
c:_0-ref="userDao">
<!--c:userDao-ref="userDao">-->
</bean>
<bean id="accountDao1" class="com.shsxt.dao.AccountDao"
p:name="cuihua"
p:age="18">
</bean>
<bean id="accountDao2" class="com.shsxt.dao.AccountDao"
c:name="zhangsan"
c:age="17">
</bean>
<bean id="accountDao3" class="com.shsxt.dao.AccountDao"
c:_0="lisi"
c:_1="16">
</bean>
6、集合注入
xml配置
<!-- 集合注入 -->
<bean id="dataService" class="com.shsxt.service.DataService">
<property name="list">
<list>
<value>河南烩面</value>
<value>南方臊子面</value>
<value>油泼面</value>
<value>方便面</value>
</list>
</property>
<property name="set">
<set>
<value>快乐小馒头</value>
<value>北方馒头</value>
<value>天津麻花</value>
<value>新疆大饼</value>
</set>
</property>
<property name="map">
<map>
<entry>
<key><value>河南</value></key>
<value>云台山风景</value>
</entry>
<entry>
<key><value>上海</value></key>
<value>宝塔</value>
</entry>
<entry>
<key><value>北京</value></key>
<value>紫禁城</value>
</entry>
</map>
</property>
<property name="prop">
<props>
<prop key="北京">北京尚学堂</prop>
<prop key="上海">上海尚学堂</prop>
<prop key="西安">西安尚学堂</prop>
</props>
</property>
</bean>
java类
package com.shsxt.service;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Created by xlf on 2019/7/4.
*/
public class DataService {
private List list;
private Set set;
private Map map;
private Properties prop;
public void setList(List list) {
this.list = list;
}
public List getList() {
return list;
}
public void setSet(Set set) {
this.set = set;
}
public Set getSet() {
return set;
}
public void setMap(Map map) {
this.map = map;
}
public Map getMap() {
return map;
}
public void setProp(Properties prop) {
this.prop = prop;
}
public Properties getProp() {
return prop;
}
@Override
public String toString() {
return "DataService{" +
"list=" + list +
", set=" + set +
", map=" + map +
", prop=" + prop +
'}';
}
}
八、注解方式注入bean
- 加入 spring-aop jar包spring-aop-4.3.2.RELEASE.jar
- Xml 配置: 加入 context 命名空间 和 xsd地址
- 添加 配置
<?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
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:annotation-config/>
</beans>
@Autowired
由spring框架提供
默认通过bean的类型进行匹配
@Resource
由jdk提供
默认通过名字匹配
如果不指定名字,名字为空,则通过类型匹配
tips:推荐使用@Resource注解,属于jdk,减少了与spring的耦合度
九、Spring Ioc 自动扫描管理bean
xml配置
<context:component-scan base-package="com.shsxt"/>
不需要再配置
<context:annotation-config/>
同时对于被 spring管理的bean类的定义上需要加入对应的注解定义
开发中的习惯
Dao 层:@Repository
Service 层:@Service
视图控制层:@Controller
如果对于开发的类实在不明确到底属于哪个层,可以使用@Component 注解定义。
十、bean的作用域问题
1、singleton作用域
lazy-init 是懒加载, 如果等于 true 时作用是指 spring 容器启动的时候不 会去实例化这个 bean, 而是在程序调用时才去实例化. 默认是 false 即 spring 容器启 动时实例化.
方式一:通过配置
<bean id="userDao" class="com.shsxt.dao.UserDao" lazy-init="true"></bean>
方式二:通过注解@lazy
容器在启动的情况下就实例化所有 singleton 的 bean 对象,并缓存与容器中单例的好处:
1)提前发现潜在的配置问题
2)Bean 对象存在于缓存中,使用时不用再去实例化 bean,加快程序运行效率
什么类型的对象适合作为单例对象来使用呢? service,dao,action 等无状态对象,保证线程的安全性。
一般来说对于无状态或状态不可改变的 对象适合使用单例模式(什么是无状态或状态 不可改变)
实际上对象状态的变化往往均是由于属性值得变化而引起的,比如 user 类 年龄 属性会有变化 属性年龄的变化一般会引起 user 对象状态的变化。对于我们的程序来 说,无状态对象 没有实例变量的存在,保证了线程的安全性,service 层业务对象即是 无状态对象。线程安全的。
prototype作用域
配置文件中通过scope=” prototype”
设置 bean 的类型 ,每次向 Spring 容器请求获取 Bean 都返回一个全新的 Bean,相对于“singleton”来说就是不缓存 Bean,每次都是 一个根据 Bean 定义创建的全新 Bean。(不是单例)
例如:
<bean id="userDao" class="com.shsxt.dao.UserDao" scope="prototype"></bean>
Web应用中的作用域
request 作用域:表示每个请求需要容器创建一个全新 Bean。比如提交表单 的数据必须是对每次请求新建一个 Bean 来保持这些表单数据,请求结束释放这些数据。
session 作用域:表示每个会话需要容器创建一个全新 Bean。比如对于每个用 户一般会有一个会话,该用户的用户信息需要存储到会话中,此时可以将该 Bean 作用 域配置为 session 级别。
globalSession:类似于 session 作用域,其用于 portlet(Portlet 是基于 Java 的 Web 组件,由 Portlet 容器管理,并由容器处理请求,生产动态内容)环境的 web 应 用。如果在非 portlet 环境将视为 session 作用域。
十一、Bean的生命周期
在 Spring 中,Bean 的生命周期包括 Bean 的定义、初始化、使用和销毁 4 个阶段
1、初始化
方式一:
配置文件
<bean id="userDao" class="com.shsxt.dao.UserDao" init-method="init"></bean>
这里init为UserDao里定义的监听方法
方式二:
实现 org.springframework.beans.factory.InitializingBean 接口
package com.shsxt.dao;
import org.springframework.beans.factory.InitializingBean;
/**
* Created by Administrator on 2019/7/5.
*/
public class UserDao implements InitializingBean{
public UserDao() {
//System.out.println("init...");
}
public void init(){
System.out.println("userDao init...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("userDao init...22222");
}
}
2、使用
3、销毁
配置
<bean id="userDao" class="com.shsxt.dao.UserDao" destroy-method="destroy"></bean>
destroy为bean中任意监听方法
测试代码
@Test
public void test01(){
AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
context.close();
}