Spring系列工厂
第⼀章 引⾔
1. EJB存在的问题
2、什么是Spring
Spring是⼀个轻量级的JavaEE解决⽅案,整合众多优秀的设计模式
- 轻量级
1. 对于运⾏环境是没有额外要求的
开源 tomcat resion jetty
收费 weblogic websphere
2. 代码移植性⾼
不需要实现额外接⼝
-
JavaEE的解决方案
-
整合设计模式
1. ⼯⼚
2. 代理
3. 模板
4. 策略
3、设计模式
1. ⼴义概念
⾯向对象设计中,解决特定问题的经典代码
2. 狭义概念
GOF4⼈帮定义的23种设计模式:⼯⼚、适配器、装饰器、⻔⾯、代理、模板..
4、工厂设计模式
4.1、什么是工厂设计模式
1. 概念:通过⼯⼚类,创建对象
User user = new User();
UserDAO userDAO = new UserDAOImpl();
2. 好处:解耦合
耦合:指定是代码间的强关联关系,⼀⽅的改变会影响到另⼀⽅
问题:不利于代码维护
简单:把接⼝的实现类,硬编码在程序中
UserService userService = new UserServiceImpl();
4.2、简单⼯⼚的设计
工厂类
public class BeanFactory {
private static Properties env = new Properties();
static {
//1、获得IO输入流
InputStream is = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
//2、文件的内容封装到Properties中
try {
env.load(is);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static UserService getUserService() {
// return new UserServiceImpl();
Class clazz = null;
try {
clazz = Class.forName(env.getProperty("userService"));
UserService userService = (UserService) clazz.newInstance();
return userService;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static UserDAO getUserDao(){
Class clazz = null;
try {
clazz = Class.forName(env.getProperty("userDao"));
UserDAO userDAO = (UserDAO) clazz.newInstance();
return userDAO;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
配置文件
userService=com.xiaohe.basic.UserServiceImpl
userDao=com.xiaohe.basic.UserDAOImpl
4.3、通⽤⼯⼚的设计
简单工厂的缺点:冗余代码
通用工厂方法
public class BeanFactory {
private static Properties env = new Properties();
static {
//1、获得IO输入流
InputStream is = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
//2、文件的内容封装到Properties中
try {
env.load(is);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/**
* 通用工厂
* @param key
* @return
*/
public static <T> T getBean(String key, Class<T> tClass){
Object ret = null;
try {
Class clazz = Class.forName(env.getProperty(key));
ret = clazz.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return (T) ret;
}
}
4.4、 通⽤⼯⼚的使⽤⽅式
1. 定义类型 (类)
2. 通过配置⽂件的配置告知⼯⼚(applicationContext.properties)
key = value
3. 通过⼯⼚获得类的对象
Object ret = BeanFactory.getBean("key")
5.总结
Spring本质:⼯⼚ ApplicationContext (applicationContext.xml)
第二章 Spring第一个程序
1、环境搭建
-
Spring的Jar包
##设置pom pom 依赖 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.4.RELEASE</version> </dependency>
-
Spring配置文件
1. 配置⽂件的放置位置:任意位置 没有硬性要求
2. 配置⽂件的命名 :没有硬性要求 建议: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">
</beans>
2、Spring核心API
- ApplicationContext 接口
作⽤:Spring提供的ApplicationContext这个⼯⼚,⽤于对象的创建
好处:解耦合
- ApplicationContext接⼝类型
# 接⼝:屏蔽实现的差异
⾮web环境 : ClassPathXmlApplicationContext (main junit)
web环境 : XmlWebApplicationContext
- 重量级资源
ApplicationContext⼯⼚的对象占⽤⼤量内存。
不会频繁的创建对象 : ⼀个应⽤只会创建⼀个⼯⼚对象。
ApplicationContext⼯⼚:⼀定是线程安全的(多线程并发访问)
3、程序开发
1、创建类型
2、配置文件配置 applicationContext
3、通过工厂类获取对象
ApplicationContext
ClassPathXmlApplicationContext
1、创建类型
public class Person {
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
2、配置文件配置 applicationContext
<?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">
<!--生产Person对象-->
<bean id="person" class="com.xiaohe.spring01.Person"/>
</beans>
3、通过工厂类获取对象
@Test
public void test01() {
//1、获取Spring工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//2、根据工厂创建对象
Person person = ctx.getBean("person", Person.class);
System.out.println("person = " + person);
}
4、细节分析
- 名称解释
Spring⼯⼚创建的对象,叫做bean或者组件(componet)
- Spring⼯⼚的相关的⽅法
@Test
public void test02() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//1、通过这种⽅式获得对象,就不需要强制类型转换
Person person = ctx.getBean("person", Person.class);
//2、当前Spring的配置⽂件中 只能有⼀个<bean class是Person类型
Person person1 = ctx.getBean(Person.class);
//3、获取IOC容器中的所有Bean标签的Id值
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
//4、根据类型获得Spring配置⽂件中对应的id值
String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
for (String beanName : beanNamesForType) {
System.out.println("beanName = " + beanName);
}
//5、是否存在指定值的Bean
boolean person2 = ctx.containsBeanDefinition("person");
}
- 配置文件需要注意的细节
# 1、不写bean的id标签是否可以
可以的
<bean class="com.xiaohe.spring01.Person"/>
# bean的id为
beanDefinitionName = com.xiaohe.spring01.Person#0
应用场景:
如果这个bean只需要使⽤⼀次,那么就可以省略id值
如果这个bean会使⽤多次,或者被其他bean引⽤则需要设置id值
# 2. name属性
作⽤:⽤于在Spring的配置⽂件中,为bean对象定义别名(⼩名)
相同:
1. ctx.getBean("id|name")-->object
2. <bean id="" class=""
等效
<bean name="" class=""
# 区别:
1. 别名可以定义多个,但是id属性只能有⼀个值
2. XML的id属性的值,命名要求:必须以字⺟开头,字⺟ 数字 下划线 连字符 不
能以特殊字符开头 /person
name属性的值,命名没有要求 /person
name属性会应⽤在特殊命名的场景下:/person (spring+struts1)
XML发展到了今天:ID属性的限制,不存在 /person
第三章 Spring与日志框架整合
Spring与⽇志框架进⾏整合,⽇志框架就可以在控制台中,输出Spring框架运⾏过程中的⼀些
重要的信息。
好处:便于了解Spring框架的运⾏过程,利于程序的调试
- Spring如何整合⽇志框架
默认
Spring1.2.3早期都是于commons-logging.jar
Spring5.x默认整合的⽇志框架 logback log4j2
Spring5.x整合log4j
1. 引⼊log4j jar包
2. 引⼊log4j.properties配置⽂件
- 导入Pom
<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.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-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
第四章 注入(Injection)
1、. 什么是注⼊
通过Spring⼯⼚及配置⽂件,为所创建对象的成员变量赋值
1.1 为什么需要注⼊
通过编码的⽅式,为成员变量进⾏赋值,存在耦合
1.2如何进⾏注⼊[开发步骤]
- 类的成员变量提供set/get方法
- 配置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">
<bean id="person" class="com.xiaohe.spring01.Person">
<property name="name" value="kobe"/>
<property name="password" value="18"/>
</bean>
</beans>
第五章 Set注入详解
# 针对于不同类型的成员变量,在<property>标签,需要嵌套其他标签
<property>
xxxxx
</property>
1、JDK内置类型
1.1、String+8种基本数据类型
<value>zhangsan</value>
1.2、数组
<list>
<value>1777777777777@qq.com</value>
<value>1888888888888@qq.com</value>
<value>1999999999999@qq.com>
</list
1.3、Set集合
<set>
<value>11111</value>
<value>112222</value>
</set>
<set>
<ref bean
<set
</set>
1.4 List集合
<list>
<value>11111</value>
<value>2222</value>
</list>
<list>
<ref bean
<set
</list>
1.5 Map集合
注意: map -- entry -- key有特定的标签 <key></key>
值根据对应类型选择对应类型的标签
<map>
<entry>
<key><value>suns</value></key>
<value>3434334343</value>
</entry>
<entry>
<key><value>chenyn</value></key>
<ref bean
</entry>
</map>
1.6 Properites
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
</props>
1.7 复杂的JDK类型 (Date)
需要程序员⾃定义类型转换器,处理
2、用户自定义类型
2.1、第一种方式
- 为成员变量提供set get⽅法
- 配置⽂件中进⾏注⼊(赋值)
<bean id="userService" class="xxxx.UserServiceImpl">
<property name="userDAO">
<bean class="xxx.UserDAOImpl"/>
</property>
</bean>
2.2、第二种方式
- 第⼀种赋值⽅式存在的问题
1. 配置⽂件代码冗余
2. 被注⼊的对象(UserDAO),多次创建,浪费(JVM)内存资源
- 为成员变量提供set get⽅法
- 配置文件进行配置
<bean id="userDAO" class="xxx.UserDAOImpl"/>
<bean id="userService" class="xxx.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property>
</bean>
# Spring4.x 废除了 <ref local=""/> 基本等效 <ref bean=""/>
3、Set注入的简化写法
3.1 基于属性简化
JDK类型注⼊
<property name="name">
<value>zhangsan</value>
</property>
简化后
<property name="name" value="zhangsan"/>
注意:value属性 只能简化 8种基本类型+String 注⼊标签
⽤户⾃定义类型
<property name="userDAO">
<ref bean="userDAO"/>
</property>
简化后
<property name="userDAO" ref="userDAO"/>
3.2基于p命名空间简化
JDK类型注⼊
<bean id="person" class="xxxx.Person">
<property name="name">
<value>zhangsan</value>
</property>
</bean>
简化后为:
<bean id="person" class="xxx.Person" p:name="zhangsan"/>
注意:value属性 只能简化 8种基本类型+String 注⼊标签
⽤户⾃定义类型
<bean id="userService" class="xx.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property
JDK类型注⼊
<bean id="person" class="xxxx.Person">
<property name="name">
<value>suns</value>
</property>
</bean>
<bean id="person" class="xxx.Person" p:name="zhangsna"/>
注意:value属性 只能简化 8种基本类型+String 注⼊标签
⽤户⾃定义类型
<bean id="userService" class="xx.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
</property>
</bean>
简化代码为:
<bean id="userDao" class="com.xiaohe.spring01.UserDAOImpl"/>
<bean id="userService" class="com.xiaohe.spring01.UserServiceImpl" p:userDAO-ref="userDao"/>
第六章 构造注入
注⼊:通过Spring的配置⽂件,为成员变量赋值
Set注⼊:Spring调⽤Set⽅法 通过配置⽂件 为成员变量赋值
构造注⼊:Spring调⽤构造⽅法 通过配置⽂件 为成员变量赋值
1、开发步骤
- 提供有参构造⽅法
public class User {
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
- 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" 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">
<bean id="user" class="com.xiaohe.spring01.User">
<constructor-arg>
<value>zhangsan</value>
</constructor-arg>
<constructor-arg>
<value>12345</value>
</constructor-arg>
</bean>
</beans>
2、构造方法重载
2.1参数个数不同
通过控制<constructor-arg>标签的数量进⾏区分
2.2、通过控制标签的数量进⾏区分
通过在标签引⼊ type属性 进⾏类型的区分 <constructor-arg type="">
3、注入的总结
未来的实战中,应⽤set注⼊还是构造注⼊?
答案:set注⼊更多
1. 构造注⼊麻烦 (重载)
2. Spring框架底层 ⼤量应⽤了 set注⼊
第七章 构造注入
1. 反转(转移)控制(IOC Inverse of Control)
控制:对于成员变量赋值的控制权
反转控制:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring⼯⼚和配置⽂件中完成
好处:解耦合
底层实现:⼯⼚设计模式
2.依赖注入(Dependency Injection DI)
注⼊:通过Spring的⼯⼚及配置⽂件,为对象(bean,组件)的成员变量赋值
依赖注⼊:当⼀个类需要另⼀个类时,就意味着依赖,⼀旦出现依赖,就可以把另⼀个类作为本
类的成员变量,最终通过Spring配置⽂件进⾏注⼊(赋值)。
好处:解耦合
第八章 Spring工厂创建复杂对象
1.什么是复杂对象
复杂对象:指的就是不能直接通过new构造⽅法创建的对象
Connection
SqlSessionFactory
2.Spring工厂创建复杂对象的三种方式
2.1 FactoryBean接口
-
开发步骤
- 实现FactoryBean接口
public class MyFactoryBean implements FactoryBean<Connection> { @Override public Connection getObject() throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hezb", "root", "123456"); return conn; } @Override public Class<?> getObjectType() { return Connection.class; } @Override public boolean isSingleton() { return false; } }
- Spring配置文件的配置
<!--如果Class中指定的类型 是FactoryBean接⼝的实现类,那么通过id值获得的是这个类所创建的复杂对象 Connection--> <bean id="conn" class="com.xiaohe.factoryBean.ConnectionFactoryBean"/>
- 测试类
@Test public void test11() { ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml"); //通过这种方式获取的是ConnectionFactoryBean ConnectionFactoryBean conn = (ConnectionFactoryBean) ctx.getBean("&conn"); System.out.println("conn = " + conn); }
-
细节
- 如果想获取到FactoryBean类型的对象,ctx.getBean(“&conn”);
- 获得的就是ConnectionFactoryBean
-
isSingleton方法
- 返回 true 只会创建⼀个复杂对象
- 返回 false 每⼀次都会创建新的对象 问题:根据这个对象的特点 ,决定是返回true (SqlSessionFactory) 还是 false (Connection)
-
FactoryBean总结
Spring中⽤于创建复杂对象的⼀种⽅式,也是Spring原⽣提供的,后续讲解Spring整合其他框架,⼤量应⽤FactoryBean
2.2 实例工厂
# 1、避免Spring框架的侵入
# 2、整合遗留系统
- 编码
public class ConnectionFactory {
public Connection getConnection(){
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/hezb", "root", "123456");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}
return conn;
}
}
- 配置文件
<bean id="connectFactory" class="com.xiaohe.factoryBean.ConnectionFactory"/>
<bean id="conn" factory-bean="connectFactory" factory-method="getConnection"/>
2.3 静态工厂
-
开发步骤
<bean id="conn" class="com.xiaohe.factoryBean.StaticConnectionFactory" factory-method="getConnection"/>
3 Spring创建对象
第九章 控制Spring工厂创建对象的次数
1.如何控制简单对象的创建次数
sigleton:只会创建⼀次简单对象 默认值
prototype:每⼀次都会创建新的对象
<bean id="account" scope="singleton|prototype" class="xxxx.Account"/>
2. 如何控制复杂对象的创建次数
FactoryBean{
isSingleton(){
return true 只会创建⼀次
return false 每⼀次都会创建新的
}
}
如没有isSingleton⽅法 还是通过scope属性 进⾏对象创建次数的控制
3. 为什么要控制对象的创建次数?
好处:节省不别要的内存浪费
- 什么样的对象只创建⼀次?
1. SqlSessionFactory
2. DAO
3. Service
- 什么样的对象 每⼀次都要创建新的?
1. Connection
2. SqlSession | Session