只是不愿随波逐流 ...|

lidongdongdong~

园龄:2年7个月粉丝:14关注:8

1、IOC

1、引言

1.1、EJB 存在的问题

image

1.2、什么是 Spring

Spring 是⼀个轻量级的 JavaEE 解决⽅案,整合众多优秀的设计模式

轻量级

1. 对于运行环境是没有额外要求的
开源 tomcat、resion、jetty
收费 weblogic、websphere
2. 代码移植性高
不需要实现额外接口

JavaEE 的解决⽅案

image

整合设计模式

1. 工厂
2. 代理
3. 模板
4. 策略

1.3、工厂设计模式

# beans.properties
categoryService=com.zzw.service.impl.CategoryServiceImpl
routeService=com.zzw.service.impl.RouteServiceImpl
// 懒加载
public class BeanFactory {
private BeanFactory() {
}
private static final ResourceBundle resourceBundle;
private static final Map<String, Object> map = new HashMap<>();
static {
resourceBundle = ResourceBundle.getBundle("beans");
}
public static Object getBean(String beanId) {
if (map.containsKey(beanId)) return map.get(beanId);
try {
String className = resourceBundle.getString(beanId);
Class clazz = Class.forName(className);
Object instance = clazz.newInstance();
map.put(beanId, instance);
return instance;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("获取 bean 对象失败, beanId: " + beanId);
}
}
}
// 积极加载 ResourceBundle(资源包)
public class BeanFactory {
private BeanFactory() {
}
private static final Map<String, Object> beanMap = new HashMap<>();
static {
try {
ResourceBundle bundle = ResourceBundle.getBundle("beans");
Enumeration<String> keys = bundle.getKeys();
while (keys.hasMoreElements()) {
String beanId = keys.nextElement();
String className = bundle.getString(beanId);
Class<?> clazz = Class.forName(className);
Object instance = clazz.newInstance();
beanMap.put(beanId, instance);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Object getBean(String beanId) {
if (beanMap.containsKey(beanId)) return beanMap.get(beanId);
throw new RuntimeException("获取 bean 对象失败, beanId: " + beanId);
}
}

2、第一个 Spring 程序

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>

2.1、ApplicationContext 接口类型

* 接⼝: 屏蔽实现的差异
* ⾮ web 环境: ClassPathXmlApplicationContext(main、junit)
* 是 web 环境: XmlWebApplicationContext
* ApplicationContext 工厂的对象占⽤⼤量内存
* 不会频繁的创建对象: ⼀个应用只会创建⼀个工厂对象
* ApplicationContext 工厂: ⼀定是线程安全的(多线程并发访问)

2.2、程序开发

1. 创建类
2. 配置文件 applicationContext.xml
<bean id="person" class="com.zzw.basic.Person"/>
3. 通过工厂类, 获得对象
`ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");`
`Person person = (Person) context.getBean("person");`

2.3、细节分析

Spring 工厂创建的对象,叫做 bean 或者组件(componet)

// 通过这种方式获得对象, 就不需要强制类型转换
Person person = ctx.getBean("person", Person.class);
System.out.println("person = " + person);
// 当前 Spring 的配置文件中, 只能有⼀个 <bean class=""> 是 Person 类型
Person person = ctx.getBean(Person.class);
System.out.println("person = " + person);
// 获取的是 Spring 工厂配置文件中所有 bean 标签的 id 值 person person1
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
// 根据类型获得 Spring 配置文件中对应的 id 值
String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
for (String id : beanNamesForType) {
System.out.println("id = " + id);
}
// ⽤于判断是否存在指定 id 值的 bean, 不能判断 name 值
if (ctx.containsBeanDefinition("person")) {
System.out.println("true");
} else {
System.out.println("false");
}
// ⽤于判断是否存在指定 id 值的 bean, 能判断 name 值
if (ctx.containsBean("person")) {
System.out.println("true");
} else {
System.out.println("false");
}
1. 只配置 class 属性
<bean class="com.baizhiedu.basic.Person"/>
(a) 上述这种配置, 有默认 id 值: com.baizhiedu.basic.Person#0
(b) 应⽤场景: 如果这个 bean 只需要使用一次, 那么就可以省略 id 值
(c) 如果这个 bean 会使用多次, 或者被其他 bean 引⽤则需要设置 id 值
2. name 属性
作用: 用于在 Spring 的配置文件中, 为 bean 对象定义别名(⼩名)
相同: ctx.getBean("id|name") -> object
<bean id="" class=""/>
<bean name="" class=""/>
区别: 别名可以定义多个, 但是 id 属性只能有⼀个值

2.4、Spring 工厂的底层实现原理

Spring 工厂是可以调⽤对象私有的构造方法创建对象
image

2.5、思考

未来在开发过程中,是不是所有的对象,都会交给 Spring 工厂来创建呢
理论上是的,但是有特例 :实体对象(entity)是不会交给 Spring 创建,它是由持久层框架进行创建

3、Spring 5.x 与日志框架的整合

* 默认
Spring 1.2.3 早期都是于 commons-logging.jar
Spring 5.x 默认整合的日志框架 logback log4j2
* Spring5.x 整合 log4j
1. 引⼊ log4j jar 包
2. 引⼊ log4.properties 配置文件
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
# resources 文件夹根目录下 log4j.properties
log4j.rootLogger = debug, console
# 日志输出到控制台显示
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-ddHH:mm:ss} %-5p %c{1}:%L - %m%n

4、注入(DI)

4.1、什么是注入

通过 Spring 工厂及配置文件,为所创建对象的成员变量赋值

4.2、为什么需要注入

image

4.3、注入总结

image

5、控制反转、依赖注入

5.1、控制反转

* 控制: 对于成员变量赋值的控制权
* 反转: 从代码中反转到 Spring 工厂和配置文件中完成
* 好处: 解耦合
* 底层实现: 工厂设计模式

image

5.2、依赖注入

* 依赖: 当⼀个类需要另⼀个类时, 就意味着依赖, ⼀旦出现依赖, 就可以把另⼀个类作为本类的成员变量, 最终通过 Spring 配置文件进⾏注入(赋值)
* 注⼊: 通过 Spring 的工厂及配置文件, 为对象(bean、组件)的成员变量赋值
* 好处: 解耦合

image

6、Spring 工厂创建复杂对象

image

6.1、FactoryBean 接口

image

<bean id="conn" class="com.baizhiedu.factorybean.ConnectionFactoryBean"/>
* 如果 Class 中指定的类型是 FactoryBean 接⼝的实现类, 那么通过 id 值获得的是这个类所创建的复杂对象 Connection
* 如果就想获得 FactoryBean 类型的对象, ctx.getBean("&conn") 获得就是 ConnectionFactoryBean 对象
* isSingleton 方法
返回 true 只会创建⼀个复杂对象
返回 false 每⼀次都会创建新的对象
* 根据这个对象的特点, 决定是返回 true(SqlSessionFactory), 还是 false(Connection)

image

* 接口回调
1. 为什么 Spring 规定 FactoryBean 接口实现 getObject()?
2. ctx.getBean("conn") 获得是复杂对象 Connection, 而没有获得 ConnectionFactoryBean(&)
* Spring 内部运⾏流程
1. 通过 conn 获得 ConnectionFactoryBean 类的对象, 进而通过 instanceof 判断出是 FactoryBean 接口的实现类
2. Spring 按照规定 getObject() ---> Connection
3. 返回 Connection

6.2、实例工厂

<bean id="connFactory" class="com.baizhiedu.factorybean.ConnectionFactory" />
<bean id="conn" factory-bean="connFactory" factory-method="getConnection"/>

6.3、静态工厂

<bean id="conn" class="com.baizhiedu.factorybean.StaticConnectionFactory" factory-method="getConnection"/>

6.4、总结

image

7、控制 Spring 工厂创建对象的次数

7.1、简单对象

<bean id="account" scope="singleton|prototype" class="xxxx.Account"/>
<!--sigleton : 只会创建⼀次简单对象, 默认值-->
<!--prototype: 每⼀次都会创建新的对象-->

7.2、复杂对象

FactoryBean {
isSingleton() {
return true; // 只会创建⼀次
return false; // 每⼀次都会创建新的
}
}
// 如没有 isSingleton ⽅法, 还是通过 scope 属性, 进⾏对象创建次数的控制

7.3、为什么要控制对象的创建次数

好处:节省不必要的内存浪费
什么样的对象只创建⼀次

  • SqlSessionFactory
  • Service
  • DAO

什么样的对象每⼀次都要创建新的

  • Connection
  • SqlSession、Session
  • Struts2 Action

8、对象的生命周期

1、构造方法
初始化之前 postProcessBeforeInitialization(BeanPostProcessor )
2、初始化 afterProperitesSet(InitializingBean)、init-method
初始化之后 postProcessAfterInitialization(BeanPostProcessor )
3、销毁 destroy(DisposableBean)、destroy-method
image

image

8.1、创建阶段

* `scope="singleton"`
* Spring 工厂创建的同时, 创建对象
* 注意: 设置 <bean lazy-init="true"/> 的情况下, 会在获取对象的同时才创建对象
* `scope="prototype"`
* Spring 工厂会在获取对象的同时才创建对象 ctx.getBean("")

8.2、初始化阶段

* 方式一: 实现 `InitializingBean` 接口, 完成 `afterProperitesSet()`
* 方式二: <bean id="product" class="xxx.Product" init-method="myInit"/>

8.3、销毁阶段

* 销毁方法适用于 `scope="singleton"`
* 方式一: 实现 `DisposableBean` 接口, 完成 `destroy()`
* 方式二: <bean id="product" class="xxx.Product" destroy-method="myDestroy"/>

9、配置文件参数化

<context:property-placeholder location="classpath:db.properties"/>

image

10、自定义类型转换器

10.1、类型转换器

Spring 通过类型转换器把配置文件中字符串类型的数据,转换成了对象中成员变量对应类型的数据,进而完成了注入
image

10.2、自定义类型转换器

内置的日期类型转换器:2020/05/01(不⽀持 2020-05-01)

// 实现 Converter<String, target> 接口
public class MyDateConverter implements Converter<String, Date> {
private String pattern;
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
@Override
public Date convert(String source) {
Date date = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
date = sdf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
<bean id="myDateConverter" class="xxxx.MyDateConverter">
<property name="pattern" value="yyyy-MM-dd"/>
</bean>
<!--用于注册类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="myDateConverter"/>
</set>
</property>
</bean>

11、后置处理 Bean

BeanPostProcessor 作用:对 Spring 工厂所创建的所有对象,进行再加工,多个 BeanPostProcessor 实现类(implements Ordered 控制顺序)
image

public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Categroy) {
Categroy categroy = (Categroy) bean;
categroy.setName("xiaowb");
}
return bean;
}
}
<bean id="myBeanPostProcessor" class="xxx.MyBeanPostProcessor"/>
posted @   lidongdongdong~  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开