参考自B站UP主视频《孙哥说Spring5》
所以也就称为重量级框架
Spring是一个轻量级的JavaEE解决方案,整合众多优秀的设计模式
轻量级:对于运行环境是没有额外的要求
代码移植性高:不需要实现额外的接口(EJB代码需要专门实现服务器的接口)
合理运用了优秀的设计模式
1. 工厂模式(核心)
2. 代理模式
3. 模板模式
4. 策略模式
关于工厂模式,可以查看之前写的一篇更加详细的笔记:让代码来告诉你什么叫工厂模式
#---------------- applicationContext.properties 文件 --------------------
# 在读取配置文件的时候,是使用 一个 Properties 集合来存储 Properties文件内容
# Properties是一种特殊Map:key要是String类型的 value也是String类型的
# 例如下面这两行配置,我们可以在代码中使用key获得value Properties.getProperties("userService");
# 当有一个新的类要使用的话,只需要改配置文件中的内容即可,也就是实现了解耦
userService = com.rick.serviceImpl.UserServiceImpl
userDao = com.rick.daoImpl.UserDaoImple
通过Bean工厂来获得一个类
----------------------------------BeanFactory.java-------------------------------------
package com.rick.factory;
import com.rick.dao.UserDao;
import com.rick.daoImpl.UserDaoImpl;
import com.rick.service.UserService;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import static java.lang.Class.*;
/**
* 工厂类
* @author Rick
*/
public class BeanFactory {
private static Properties env = new Properties();
static{
// 静态代码块,在系统启动的时候就加载配置文件
try {
//获得IO输入流,读取文件
InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
//文件中的内容封装进 Properties集合
env.load(inputStream);
// 关闭输入流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
对象的创建方式:
1、直接通过调用构造方法 UserService userService = new UserService();
2、通过反射的形式,创建对象,解耦合 getInstance()
*/
public static UserService getUserService(){
//return new UserServiceImpl();
UserService userService = null;
try {
// 获得Class模板
Class clazz = Class.forName(env.getProperty("userService"));
// 通过模板创建
userService = (UserService) clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return userService;
}
public static UserDaoImpl getUserDao(){
UserDaoImpl userDaoImpl = null;
try {
Class clazz = Class.forName(env.getProperty("userDao"));
userDaoImpl = (UserDaoImpl)clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return userDaoImpl;
}
}
-------------------------------UserService.java--------------------------
package com.rick.service;
import com.rick.domain.User;
/**
* 接口
* @author Rick
*/
public interface UserService {
public void saveUser(User user);
public void querryUserByNameAndPassword(User user);
}
------------------------------UserServiceImpl.java--------------------------------------
package com.rick.serviceImpl;
import com.rick.dao.UserDao;
import com.rick.factory.BeanFactory;
import com.rick.service.UserService;
import com.rick.domain.User;
/**
* 实现类
* @author Rick
*/
public class UserServiceImpl implements UserService {
// UserDao userDao = new UserDaoImpl(); // 这样对于两个类的耦合性很高,不利于代码的维护
UserDao userDao = BeanFactory.getUserDao(); // 使用 Bean工厂 来获得 UserDao,能够降低类之间的耦合性
@Override
public void saveUser(User user) {
userDao.saveUser(user);
}
@Override
public void querryUserByNameAndPassword(User user) {
userDao.querryUserByNameAndPassword(user);
}
}
------------------------------------------------UserDao.java-------------------------------
package com.rick.dao;
import com.rick.domain.User;
/**
* 接口
* @author Rick
*/
public interface UserDao {
public void saveUser(User user);
public void querryUserByNameAndPassword(User user);
}
-----------------------------------------UserDaoImpl.java------------------------------
package com.rick.daoImpl;
import com.rick.dao.UserDao;
import com.rick.domain.User;
/**
* 实现类
* @author Rick
*/
public class UserDaoImpl implements UserDao {
@Override
public void saveUser(User user) {
System.out.println("saving" + user.toString());
}
@Override
public void querryUserByNameAndPassword(User user) {
System.out.println(user.toString());
}
}
---------------------------------AppTest.java------------------------------------------
package com.ric
import static org.junit.Assert.assertTrue;
import com.rick.service.UserService;
import com.rick.factory.BeanFactory;
import org.junit.Test;
// 测试类
public class AppTest
{
@Test
public void test1() throws Exception{
// 如果有一个新的实现类 UserServiceImplNew 就需要改动代码来使用新的类
//UserService userService = new UserServiceImplNew();
//UserService userService = new UserServiceImpl();
// 通过配置文件来集中管理
UserService userService = BeanFactory.getUserService(); // 通过工厂类获得类
User user = new User();
userService.querryUserByNameAndPassword(user);
}
}
将上面的 BeanFactory 中的代码优化一下,使其能够适合更多的类去使用
package com.rick.factory;
import com.rick.dao.UserDao;
import com.rick.daoImpl.UserDaoImpl;
import com.rick.service.UserService;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import static java.lang.Class.*;
/**
* 通用的工厂类
* @author Rick
*/
public class BeanFactory {
private static Properties env = new Properties();
static{
try {
//获得IO输入流
InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
//文件内容分装
env.load(inputStream);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* key : 配置文件中的 key
* 通过传入 key 来获得指定的全限定类名,从而获得类
* 这样就可以实现,通过 BeanFactory 获得任意自己想要的类名
*/
public static Object getBean(String key){
Object obj = null;
Class clazz = null;
try {
clazz = Class.forName(env.getProperty("key"));
obj = clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return obj;
}
}
软件版本
环境搭建
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.15.RELEASEversion>
dependency>
applicationContext.xml
ApplicationContext
ApplicationContext 为 【接口类型】
接口:可屏蔽实现的差异
提供了两种接口的实现
非web环境:ClassPathXmlApplicationContext
web环境:XmlWebApplicationContext
ApplicationContext 为 【重量级资源】--> ApplicationContext工厂的对象会【占用大量内存】
不会频繁的创建对象:(单例)
一个应用只会创建一个工厂对象
ApplicationContext工厂:
一定是【线程安全】的(多线程并发访问)
applicationContext.xml
ApplicationContext
或ClassPathXmlApplication
实体类
//-----------------------------------Person.java
package com.rick.domain;
/**
* @author Rick
* 通过工厂创建
*/
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
", age=" + age +
", dog=" + dog +
'}';
}
}
创建配置文件
<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.rick.domain.Person"/>
beans>
测试获得类
//--------------------------------Test.java
package com.rick;
import com.rick.domain.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Unit test for simple App.
*/
public class AppTest
@Test
public void test1(){
//获得Spring的工厂,并传入配置文件的路径
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
/**
*需要强制转换类型
*Person user = (Person) ctx.getBean("person");
*不需要强制转换类型 但是要求只能由一个Bean标签是Person类型
*Person person = ctx.getBean(Person.class);
*/
// 通过传入 Class 模板来获得,可以避免强制类型转换
Person user = ctx.getBean("person",Person.class);
System.out.println(person.toString());
//获取 Spring工厂配置文件 中所有 bean标签 的 id值,但 不能判断 name值
String[] beanDefinitionNames = ctx.getBeanDefinitionNames(Person.class);
//根据 类型 获得 Spring配置文件 中对应的 id值,也 能判断 name值
String[] beanNamesForType = ctx.getBeanNamesFOrType(Person.class);
//判断是否存在指定的 id值 的 bean
boolean isExist = ctx.containBeanDefinition("a");
//判断是否存在指定的 id值 的 bean
boolean isExist = ctx.containBean("a");
}
}
只配置class属性:
com.rick.domain.Person#0
bean
只用一次则可以省略id值,反之若需要多次使用则必须设置id值name属性
id属性是大名
】name="p,p1,p2"
,但 id属性只能有一个值id="person"
/person
或者2person
// 引入 name 属性之后,这两者就有一定的区别的
// 不能识别 name
boolean isExist = ctx.containBeanDefinition("a");
// 可以识别 name
boolean isExist = ctx.containBean("a");
对Spring工厂底层实现原理的简单分析
1. 通过 ClassPathXmlApplicationContext() 或 XmlWebApplicationContext() 工厂读取配置文件 applicationContext.xml
2. 获得bean标签的相关信息(id的值,class的值),通过反射创建对于的对象
Classs<?> clazz = Class.forName(Person.class);
Person person = clazz.newInstance();
3. 反射创建对象,底层也是会调用对象自己的构造方法
Classs<?> clazz = Class.forName(Person.class);
Person person = clazz.newInstance();
等效于
Person person = new Person();
整合日志框架后,日志框架就可以在控制台中,输出执行过程中的一些重要的信息,可以便于我们了解运行过程,也可以利于程序的调试
Spring 1、2、3早期都是使用 commin-logging.jar
,Spring 5.x 默认整合的日志框架logback log4j2
logback
是由log4j
创始人设计的又一个开源日志组件
logback当前分成三个模块:
logback-core
:是其它两个模块的基础模块。logback- classic
:是log4j的一个 改良版本,且完整实现Slf4j API
,使你可以很方便地更换成其它日志系统如log4j
或JDK14 Logging
logback-access
:与Servlet容器
集成提供通过Http
来访问日志的功能logback-corelogback-classic
:此外logback-classic
完整实现Slf4J API
使你可以很方便地更换成其它日志系统如log4j
或JDK14 Logging
。
1. 更快的实现 Logback的内核重写了,在一些关键执行路径上性能提升10倍以上。而且logback不仅性能提升了,初始化内存加载也更小了。
2. 非常充分的测试 Logback经过了几年,数不清小时的测试。Logback的测试完全不同级别的。这是简单重要的原因选择logback而不是log4j。
3. Logback-classic非常自然实现了SLF4j Logback-classic实现了 SLF4j。在使用SLF4j中,你都感觉不到logback-classic。而且因为logback-classic非常自然地实现了SLF4J, 所 以切换到log4j或者其他,非常容易,只需要提供成另一个jar包就OK,根本不需要去动那些通过SLF4JAPI实现的代码。
4. 非常充分的文档 官方网站有两百多页的文档。
5. 自动重新加载配置文件 当配置文件修改了,Logback-classic能自动重新加载配置文件。扫描过程快且安全,它并不需要另外创建一个扫描线程。这个技术充分保证了应用程序能跑得很欢在JEE环境里面。
6. Lilith Lilith是log事件的观察者,和log4j的chainsaw类似。而lilith还能处理大数量的log数据 。
7. 谨慎的模式和非常友好的恢复 在谨慎模式下,多个FileAppender实例跑在多个JVM下,能 够安全地写道同一个日志文件。RollingFileAppender会有些限制。Logback的FileAppender和它的子类包括 RollingFileAppender能够非常友好地从I/O异常中恢复。
8. 配置文件可以处理不同的情况 开发人员经常需要判断不同的Logback配置文件在不同的环境下(开发,测试,生产)。而这些配置文件仅仅只有一些很小的不同,可以通过,和来实现,这样一个配置文件就可以适应多个环境。
9. Filters(过滤器) 有些时候,需要诊断一个问题,需要打出日志。在log4j,只有降低日志级别,不过这样会打出大量的日志,会影响应用性能。在Logback,你可以继续 保持那个日志级别而除掉某种特殊情况,如alice这个用户登录,她的日志将打在DEBUG级别而其他用户可以继续打在WARN级别。要实现这个功能只需 加4行XML配置。可以参考MDCFIlter 。
10. SiftingAppender(一个非常多功能的Appender) 它可以用来分割日志文件根据任何一个给定的运行参数。如,SiftingAppender能够区别日志事件跟进用户的Session,然后每个用户会有一个日志文件。
11. 自动压缩已经打出来的log RollingFileAppender在产生新文件的时候,会自动压缩已经打出来的日志文件。压缩是个异步过程,所以甚至对于大的日志文件,在压缩过程中应用不会受任何影响。
12. 堆栈树带有包版本 Logback在打出堆栈树日志时,会带上包的数据。
13. 自动去除旧的日志文件 通过设置TimeBasedRollingPolicy或者SizeAndTimeBasedFNATP的maxHistory属性,你可以控制已经产生日志文件的最大数量。如果设置maxHistory 12,那那些log文件超过12个月的都会被自动移除。
总结:logback 比 log4j 优秀,可以取代之。
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-accessartifactId>
<version>1.2.3version>
dependency>
放在 resources
文件夹根目录下
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="LOG_PATH" value="${catalina.base}/logs"/>
<property name="PROJECT_NAME" value="spring-starter"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %level ${PROJECT_NAME} [%thread] [%logger{50}:%line] %msg%npattern>
<charset>utf-8charset>
encoder>
appender>
<appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${PROJECT_NAME}/${PROJECT_NAME}.log.error.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${PROJECT_NAME}/log-error-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>10MBmaxFileSize>
<maxHistory>30maxHistory>
rollingPolicy>
<append>trueappend>
<encoder>
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %level ${PROJECT_NAME} [%thread] [%logger{50}:%line] %msg%npattern>
<charset>utf-8charset>
encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<appender name="FILE-WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${PROJECT_NAME}/${PROJECT_NAME}.log.warn.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${PROJECT_NAME}/log-warn-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>10MBmaxFileSize>
<maxHistory>30maxHistory>
rollingPolicy>
<append>trueappend>
<encoder>
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %level ${PROJECT_NAME} [%thread] [%logger{50}:%line] %msg%npattern>
<charset>utf-8charset>
encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARNlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<appender name="FILE-INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${PROJECT_NAME}/${PROJECT_NAME}.log.info.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${PROJECT_NAME}/log-info-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>10MBmaxFileSize>
<maxHistory>30maxHistory>
rollingPolicy>
<append>trueappend>
<encoder>
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %level ${PROJECT_NAME} [%thread] [%logger{50}:%line] %msg%npattern>
<charset>utf-8charset>
encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFOlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<appender name="FILE-DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${PROJECT_NAME}/${PROJECT_NAME}.log.debug.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${PROJECT_NAME}/log-debug-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>10MBmaxFileSize>
<maxHistory>30maxHistory>
rollingPolicy>
<append>trueappend>
<encoder>
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %level ${PROJECT_NAME} [%thread] [%logger{50}:%line] %msg%npattern>
<charset>utf-8charset>
encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUGlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<appender name="OTHER" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARNlevel>
filter>
<File>${LOG_PATH}/${PROJECT_NAME}/${PROJECT_NAME}.log.other.logFile>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${PROJECT_NAME}/log-other-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<maxFileSize>35MBmaxFileSize>
<maxHistory>30maxHistory>
rollingPolicy>
<append>trueappend>
<encoder>
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %level ${PROJECT_NAME} [%thread] [%logger{50}:%line] %msg%npattern>
<charset>utf-8charset>
encoder>
appender>
<logger name="com.tao" level="INFO" additivity="false">
<appender-ref ref="FILE-ERROR" />
<appender-ref ref="FILE-WARN" />
<appender-ref ref="FILE-INFO" />
<appender-ref ref="FILE-DEBUG" />
<appender-ref ref="STDOUT" />
logger>
<root level="WARN">
<appender-ref ref="OTHER" />
root>
configuration>