Spring 学习(1)
- 分层架构
- web 层 ---- Spring MVC | struts
- service 层 ---- Spring
- dao 层 ---- Mybatis | JDBCTemplate | hibernate —> Spring-date
Spring 核心
- Spring 核心是控制反转(Ioc) 和面向切片(Aop)
- Ioc
- Aop
Spring 优点
- 方便解耦,简化开发(高内聚、低耦合)
Spring 就是大工厂(容器),用于生成Bean
-
Aop 编程的支持
-
声明式事务的支持
-
方便程序的测试
-
方便集成各种优秀的框架
-
降低JavaEE Api 的使用难度
Spring 体系结构
- 核心容器: beans、core、context、expression
Spring开发:
入门案例:Ioc(控制反转)
- 导入jar 包和依赖
- jar 包
spring-expression-4.3.18.RELEASE.jar
spring-context-4.3.18.RELEASE.jar
spring-beans-4.3.18.RELEASE.jar
spring-core-4.3.18.RELEASE.jar
- 依赖
commons-logging-1.2.jar
目标类
- 提供UserService 接口和实现类
- 获取UserService 实现类的实例
- 使用Spring 创建实例对象(Ioc 控制反转)
- UserService 接口
package com.spring1_Ioc;
/**
* @InterfaceName Userservice
* @Author 秃头的JJ
* Date 2019/5/21 0021 20:20
*/
public interface Userservice {
void springTest();
}
- UserServiceImpl 实现类
package com.spring1_Ioc.impl;
import com.spring1_Ioc.Userservice;
/**
* @ClassName UserServiceImpl
* @Author 秃头的JJ
* Date 2019/5/21 0021 20:21
* Version 1.0
*/
public class UserServiceImpl implements Userservice {
@Override
public void springTest() {
System.out.println("Spring Hello");
}
}
配置文件
- 配置文件位置任意,在开发中一般在classPath(src)下
- 配置文件名称任意,开发中常用applicationContext.xml
- 内容:添加schema 约束
- 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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置bean
bean:是需要创建的对象
id:实例的id
class:实例的位置
-->
<bean id="userServiceId" class="com.spring1_Ioc.impl.UserServiceImpl" >
</bean>
</beans>
测试方法
@Test
public void springTest(){
/* 从Spring 容器获取 */
// 加载容器
String xmlPath = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
// 从Spring 容器内获取实例
Userservice us = (Userservice) ac.getBean("userServiceId");
us.springTest();
}
入门案例:DI(依赖注入) — 使用 子标签
- is a:是一个 — 继承
- has a:有一个 — 成员变量(依赖)
- 依赖:一个对象需要使用另一个对象
- 注入:提供setter 方法进行另一个对象实例的设置
目标类
- 创建两个接口和实现类
- 配置到xml 文件内
- Dao 接口
package com.spring2_DI;
/**
* @InterfaceName Dao
* @Author 秃头的JJ
* Date 2019/5/21 0021 21:39
*/
public interface Dao {
void daoTest();
}
- Dao 实现类
package com.spring2_DI.impl;
import com.spring2_DI.Dao;
/**
* @ClassName DaoImpl
* @Author 秃头的JJ
* Date 2019/5/21 0021 21:39
* Version 1.0
*/
public class DaoImpl implements Dao {
public DaoImpl() {
System.out.println("Dao Test DaoImpl");
}
@Override
public void daoTest() {
System.out.println("Dao Test");
}
}
- UserService_DI 接口
package com.spring2_DI;
/**
* @InterfaceName UserService_DI
* @Author 秃头的JJ
* Date 2019/5/21 0021 21:37
*/
public interface UserService_DI {
void DiTest();
}
- UserService_DI 实现类
package com.spring2_DI.impl;
import com.spring2_DI.Dao;
import com.spring2_DI.UserService_DI;
/**
* @ClassName UserService_DIImpl
* @Author 秃头的JJ
* Date 2019/5/21 0021 21:38
* Version 1.0
*/
public class UserService_DIImpl implements UserService_DI {
private Dao dao;
private String str;
public void setDao(Dao dao) {
this.dao = dao;
}
public void setStr(String str) {
this.str = str;
}
@Override
public void DiTest() {
System.out.println(this.str);
}
}
配置文件
property: 依赖注入所使用的标签
name: 所要输入的属性名称 ===== 要与实例化的类内的属性名称保持一致
ref: 指代需要传入的实例化对象的bean 的id
value: 注入到属性的具体值
-->
<bean id="UserService_DIImpl" class="com.spring2_DI.impl.UserService_DIImpl">
<property name="dao" ref="daoID"/>
<property name="str" value="HelloWorld This Is A String" />
</bean>
<bean id="daoID" class="com.spring2_DI.impl.DaoImpl"></bean>
测试方法
package com.spring2_DI.test;
import com.spring2_DI.impl.UserService_DIImpl;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @ClassName MainTest
* @Author 秃头的JJ
* Date 2019/5/21 0021 20:31
* Version 1.0
*/
public class MainTest {
private static ApplicationContext ac;
@BeforeAll
public static void init(){
// 加载容器
String xmlPath = "applicationContext.xml";
ac = new ClassPathXmlApplicationContext(xmlPath);
}
@Test
public void springTest(){
/* 从Spring 容器获取 */
// 从Spring 容器内获取实例
UserService_DIImpl us = (UserService_DIImpl) ac.getBean("UserService_DIImpl");
us.DiTest();
}
}
核心API
- BeanFactory
用于生成任意Bean
- ApplicationContext
- 采用延时加载,在第一次调用getBean() 时才会初始化Bean
是BeanFactory 的子接口,功能更加强大(国际化处理、事件传递、Bean 自动装配、各种不同应用层的Context 实现)
- ClassPathXmlApplicationContext 用于记载classPath(src) 下的指定的XML 文件
- FileSystemXmlApplicationContext 用于记载指定盘符下的指定XML 文件
基于XMl 的装配Bean
Bean 的实例化方式
- 三种Bean 的实例化方式:默认构造、静态工厂、实例工厂
- 默认构造
<bean id="" class="">
必须提供默认构造
- 静态工厂
- 常用于Spring 整合其他框架(工具)
- 用于生产实例,所有方法必须是static
<bean id="" class="工厂的路径" factory-method="静态方法">
- 实例代码
- applicationContext.xml
<!-- 配置静态工厂
id: 静态工厂的id
class:静态工厂的全路径类名
factory-method:静态工厂内的静态方法(创建类的方法)
由Spring 代理静态工厂,并调用创建类的方法,实现和FactoryName.FatoryMethodName() 一样的功能(输出被创建类的对象)
-->
<bean id="staticFactoryTest"
class="com.spring3_static_factory.Factory"
factory-method="createDao"></bean>
- 自定义工厂类
package com.spring3_static_factory;
/**
* @ClassName User
* @Author 秃头的JJ
* Date 2019/5/22 0022 10:14
* Version 1.0
*/
public class Factory {
private static final Dao dao = new Dao();
public static Dao createDao(){
return dao;
}
}
- 工厂实现类
package com.spring3_static_factory;
/**
* @ClassName Dao
* @Author 秃头的JJ
* Date 2019/5/22 0022 10:14
* Version 1.0
*/
public class Dao {
public void print(){
System.out.println("Hello Static Fatory");
}
}
- 测试方法
@Test
public void staticFactoryTest(){
String xmlPath = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
Dao dao = ac.getBean("staticFactoryTest", Dao.class);
dao.print();
}
- 实例工厂
- 必须先有工厂实例对象,通过实例对象创建对象。提供的所有方法都是非静态的
- applicationContext.xml
<!--
实例工厂的使用:由Spring 创建工厂Bean 和待实现类的Bean
factory-bean: 指代工厂类Bean 的id
factory-method: 指代工厂类内创建实现类对象的方法
-->
<!-- 创建实例工厂的Bean -->
<bean id="factory" class="com.spring4_factory.Factory"></bean>
<!-- 使用实例工厂的Bean 创建实现对象 -->
<bean id="dao" factory-bean="factory" factory-method="createDao"></bean>
- Factory 工厂类
package com.spring4_factory;
/**
* @ClassName Factory
* @Author 秃头的JJ
* Date 2019/5/22 0022 10:55
* Version 1.0
*/
public class Factory {
private final Dao dao = new Dao();
public Dao createDao(){
return dao;
}
}
- 待实现类
package com.spring4_factory;
/**
* @ClassName Dao
* @Author 秃头的JJ
* Date 2019/5/22 0022 10:55
* Version 1.0
*/
public class Dao {
public void print(){
System.out.println("Hello Factory");
}
}
- 测试方法
@Test
public void factoryTest(){
String xmlPath = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
Dao dao = ac.getBean("dao", Dao.class);
dao.print();
}
Bean 的种类
普通Bean
<bean id="" class="">
Spring 会直接创建class 对应类型的实例,并返回
FactoryBean
- 是特使的Bean ,具有工厂生产对象的能力,但是只能生产特点的对象.
Bean 必须实现FactoryBean 接口, 该接口提供了getObject() 方法获取特点的Bean
<bean id="" class="FactoryBean">
先创建FactoryBean 实例,在调用getObject() 方法,并返回方法的返回值
- 类比于
FactoryBean fb = new FactoryBean();
return fb.getObject();
注:
- BeanFactory 和FactoryBean 的区别
- BeanFactory : 工厂,生产任意Bean
- FactoryBean : 是一个特殊Bean, 生产一个特定的Bean(过去代理对象的实例 AOP是使用)
作用域
- 作用域:用于确定Spring 所创建的Bean 的实例的个数
类别 | 说明 |
---|---|
singleton*(单例,默认的) | 在Spring Ioc 容器内中仅存在一个Bean 实例,Bean 以单例方式存在,默认值 |
prototype*(多例) | 每次从容器中调用Bean 时,都会返回一个新的实例 |
request | 每次HTTP 请求都会创建一个新的Bean ,该作用域仅适用于WebApplicationContext 环境 |
session | 同一个HTTP session 共享一个Bean, 不同Session 使用不同的Bean ,仅适用于WebApplicationContext 环境 |
globalSession | 一般使用在Portlet 应用环境,仅适用于WebApplicationContext 环境 |
- 配置信息
<bean id="" class="" scope="">
- applicationContext.xml
<!--
作用域:默认使用singleton (单例)
singleton: 多例,每请求一次,创建一个新的实例
-->
<bean id="scopTest" class="com.spring5_scope.ScopTest" scope="prototype" />
- ScopTest 测试类
package com.spring5_scope;
/**
* @ClassName ScopTest
* @Author 秃头的JJ
* Date 2019/5/22 0022 16:03
* Version 1.0
*/
public class ScopTest {
public void print(){
System.out.println(this.hashCode());
}
}
- 测试方法
@Test
public void factoryTest(){
String xmlPath = "com/spring5_scope/applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
ScopTest st = ac.getBean("scopTest", ScopTest.class);
ScopTest st1 = ac.getBean("scopTest", ScopTest.class);
System.out.println(st);
System.out.println(st1);
}
生命周期
- Spring 生命周期(11个步骤)
- instantiate Bean 对象实例化
- populate properties 封装属性
- 如果Bean 实现BeanNameAware 执行setBeanName
- 如果Bean 实现BeanfactoryAware 或者 ApplicationContextAware 设置工厂setBeanFactory 或者上下文对象setApplicationContext
- 如果存在类实现BeanPostProcessor(后处理Bean), 执行postProcessBeforeInitialization
- 如果Bean 实现执行Initialization 执行afterPropertiesSet
- 调用
<bean init-method="初始化方法">
指定初始化方法- 如果存在类实现BeanPostProcessor(处理Bean), 执行执行postProcessAfterInitialization
- 执行业务处理
- 如果Bean 实现DisposableBean 执行destory(销毁)
- 调用
<bean destory-method="“销毁方法">
指定销毁的方法
初始化和销毁
- 在目标方法执行前和执行后,进行初始化和销毁
<bean id="" class="" init-method="初始化方法" destory-method="销毁方法">
- 销毁方法执行条件:1、必须调用Close 方法;2、必须时单例
- applicationContext.xml
<!--
定义初始化和销毁方法
init-method: 指定初始化方法(初始化方法可以为私有)
destroy-method: 指定销毁方法(销毁方法可以为私有)
销毁方法执行条件:1、必须调用Close 方法;2、必须时单例
-->
<bean id="lifecycle" class="com.spring6_lifecycle.LifeCycleBean"
init-method="init" destroy-method="destory" />
- LifeCycleBean 测试Bean
package com.spring6_lifecycle;
/**
* @ClassName LifeCycleBean
* @Author 秃头的JJ
* Date 2019/5/22 0022 16:45
* Version 1.0
*/
public class LifeCycleBean {
private void init(){
System.out.println("初始化方法");
}
public void print(){
System.out.println("Hello Life Cycle Bean");
}
private void destory(){
System.out.println("销毁");
}
}
- 测试方法
@Test
public void lifeCycleTest() throws Exception {
String xmlPath = "com/spring6_lifecycle/applicationContext.xml";
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(xmlPath);
LifeCycleBean lcb = ac.getBean("lifecycle", LifeCycleBean.class);
lcb.print();
/* 使用反射调用 close 方法 */
// ac.getClass().getMethod("close").invoke(ac);
ac.close();
}
BeanPostProcessor 后处理Bean
- Spring 提供的一种机制,需要实现此接口BeanPostProcessor,并将实现类提供给Spring
容器, Spring 容器自动执行,在初始化方法执行前执行befor(), 在初始化方法执行之后执行
after() - Spring 提供工厂钩子,用于修改实例对象,可以生产代理对象,时AOP 的底层
- applicationContext.xml
<!--
配置测试Bean
-->
<bean id="testBean" class="com.spring7_BeanPostProcessor.pojo.TestBean"
init-method="init" destroy-method="destory"></bean>
<!--
将BeanPostProcessor 实现类提供个Spring 进行管理
其逻辑类比于:
A a = new A();
// 在这里调用BeanPostProcessor 实现类的before 方法
a = B.before(a);
a.init();
// 在这里调用BeanPostProcessor 实现类的after 方法
a = B.after(a);
// 调用a 的方法
a.print();
a.destory();
-->
<bean class="com.spring7_BeanPostProcessor.pojo.BeanPostProcessorTest"></bean>
- BeanPostProcessor 实现类
package com.spring7_BeanPostProcessor.pojo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @ClassName BeanPostPrecessorTest
* @Author 秃头的JJ
* Date 2019/5/22 0022 19:13
* Version 1.0
*/
public class BeanPostProcessorTest implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization 执行了");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization 执行了");
return bean;
}
}
- TestBean 测试类
package com.spring7_BeanPostProcessor.pojo;
/**
* @ClassName TestBean
* @Author 秃头的JJ
* Date 2019/5/22 0022 19:13
* Version 1.0
*/
public class TestBean {
private void init(){
System.out.println("初始化方法执行了");
}
public void print(){
System.out.println("BeanPostPrecessorTest Say HelloWorld");
}
private void destory(){
System.out.println("销毁方法执行了");
}
}
- 测试方法
private static ApplicationContext ac;
@BeforeAll
public static void init(){
String xmlPath = "com/spring7_BeanPostProcessor/applicationContext.xml";
ac = new ClassPathXmlApplicationContext(xmlPath);
}
@Test
public void beanPostProcessorTest(){
TestBean tb = ac.getBean("testBean", TestBean.class);
tb.print();
}
属性的依赖注入
属性依赖注入 — 构造方法(定义了有参构造器需要再定义无参构造器避免错误发生)
- applicationContext.xml 内Bean 方法配置
<!--
使用构造器注入值:使用<constructor-arg> 标签
index: 指代有参构造器参数列表的第几位参数(从o 开始)
value: 指代具体的参数值
ref: 传入另一个Bean
name: 指代具体的参数名称
-->
<bean id="user" class="com.spring9_DI_Contructor.pojo.User">
<constructor-arg name="password" value="12138"/>
<constructor-arg name="username" value="12138"/>
<constructor-arg name="userInfo" ref="info"/>
</bean>
<bean id="info" class="com.spring9_DI_Contructor.pojo.UserInfo">
<constructor-arg index="0" value="上官翠花"/>
<constructor-arg index="1" value="女"/>
<constructor-arg index="2" value="漕溪路"/>
<constructor-arg index="3" value="1111级20班"/>
</bean>
属性依赖注入 — setter 方法(待实例化类内必须含有setter 方法)
- applicationContext.xml
<!--
setter 方法实现参数注入:使用<property> 标签
name: 指代参数名称
value: 指代具体的要传入的值
<property name="" value="具体的值">
==等价于==>
<propery name="">
<value>具体的值</value>
</property>
ref: 指代需要传入的bean
bean: 具体的bean 实例
-->
<bean id="user" class="com.spring8_DI_setter.pojo.User">
<property name="username" value="12138"></property>
<property name="password">
<value>12138</value>
</property>
<property name="userInfo" ref="info"></property>
</bean>
<bean id="info" class="com.spring8_DI_setter.pojo.UserInfo">
<property name="sex" value="男"></property>
<property name="addr" value="春熙路"></property>
<property name="class_" value="2222级05班"></property>
<property name="name" value="欧阳铁柱"></property>
</bean>
属性依赖注入 — P命名空间
- 对setter 方法注入进行简化,替换了
而是使用<bean p:属性名=“”"“普通值" p:属性名=”“引用值”> - 使用前提:需要添加命名空间
- 在命名空间内加入:
xmlns:p="http://www.springframework.org/schema/p"
- 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"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
p:属性名=“"属性值"
作用与<property name="属性名" value="属性值"> 一样
但是在处理List 集合等数据不太友善
-->
<bean id="user" class="com.spring10_DI_Pnamespace.pojo.User"
p:username="12138"
p:password="12138p"
p:userInfo-ref="info">
<property name="strings">
<list>
<value>HelloWorld1</value>
<value>HelloWorld2</value>
<value>HelloWorld3</value>
<value>HelloWorld4</value>
<value>HelloWorld5</value>
<value>HelloWorld6</value>
</list>
</property>
</bean>
<bean id="info" class="com.spring10_DI_Pnamespace.pojo.UserInfo">
<constructor-arg index="0" value="上官翠花"/>
<constructor-arg index="1" value="女"/>
<constructor-arg index="2" value="漕溪路"/>
<constructor-arg index="3" value="1111级20班"/>
</bean>
属性依赖注入 — SpEL
- 对 进行同样编程,所有内容都使用value
<property name="" value="#{表达式}">
#{123}、#{‘String’} ----> 数字、字符串
#{beanID} ----> 引用另一个bean
#{beanID.propName} ----> 操作属性
#{beanID.toString()} ----> 执行方法
#{T(静态类).字段或者方法} ----> 静态方法或字段的使用
属性依赖注入 — 集合注入
- 数组
- List
- Set
- Map — 键值对 使用 子标签
- Properties (Props)
集合的注入都是使用 的子标签
- applicationContext.xml
<bean id="listTestId" class="com.spring12_DI_List.pojo.ListTest">
<property name="strs">
<!-- 注入数组值 -->
<array>
<value>欧阳铁柱</value>
<value>上官翠花</value>
<value>孙秃子</value>
<value>李狗蛋</value>
</array>
</property>
<property name="ints">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
<value>4</value>
</array>
</property>
<property name="stringList">
<!-- 注入List 集合值 -->
<list>
<value>欧阳铁柱</value>
<value>上官翠花</value>
<value>孙秃子</value>
<value>李狗蛋</value>
</list>
</property>
<property name="stringSet">
<!-- 注入Set 集合值 -->
<set>
<value>欧阳铁柱</value>
<value>上官翠花</value>
<value>孙秃子</value>
<value>李狗蛋</value>
</set>
</property>
<property name="map">
<!-- 注入Map 集合值 -->
<map>
<description>map Test</description>
<!-- 使用entry 绑定键值对 -->
<entry key="黄金缔造者" value="欧阳铁柱"></entry>
<entry key="白银缔造者" value="上官翠花"></entry>
<entry key="青铜缔造者" value="孙秃子"></entry>
<entry key="黑铁缔造者" value="李狗蛋"></entry>
</map>
</property>
<property name="props">
<!-- 注入Properties 值 -->
<props>
<prop key="黄金">欧阳铁柱</prop>
<prop key="白银">欧阳铁柱</prop>
<prop key="青铜">欧阳铁柱</prop>
<prop key="黑铁">欧阳铁柱</prop>
</props>
</property>
</bean>
装配Bean 基于annotation (注解)
- 注解: 实际上是一个Class ,使用 @注解名 来实现
- 注解的作用就是简化开发,替代XML 配置文件
- 使用注解的前提
IOC 控制反转 ---- 创建Bean
- @Component(“id”) 取代
- 注解使用时,要添加命名空间,让Spring 扫描含有注解的Class
- 在web 开发中,提供了3个 @Component 衍生注解(功能是一样的)
* @Repository --- Dao 层
* @Service --- service 层
* Controller --- 控制层(WEB 层)
DI 依赖注入 ---- 对Bean 内参数进行赋值
- 可以个私有字段设置值,也可以使用setter 方法进行设置
- 普通值: @Value(“值”)
- 引用值:
- 使用类型注入
- @Autowired
- 使用名称注入 (两种注入方式)
@Autowired @Qualifier("名称")
- @Resource(name = “名称”)
生命周期
- 初始化:@PostConstruct
- 销毁:@PreDestroy
作用域
- @Socpe(“prototype”) ---- 多例
XML 和注解配合使用
- 一般是将所有Bean 配置在XML 内
- 将所有依赖通过@Autowired 实现
注:
1、默认情况这样配置无效,需要在xml 内配置<context:annotation-config>
2、<context:annotation-config>
与<context:component-scan>
不同时出现