2002年Rob Jhonson提出EJB存在的缺陷
EJB(Enterprise Java Bean)缺陷
总结:EJB是重量级框架
Spring是一个轻量级JavaEE解决方案,整合了众多优秀的设计模式
轻量级
对于运行环境没有额外要求
开源 tomcat resion jetty
收费 weblogic websphere
代码移植性高
不需要实现额外接口
JavaEE的解决方案
面对每一层都有解决方案
整合设计模式
工厂、代理、模板、策略
设计模式
1.广义
面向对象设计中,解决特定问题的经典代码
2.狭义
GOF4人定义的23种设计模式
概念:通过工厂类创建对象
好处:解耦合
耦合:代码间的强关联关系,一方的改变会影响另一方
问题:不利于代码维护
//简单案例:把接口的实现类硬编码在程序中
UserServiceImpl userService = new UserServiceImpl();
工厂类
public class BeanFactory {
public static UserService getUserService(){
return new UserServiceImpl();
}
}
测试类
import com.liu.pojo.BeanFactory;
import com.liu.pojo.User;
import com.liu.pojo.UserService;
import com.liu.pojo.UserServiceImpl;
import org.junit.Test;
public class TestSpring {
@Test
public void test01(){
//UserServiceImpl userService = new UserServiceImpl();
//通过工厂类创建对象,解耦合
UserService userService = BeanFactory.getUserService();
userService.login("liu","123456");
User user = new User("liushihao","123456");
userService.register(user);
}
}
但是这种工厂类的设计过于简单,仍然引入了具体的UserServiceImpl,仍然存在耦合。
对象的创建方式:
直接调用构造方法创建对象
UserServiceImpl userService = new UserServiceImpl();
通过反射的形式创建对象 解耦合
Class clazz = Class.forName(“com.liu.pojo.UserServiceImpl”);
UserService userService = (UserService)clazz.newInstance();
package com.liu.pojo;
public class BeanFactory {
public static UserService getUserService(){
UserService userService = null ;
try {
//通过反射一种方式还是无法彻底解决耦合,还是引入了接口实现类的全限定类名
Class clazz = Class.forName("com.liu.pojo.UserServiceImpl");
userService = (UserService)clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userService;
}
}
通过添加小的配置文件解决Class.forName(“com.liu.pojo.UserServiceImpl”);中的耦合问题
配置文件
#Properties类型的集合来存储Properties文件的内容
#特殊的Map key=String value=String
#Properties [userService = com.liu.pojo.UserServiceImpl]
#Properties.getProperty("userService")就可以获取com.liu.pojo.UserServiceImpl
userService = com.liu.pojo.UserServiceImpl
工厂类
package com.liu.pojo;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory {
private static Properties env = new Properties();
//IO是系统级资源,我们应该避免重复性地打开IO,最好在程序启动时一次性读取想要的内容
static{
//1.获得输入流
InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
//2.文件的内容封装到Properties集合中 key="userService" value="com.liu.pojo.UserServiceImpl"
try {
env.load(inputStream);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static UserService getUserService(){
UserService userService = null ;
try {
Class clazz = Class.forName(env.getProperty("userService"));
userService = (UserService)clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userService;
}
}
日后业务需要改动,只需将新的实现类写入配置文件applicationContext.properties中
但是这样的设计还是存在问题,只要有新的实现类出现,就需要在工厂类中添加
public static XXXX getXXXX(){
XXXX xxxx = null;
try {
Class clazz = Class.forName(env.getProperty("xxxx"));
xxxx = (XXXX)clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return xxxx;
}
这样的话会存在大量冗余代码
package com.liu.pojo;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory {
private static Properties env = new Properties();
static{
InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
try {
env.load(inputStream);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//key代表小配置文件中的key [userDAO, userService]
public static Object getBean(String key){
Object ret = null;
try {
Class clazz = Class.forName(env.getProperty(key));
ret = clazz.newInstance();
}catch (Exception e){
}
return ret;
}
}
至此,需要创建对象时只需要调用工厂类的getBean()方法,传入配置文件中对应的字符串即可。
//getBean是静态方法,可以直接类名.方法名进行调用UserService userService = (UserService) BeanFactory.getBean("userService");
定义类型(类)
通过配置文件的配置告知给工厂(applicationContext.properties)
key = value
通过工厂获得类的对象
Object ret = BeanFactory.getBean(“key”);
Spring本质:工厂 ApplicationContext(applicationContext.xml)
思路与上述工厂的设计一致,只不过Spring的工厂更为强大。
Spring的jar包(在pom.xml中设置依赖)
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.4.RELEASEversion>
dependency>
Spring的配置文件
配置文件的路径:任意位置 没有硬性要求
配置文件的命名:没有硬性要求 建议applicationContext.xml
一般配置方式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CLfY6wNf-1620665224815)(D:\截图\image-20210321211433462.png)]
ApplicationContext
接口:屏蔽实现的差异两种实现: 非web环境:ClassPathXmlApplicationContext(main junit) web环境:XmlWebApplicationContext
ApplicationContext工厂的对象(ClassPathXmlApplicationContext和XmlWebApplicationContext)占用大量内存重量级资源特点:故一个应用只会创建一个工厂对象ApplicationContext工厂一定是线程安全的(多线程并发访问)
创建类型
配置applicationContext.xml
<bean id="person" class="com.liu.pojo.Person">bean>
通过工厂类获得对象 ClassPathXmlApplicationContext
//1.获取Spring的工厂 ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");//2.通过工厂类获得对象 Person person = (Person) context.getBean("person");
Spring工厂创建的对象,叫做bean或者组件(component)
//通过这种方式获得对象就不需要进行强制类型转换
Person person = context.getBean("person", Person.class);
//当前配置文件中只能有一个
Person person = context.getBean(Person.class);
//获取当前配置文件中所有bean标签的id值
String[] beanDefinitionNames = context.getBeanDefinitionNames();
//获取配置文件中对应类型的id值
String[] beanNamesForType = context.getBeanNamesForType(Person.class);
//判断是否存在对应id的bean 返回值是boolean类型
//containsBeanDefinition 只能根据id判断,无法判断name
context.containsBeanDefinition("person");
//containsBean id、name都可以判断
context.containsBean("person");
<bean class="com.liu.pojo.Person">bean>
a)这种没有id值的配置 Spring会自动生成一个id com.liu.pojo.Person#0
b)应用场景:如果这个bean只需要使用一次,那么就可以省略id值
如果这个bean需要使用多次或者被其他bean引用,则需要设置id值
name属性
作用:用于在Spring配置文件中,为bean对象定义别名(小名)
与id相同的地方
1)context.getBean(“id/name”) --> object
2) 与id不同的地方 1)别名可以定义多个,id是唯一的 2)以前XML的id属性的值有命名要求:以字母开头,后面跟字母、数字、下划线、连字符 而name属性的值命名没有要求 如/person name属性会应用在特殊命名场景下:/person(spring + struts1) 但是XML发展到今天已经不存在id命名的限制,现在优先使用id 3)工厂的方法中id和name //containsBeanDefinition 只能根据id判断,无法判断name
context.containsBeanDefinition("person");
//containsBean id、name都可以判断
context.containsBean("person");
Spring工厂是可以调用private修饰的构造方法创建对象的
问题:Spring的工厂很强大,那么未来开发中是不是所有对象都交给Spring工厂来创建?
回答:理论上是的。但是有特例—实体对象(entity,会封装数据库表中的数据,这些数据会操作数据库),这类对象交给持久层框架来创建
Spring与日志框架整合,日志框架就可以在控制台输出Spring运行过程中的一些重要信息(对象的创建、销毁、进行了什么操作等)。便于了解Spring的运行过程,利于程序的调试
早期Spring1、2、3都是与commons-logging.jar进行整合
Spring5.x默认整合的日志框架是logback、log4j2
Spring整合log4j
1.引入log4j相关jar包
pom.xml
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.25version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
2.引入log4j.properties配置文件
#resources文件夹目录下
### 配置根
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
通过Spring工厂及配置文件,为所创建对象的成员变量赋值
通过编码的方式为成员变量赋值,存在耦合
@Test
public void test05(){
ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
Person person = (Person) context.getBean("person");
//原来赋值的方法,通过代码为成员变量赋值,存在耦合
person.setId(1);
person.setName("liushihao");
System.out.println(person);
}
1.在类中为成员变量提供set、get方法
2.配置Spring配置文件
<bean id="person" name="p" class="com.liu.pojo.Person">
<property name="id" value="10"/>
<property name="name" value="david"/>
bean>
解耦合
Spring通过底层调用对象属性对应的set方法,完成成员变量的赋值,这种方式也称为set注入
<bean id="person" name="p" class="com.liu.pojo.Person">
等效于
Account account = new Account();
<property name="id" value="10"/>
等效于
account.setId(10);
<property name="name" value="david"/>
等效于
account.setName("david");
针对于不同类型的成员变量,在标签中需要嵌套不同标签
8种基本类型(byte short int long float double char boolean)+String
在property标签中嵌套value
<property name="id">
<value>10value>
property>
<property name="name">
<value>liushvalue>
property>
数组类型
<property name="emails">
<list>
<value>[email protected]value>
<value>[email protected]value>
<value>[email protected]value>
list>
property>
集合类型
<property name="tels">
<set>
<value>138123123value>
<value>432123432value>
<value>132543543value>
set>
property>
<property name="addresses">
<list>
<value>centryparkvalue>
<value>swimmingpoolvalue>
<value>blocksvalue>
<value>blocksvalue>
<value>blocksvalue>
list>
property>
<property name="qqs">
<map>
<entry>
<key><value>liushvalue>key>
<value>1234324value>
entry>
map>
property>
<property name="p">
<props>
<prop key="key1">value1prop>
<prop key="key2">value2prop>
props>
property>
复杂的JDK类型(Date)
需要程序员自定义类型转换器处理。
为成员变量提供set get方法
配置文件中进行注入(赋值)
<bean id="userService" class="com.liu.pojo.UserServiceImpl">
<property name="userDAO">
<bean class="com.liu.pojo.UserDAOImpl"/>
property>
bean>
第一种方式存在的问题
假如有多个类都有UserDAO作为成员变量,那么配置文件中
<property name="userDAO">
<bean class="com.liu.pojo.UserDAOImpl"/>
property>
这段代码会重复出现多次 冗余
且会创建很多次userDAO对象 浪费内存(JVM)资源
为成员变量提供set get方法
在配置文件中先创建userDAO,然后在有UserDAO作为成员变量的类创建标签中,用ref标签引用这个userDAO对象
<bean id="userDAO" class="com.liu.pojo.UserDAOImpl"/>
<bean id="userService" class="com.liu.pojo.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
property>
bean>
<bean id="orderService" class="com.liu.pojo.orderServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
property>
bean>
#Spring4.x 废除了 <ref local=""/> 这个标签和<ref bean=""/>基本等效
JDK类型注入
<property name="name">
<value>liushvalue>
property>
简化写法
<property name="name" value="liush"/>
注意:value属性只能简化8中基本类型+String的注入标签
用户自定义类型
<property name="userDAO">
<ref bean="userDAO"/>
property>
简化写法
<property name="userDAO" ref="userDAO" />
p即property的缩写
JDK类型注入
<bean id="person" class="com.liu.pojo.Person">
<property name="name">
<value>liushvalue>
property>
bean>
简化写法
<bean id="person" class="com.liu.pojo.Person" p:name="xiaodong" p:id="11" />
用户自定义类型
<bean id="person" class="com.liu.pojo.Person">
<property name="userDAO">
<ref bean="userDAO"/>
property