学习b站视频《孙哥说spring5》时写的,传送门:001_简介_哔哩哔哩_bilibili
核心、基石,Spring
所有的特性都由工厂衍生而来
面试重点,重点:Spring
动态代理的底层实现
Spring
通过持久层整合,与现有的持久化方案集成
与strus2或MVC整合
Spring
开发的主流,Spring
boot开发的前置性知识
Spring
?1、Spring
是一个轻量级的JavaEE解决方案,整合了众多优秀的设计模式
轻量级:
对运行环境没有额外要求
代码移植性高
JavaEE解决方案
Spring
可以解决JavaEE开发每一层的问题
封装和整合了诸多设计模式
工厂
代理
模板
策略
什么是设计模式?
广义:面向对象设计中,解决特定问题的经典代码
狭义:GOF
四人帮定义的23种设计模式:工厂、适配器、装饰器、门面、代理······。具体需要根据项目和代码特点合理的选择设计模式,不一定全用。
概念:通过工厂类创建对象,习惯上直接new一个对象,但Spring
不提倡,推荐使用工厂类
好处:解耦合,耦合:代码见的强关联关系,一方改变将影响到另一方,耦合度高不利于代码维护。
简单工厂实现:通过工厂类中共提供的工厂方法创建对象
public class BeanFactory {
public static UserServiceImpl getUserService(){
return new UserServiceImpl();
}
}
//调用
class SingleApplicationTests {
@Test
void test() {
UserService userService = BeanFatory.getUserService();
userService.resetPassword(1);
}
}
对象创建的方式(工厂反射):
调用构造方法创建对象,就是直接new,缺点是耦合度高。上述代码中虽然用到了工厂方法,但工厂方法中依然是通过调用构造方法来创建对象,没有达到解耦合的目的。
通过反射的形式创建对象,能够达到解耦合的目的。创建方式分为两步:
通过Class.forName(String name)
方法获取类对象,方法的入参为全限定名(全限定名指包名加类名)。
通过类对象的newInstance
方法获取类对象,该方法的返回值是Object,因此需要经过参数类型转换。
//例:反射存在异常,因此在使用时需要将异常抛出
public class BeanFatory {
public static UserService getUserService(){
UserService userService = null;
try {
Class aClass = Class.forName("com.example.spring.service.impl.UserServiceImpl");
userService = (UserService) aClass.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userService;
}
}
Class.forName(String name)
方法传入的是类的全限定名,一旦类名更改,则该方法的入参也要修改,因此需要将耦合字符串也处理掉(通过配置文件)
## properties文件的读取思路
## 通过properties类型的集合存储properties文件的内容
## properties类型的集合的特点:特殊的Map,都是key-value结构,特殊之处在于properties的key和value都必须是字符串类型
## 将properties的键和值封装到properties集合当中
## 调用properties的getProperty()方法,以userService为键,就可以获取到值,至此就完成了文件内容的读取
userService = com.example.spring.service.impl.UserServiceImpl
调用:
//在工厂类中创建properties的集合
public static Properties properties = new Properties();
//使用静态代码块的方式来读取properties文件的内容
static {
try {
//首先获取IO的输入流
InputStream inputStream = BeanFatory.class.getResourceAsStream("/application.properties");
//将文件的内容封装到 properties集合中
properties.load(inputStream);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//Class.forName();方法的参数改为userService
完整的BeanFatory
代码如下:
public class BeanFatory {
//在工厂类中创建properties的集合
public static Properties properties = new Properties();
//使用静态代码块的方式来读取properties文件的内容
static {
try {
//首先获取IO的输入流
InputStream inputStream = BeanFatory.class.getResourceAsStream("/application.properties");
//将文件的内容封装到 properties集合中
properties.load(inputStream);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static UserService getUserService(){
UserService userService = null;
try {
Class> aClass = Class.forName(properties.getProperty("userService"));
userService = (UserService) aClass.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userService;
}
public static UserDao getUserDao(){
UserDao userDao = null;
try {
Class> aClass = Class.forName(properties.getProperty("userDao"));
userService = (UserService) aClass.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return userDao;
}
}
通过4.2的学习可以发现,通过简单工厂的工厂方法确实可以达到解耦合的目的,但需要为每个对象都提供一个对象方法,工厂方法中大部分代码都是重复的,因此会造成代码冗余。可通过设计通用工厂解决上述的问题。
通用工厂中的工厂方法设计如下:
在通用工厂中设计一个通用的工厂方法来创建对象,方法的返回值为Object,入参为String类型过的key,调用工厂方法时需要key,将结果的数据类型强制转换为需要的数据类型即可。
方法如下 :
//使用通用工厂可以创建一切想要创建的对象(前提是先在配置文件中配置好)
public static Object getObject(String key){
Object object = null;
try {
Class> aClass = Class.forName(properties.getProperty(key));
object = aClass.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return object;
}
调用方式如下:
void test() {
UserService userService = (UserService) BeanFatory.getObject("userService");
userService.getUser();
}
使用步骤:
定义要创建的类
在配置文件中配置要创建的类,读取配置告知工厂要创建的类型
调用工厂方法,通过key获取类对象,将结果强制转换成自己要创建的类型(类)
学习了Spring
的基本概念
了解了设计模式的基本概念,诸侯中学习了工厂设计模式
Spring
的本质就是工厂,Spring
提供了一个名为ApplicationContext的
工厂及相应的配置文件applicationContext.xml
,实现的思路与4.3相同但更加强大。
Spring
项目jdk
:1.8+(17)
Maven
:3.5+(3.6.3)
IntelliJ IDEA
:2018+(2021.2.2)
SpringFramework
:5+(5.1.4)
环境搭建主要有以下两点
Spring
的jar包
Spring
是通过基于Maven来管理jar包,只需要设置好pom的依赖,依赖是通过Maven的中心仓库来查找Spring
相关jar包的坐标来进行设置。
Maven仓库的地址: Maven Repository: Search/Browse/Explore (mvnrepository.com)
Spring
的配置文件
配置文件的放置位置:任意位置
配置文件命名:随意命名,Spring
建议使用applicationContext.xml(只是建议,不是硬性规定)
配置文件的命名和位置随意,但需要设置配置文件所处的路径
配置文件的创建:右击resources目录new -> XMLConfiguration File -> Spring
config
Spring
的核心API
核心API
即核心类,框架最核心的类型
ApplicationContext
工厂
Spring
提供的ApplicationContext
工厂主要用于对象的创建,能达到解耦合的目的。
ApplicationContext
接口类型
主要为了屏蔽实现的差异,由于工厂应用到的开发场景不用,其特点也略有不同,将工厂定义成接口可以屏蔽工厂之间具体的差异。
Spring
提供两种ApplicationContext
工厂的实现,分别是非web环境的ClassPathXmlApplicationContext
(主要指main或junit
单元测试,不需要启动服务器,所以是非web环境)和web环境的XmlWebApplicationContext
。
重量级资源
ApplicationContext
工厂的对象会占用大量的内存,
不要频繁的创建对象,一个应用只创建一个对象。
ApplicationContext
工厂一定是线程安全的,可以被多线程并发访问。
回顾工厂的使用步骤:
创建想要调用的类(想调用先得有)。
在applicatiionContext.xml
配置文件中配置想要调用的类,key-value形式,key必须唯一,值为类的全限定名具体的创建方式为在
标签内部写
标签,北部设置两个属性,分别是id和class,id存放key,class存放value。
通过Spring
提供的ApplicationContext
工厂类可以获得对象。测试时需要选择对应的工厂子类来创建工厂,在使用工厂子类时需要设置配置文件的全限定名。
创建实例如下:
用UserServiceImpl测试,已有无需创建。
applicationContext.xml文件配置:
方法中使用工厂创建对象:
void contextLoads() {
//配置文件在resources文件夹的根目录下,所以全限定名为/applicationContext.xml
ApplicationContext app =new ClassPathXmlApplicationContext("/applicationContext.xml");
//使用工厂创建对象
UserService user = (UserService) app.getBean("User");
//调用get方法测试
user.get();
}
名词解释
Spring
工厂创建的对象叫做bean或者组件(componet)。
Spring
工厂的相关方法:
void test() {
//配置文件在resources文件夹的根目录下,所以全限定名为/applicationContext.xml
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
//getbean的第一种重构形式,通过指定id来创建对象
UserService user = (UserService) app.getBean("User");
//getbean的第二种重构形式,通过指定id和类型来获取对象,省略强制转换
UserService user1 = app.getBean("User", UserService.class);
//getbean的第三种重构形式,直接传入class对象,使用此方法需确保在配置文件中只能有一个 标签的值为class
UserService user2 = app.getBean( UserService.class);
//获取工厂配置文件中所有标签id的值,返回值的类型为数组
String[] beanDefinitionNames = app.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//获取工厂配置文件中所有class值为UserServiceImpl的标签id的值,返回值的类型为数组
String[] beanNamesForType = app.getBeanNamesForType(UserServiceImpl.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
//判断是否存在指定id值的bean,只能通过id来判断
boolean b = app.containsBeanDefinition("U");
System.out.println(b);
//判断是否存在指定id值的bean,可以通过id和name来判断
boolean u = app.containsBean("U");
System.out.println(u);
//调用方法测试
//user.get();
}
配置文件需要注意的细节
class
配置
配置文件中创建
标签只配置class属性是可以的,Spring
认可这种语法
标签只配置class而没有配置id,Spring
会配置一个默认的id值
上述配置的应用场景:这个
只需使用一次则不用设置id,如果这个
使用了多次或者被其他的
引用,则需要设置id
值。
name属性
name属性为
相同:调用getbean()
方法创建对象时,可以使用name
创建,创建
标签是可以用name
来取代id
区别:
标签的id只能有一个值,但是name
可以有多个值。
xml
的id
属性的值的命名必须以字母为开头,后面可以接字母、数字、下划线、连字符,name
属性没有命名要求,因此name
属性通常应用在特殊命名的场景
如今,xml
的id
命名限制已经没有了
Spring
工厂的源码分析执行顺序:
通过ClassPathXmlApplicationContext
工厂读取applicationContext.xml
配置文件信息。
获得
标签的相关属性id和class的值,通过反射创建对象,将class
属性的值作为参数传递到Class.forName()
方法中去,调用newInstance
方法即可创建成功。
通过反射创建对象时,底层同样会调用该对象的无参构造方法,通过因此反射创建对象与new等效。
即使对象的构造方法是私有的,Spring
依旧可以调用,因为反射可以调用类型的私有的属性、方法及构造方法
问:什么情况下会用工厂来创建对象? 答:除实体类以外,所有的对象都由工厂创建,实体类对象本身意义不大,其对应的数据库的数据更为重要,获取这些数据需要操作数据库,因此实体类由持久层创建
Spring整合日志框架之后,日志可以在控制台输出Spring执行过程中的一些重要信息, 优点利于了解Spring的执行过程,便于开发人员对程序进行调试。
Spring
如何整合日志框架默认: Spring的早期版本1.X、2.X、3.X都是与commons-logging整合 Spring4.X、5.X两个版本默认与logback、log4j2整合 手动整合log4j 1.引入相关jar包 2.引入log4j.properties配置文件
pom依赖:
org.slf4j
slf4j-log4j12
1.7.26
log4j
log4j
1.2.17
配置文件(此处使用yaml格式):
log4j:
rootLogger: debug.console
appender:
console: org.apache.log4j.ConsoleAppender
target: System.out
layout: org.apache.log4j.PatternLayout
ConversionPattern: %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
通过Spring
工厂及配置文件为对象的成员变量赋值
通过编码的方式为成员变量赋值存在耦合,利如set。
类为成员变量提供get/set方法。
配置Spring
的配置文件
56
解耦合!
类为成员变量提供get/set方法。
配置Spring
的配置文件,在
标签中创建
标签来调用成员变量的set方法,其name
属性用来指定成员变量,
内部创建
标签用来设置成员变量的值。
读取
标签,获取id和要创建的类型。通过反射创建好对象
解析property
标签,读取到类拥有的成员变量,进行赋值操作,等效于通过对象调用了set
方法,再通过
标签来读取要赋的值,至此,赋值操作完成。
Spring
通过底层调用对象成员变量对应的set方法来为成员变量赋值,英雌也被称之为色图注入