1、引言
1.1、EJB 存在的问题

1.2、什么是 Spring
Spring 是⼀个轻量级的 JavaEE 解决⽅案,整合众多优秀的设计模式
轻量级
| 1. 对于运行环境是没有额外要求的 |
| 开源 tomcat、resion、jetty |
| 收费 weblogic、websphere |
| 2. 代码移植性高 |
| 不需要实现额外接口 |
JavaEE 的解决⽅案

整合设计模式
1.3、工厂设计模式
| |
| 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); |
| } |
| } |
| } |
| |
| 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); |
| |
| |
| |
| Person person = ctx.getBean(Person.class); |
| System.out.println("person = " + person); |
| |
| |
| |
| String[] beanDefinitionNames = ctx.getBeanDefinitionNames(); |
| for (String beanDefinitionName : beanDefinitionNames) { |
| System.out.println("beanDefinitionName = " + beanDefinitionName); |
| } |
| |
| |
| |
| String[] beanNamesForType = ctx.getBeanNamesForType(Person.class); |
| for (String id : beanNamesForType) { |
| System.out.println("id = " + id); |
| } |
| |
| |
| |
| if (ctx.containsBeanDefinition("person")) { |
| System.out.println("true"); |
| } else { |
| System.out.println("false"); |
| } |
| |
| |
| |
| 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 工厂是可以调⽤对象私有的构造方法创建对象

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> |
| |
| 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、为什么需要注入

4.3、注入总结

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

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

6、Spring 工厂创建复杂对象

6.1、FactoryBean 接口

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

| * 接口回调 |
| 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、总结

7、控制 Spring 工厂创建对象的次数
7.1、简单对象
| <bean id="account" scope="singleton|prototype" class="xxxx.Account"/> |
| |
| |
7.2、复杂对象
| FactoryBean { |
| isSingleton() { |
| return true; |
| return false; |
| } |
| } |
| |
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


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"/> |

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

10.2、自定义类型转换器
内置的日期类型转换器:2020/05/01(不⽀持 2020-05-01)
| |
| 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 控制顺序)

| 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"/> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步