spring全家桶
Spring FrameWork:
概述:
Spring是分层的 Java SE/EE应用 full-stack(全栈式) 轻量级开源框架。
提供了表现层 SpringMVC和持久层 Spring JDBC Template以及 业务层 事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。
两大核心:以 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。
优势:
1)方便解耦,简化开发
Spring就是一个容器,可以将所有对象创建和关系维护交给Spring管理
通过配置文件+反射,解决编译器依赖问题(去掉new关键字)+硬编码问题。
2)AOP编程的支持
Spring提供面向切面编程(动态代理),方便实现程序进行权限拦截,运行监控等功能。
3)声明式事务的支持
通过配置完成事务的管理,无需手动编程
4)方便测试,降低JavaEE API的使用
Spring对Junit4支持,可以使用注解测试
5)方便集成各种优秀框架
不排除各种优秀的开源框架,内部提供了对各种优秀框架的直接支持
IOC:
概述:
控制反转(Inverse Of Control)不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序。
控制:在java中指的是对象的控制权限(创建、销毁)
反转:指的是对象控制权由原来 由开发者在类中手动控制 反转到 由Spring容器控制
之前一个实例需要开发者自己手动创建,现在直接从spring的IOC容器获得,对象的创建权交给了spring控制
自定义IOC容器:
把所有需要创建对象的信息定义在配置文件中
编写BeanFactory工具类,static代码块读取配置,实例化对象
service获取对象userDao = (UserDao) BeanFactory.getBean("userDao");
相关API:
BeanFactory:
介绍:
BeanFactory是 IOC 容器的核心接口,它定义了IOC的基本功能。
在第一次调用getBean()方法时,创建指定对象的实例
示例:
BeanFactory beanFactory =new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
ApplicationContext:
介绍:
接口,代表应用上下文对象,可以获得spring中IOC容器的Bean对象。
特点:
在spring容器启动时,加载并创建所有对象的实例
常用实现类:
- ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件 推荐使用这种。
- FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
- AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
常用方法:
- Object getBean(String name);
根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。
- T getBean(Class requiredType);
根据类型从容器中匹配Bean实例,当容器中相同类型(即接口有多个实现)的Bean有多个时,则此方法会报错。
- T getBean(String name,Class requiredType);
根据Bean的id和类型获得Bean实例,解决容器中相同类型Bean有多个情况。
Bean标签:
格式:
<bean id="" class=""></bean>
基本属性:
id:Bean实例在Spring容器中的唯一标识
class:Bean的全限定名,默认情况下它调用的是类中的 无参构造函数,如果没有无参构造函数则不能创建成功。
范围配置:
格式:
<bean id="" class="" scope=""></bean>
参数:
singleton 默认值,单例的
prototype 多例的,适合线程安全的场景
request WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中
session WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中
global sessionWEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession 相当于 session
singleton:
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
prototype:
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
Bean的生命周期:
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
生命周期配置:
<bean id="" class="" scope="" init-method="" destroy-method=""></bean>
destroy-method可能来不及打印,可以手动执行context.close()方法触发。
循环依赖问题:
现象:
多个bean之间相互依赖,比如A依赖B,B依赖于C,C依赖于A
JavaSE中的循环依赖:
构造器方式注入依赖:比如A a = new A(new B(new A(...)))代码上无法通过编译了。
setter方式注入依赖:能解决循环依赖的问题。
spring中:
scope为singleton是支持循环依赖的,原理是使用了三级缓存提前暴露(中间态)来解决循环依赖的问题。
scope为prototype会初始化报错。
三级缓存:
概述:
第一级缓存(单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象
第二级缓存earlySingletonObjects:存放早期暴露出来的Bean对象,Bean的生命周期还没结束,属性还没填充完。
第三级缓存singletonFactories:存放生成bean的工厂。
创建流程:
getBean发现A没有,然后A创建过程先执行doCreateBean将自己放入三级缓存,populateBean的时候需要B,于是去实例化B。
B实例化的时候发现需要A,于是B先查一级缓存、二级缓存、三级缓存,直到找到A。然后将A放到二级缓存并删除三级缓存的A。
B顺利初始化完毕,将自己放到一级缓存里面(B里面的A还是创建中状态)addSingleton()
接着回到创建A,此时B创建完毕,直接从一级缓存拿到B,完成创建,并将自己放到一级缓存里。
Bean实例化:
分类:
无参构造方法实例化
工厂静态方法实例化<bean id="userDao" class="com.lagou.factory.StaticFactoryBean" factory-method="createUserDao" />
工厂普通方法实例化
<bean id="dynamicFactoryBean" class="com.lagou.factory.DynamicFactoryBean"/>
<bean id="userDao" factory-bean="dynamicFactoryBean" factorymethod="createUserDao"/>
Bean依赖注入:
概述:
依赖注入 DI(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
传统做法是在service层通过IOC容器获取持久层的实例,然后在service层调用实例的方法。
而现在通过框架把持久层对象传入业务层
方式一:构造方法
service层提供有参构造方法
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
配置Spring容器调用有参构造时进行注入
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
<constructor-arg index="0" type="com.lagou.dao.UserDao" ref="userDao"/> //多个有参时属性index从0开始;ref表示引用的类型
<constructor-arg name="userDao" ref="userDao"/> // 简化写法,name表示参数的名字
</bean>
方式二:set方法
在UserServiceImpl中创建set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
配置Spring容器调用set方法进行注入
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/> //name指定参数名字
</bean>
方式三:P命名空间注入(简洁不了多少,使用较少)
P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中
需要新引入P命名空间:
xmlns:p="http://www.springframework.org/schema/p"
需要修改注入方式:
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>
依赖注入的数据类型:
分类:
1. 普通数据类型
2. 引用数据类型
3. 集合数据类型
注入普通数据类型(set方法)
<bean id="user" class="com.lagou.domain.User">
<property name="username" value="jack"/>
<property name="age" value="18"/>
</bean>
注入集合数据类型
List集合注入
<bean id="user" class="com.lagou.domain.User">
<property name="username" value="jack"/>
<property name="age" value="18"/>
</bean>
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl">
<property name="list">
<list>
<value>aaa</value>
<ref bean="user"></ref>
</list>
</property>
</bean>
Set集合注入
list标签改为set标签即可
Array数组注入
标签改为array标签即可,注意类型一致的问题,Object类型
Map集合注入
<map>
<entry key="k1" value="ddd"/>
<entry key="k2" value-ref="user"></entry>
</map>
Properties配置注入
<props>
<prop key="k1">v1</prop>
<prop key="k2">v2</prop>
<prop key="k3">v3</prop>
</props>
配置文件模块化:
介绍:
Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,也就是所谓的配置文件模块化。
并列的多个配置文件
ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");
主从配置文件
<import resource="applicationContext-xxx.xml"/> beans标签内
注意
同一个xml中不能出现相同名称的bean,如果出现会报错
多个xml如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean
配置DbUtils
介绍:
持久层使用DbUtils,而不是ORM场景
如何配置:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/spring_db"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"> //实例化有参构造
<constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean>
<bean id="accountDao" class="com.lagou.dao.impl.AccountDaoImpl">
<property name="queryRunner" ref="queryRunner"></property> //传递给dao层,dao层需要这个依赖来sql操作数据库
</bean>
然后再将dao层的对象传递给service层即可,dao.xxx调用方法
抽离数据库配置到jdbc.properties文件:
首先,需要引入context命名空间和约束路径:
- 命名空间:
xmlns:context="http://www.springframework.org/schema/context"
- 约束路径:
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
使用变量
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
注解开发:
介绍:
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。
常用注解:
@Component 使用在类上用于实例化Bean,任意类都可以。
@Controller 使用在web层类上用于实例化Bean,可以跨层乱用,这个是使用规范。
@Service 使用在service层类上用于实例化Bean,默认情况下,scope值是单例-singleton的,也是线程不安全的。
静态变量不安全,用ThreadLocal来封装,这个是线程安全的
@Repository 使用在dao层类上用于实例化Bean
@Autowired 使用在字段上用于根据类型依赖注入,能获取到父类类型。
@Qualifier 结合@Autowired一起使用,根据名称进行依赖注入,不能单独使用
@Resource 相当于@Autowired+@Qualifier,按照名称进行注入
@Value 注入普通属性
@Scope 标注Bean的作用范围
@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestroy 使用在方法上标注该方法是Bean的销毁方法
使用:
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
<context:component-scan base-package="com.lagou"></context:component-scan>
Bean实例化(IOC):
介绍:
使用@Compont或@Repository标识UserDaoImpl需要Spring进行实例化。
示例:
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
------------------------------------------------------------------
// @Component(value = "userDao")
@Repository // 如果没有写value属性值,Bean的id为:类名首字母小写
public class UserDaoImpl implements UserDao {
}
属性依赖注入(DI):
介绍:
使用@Autowired或者@Autowired+@Qulifier或者@Resource进行userDao的注入
示例:
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
<property name="userDao" ref="userDaoImpl"/>
</bean>
----------------------------
@Autowired
@Qualifier("userDaoImpl")
private UserDao userDao;
@Value注入普通类型:
介绍:
使用@Value进行字符串的注入,结合SPEL表达式获得配置参数
需要使用<context:property-placeholder>引入
示例:
@Value("注入普通数据")
private String str;
@Value("${jdbc.driver}")
private String driver;
@Scope标注Bean的范围:
@Service
@Scope("singleton")
public class UserServiceImpl implements UserService {{
}
Bean生命周期:
介绍:
使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法
示例:
@PostConstruct
public void init(){
System.out.println("初始化方法....");
}
@PreDestroy
public void destroy(){
System.out.println("销毁方法.....");
}
xml配置注解:
介绍:
使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如<context:property-placeholder>、<context:component-scan>、<import>
对应注解:
@Configuration 用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解
@Bean 使用在方法上,标注将该方法的返回值存储到 Spring 容器中,非自定义的Bean的配置(比如第三方类),默认名字与方法一致,首字母小写。
在不设置scope的时候他也是线程不安全的。
@PropertySource 用于加载 properties 文件中的配置,替代了<context:property-placeholder>
@ComponentScan 用于指定 Spring 在初始化容器时要扫描的包
@Import 用于导入其他配置类
使用:
编写Spring核心配置类,位于com.example.config路径下
@Configuration
@ComponentScan("com.lagou")
@Import(DataSourceConfig.class)
public class SpringConfig {
@Bean("queryRunner")
public QueryRunner getQueryRunner(@Autowired DataSource dataSource) {
return new QueryRunner(dataSource);
}
}
编写数据库配置信息类:
@PropertySource("classpath:jdbc.properties") //类中可以通过Value("${}")获取配置
public class DataSourceConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")
public DataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
使用AnnotationConfigApplicationContext(SpringConfig.class)生成context对象。
Spring整合Junit:
介绍:
在普通的测试类中,需要开发者手动加载配置文件并创建Spring容器,然后通过Spring相关API获得Bean实例;如果不这么做,那么无法从容器中获得对象。
可以让SpringJunit负责创建Spring容器来简化这个操作,开发者可以直接在测试类注入Bean实例(无需手动实例化);但是需要将配置文件的名称告诉它。
步骤分析:
1. 导入spring集成Junit的坐标
spring-test和junit,spring5 及以上版本要求 junit 的版本必须是 4.12 及以上
2. 使用@Runwith注解替换原来的运行器
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunitTest {
}
3. 使用@ContextConfiguration指定配置文件或配置类
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(value = {"classpath:applicationContext.xml"}) 加载spring核心配置文件
@ContextConfiguration(classes = {SpringConfig.class}) // 加载spring核心配置类
public class SpringJunitTest {
}
4. 使用@Autowired注入需要测试的对象(成员变量)
@Autowired
private AccountService accountService;
5. 创建测试方法进行测试
@Test
public void testFindById() {
Account account = accountService.findById(3);
System.out.println(account);
}
AOP:
背景:
dao层和service层的交互,dao层的事务是彼此独立的,service层操作多个dao层如何实现一个事务?比如转账。
上面的代码事务在dao层,转出转入操作都是一个独立的事务,但实际开发,应该把业务逻辑控制在一个事务中,所以应该将事务挪到service层。
传统事务:
步骤:
1. 编写线程绑定工具类(确保前后操作都在同一个conn中,才能确保同一个事务)
threadLocal.set(connection);
threadLocal.remove();
2. 编写事务管理器(手动开启,手动提交、回滚等)
3. 修改service层代码(前后多个操作写在一起,先开启事务再执行最后提交,遇到异常回滚)
4. 修改dao层代码(queryRunner.update方法获取之前创建好的conn对象)
问题分析:
上面代码,通过对业务层改造,已经可以实现事务控制了,但是由于我们添加了事务控制,也产生了一个新的问题: 业务层方法变得臃肿了,里面充斥着很多重复代码。
并且业务层方法和事务控制方法耦合了,违背了面向对象的开发思想。
解决:
可以将业务代码和事务代码进行拆分,通过动态代理的方式,对业务方法进行事务的增强。这样就不会对业务层产生影响,解决了耦合性的问题啦!
动态代理:
介绍:
JDK 代理: 基于接口的动态代理技术·:利用拦截器(必须实现invocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,从而实现方法增强
局限性:要求被代理类至少实现一个接口。
CGLIB代理:基于父类的动态代理技术:动态生成一个要代理的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,
顺势织入横切逻辑,对方法进行增强
CGLIB是高效的代码生成包,底层依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强。
JDK动态代理:
原理:
Proxy.newProxyInstance(classLoader, interfaces, invokehandler)生成接口对应的具体类(反编译工具jd-gui),这个类继承Proxy,实现了具体类,有相同的方法public final void xxx()
该相同的方法里通过this.h.invoke(this, m3, null)调用重写后的invoke方法,m3是具体对象的方法引用,重写后的invoke方法可以调用m3。
interfaces是这个具体类实现的接口。比如accountService.getClass().getInterfaces()中的accountService一般是具体实现了接口AccountService的类的对象。
使用:
将service的一些通用操作抽离出来,放到invoke方法里
public AccountService createAccountServiceJdkProxy() {
AccountService accountServiceProxy = (AccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
transactionManager.beginTransaction();// 1.开启事务
result = method.invoke(accountService, args);// 2.业务操作
transactionManager.commit();// 3.提交事务
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback();// 4.回滚事务
} finally {
transactionManager.release();// 5.释放资源
}
return result;
}
});
return accountServiceProxy;
}
CGLIB代理:
public AccountService createAccountServiceCglibProxy(){
// 编写cglib对应的API来生成代理对象进行返回
// 参数1 : 目标类的字节码对象
// 参数2: 动作类,当代理对象调用目标对象中原方法时,那么会执行intercept方法
AccountService accountServiceproxy = (AccountService) Enhancer.create(accountService.getClass(), new MethodInterceptor() {
@Override // o : 代表生成的代理对象 method:调用目标方法的引用 objects:方法入参 methodProxy:代理方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
try {
transactionManager.beginTransaction();
method.invoke(accountService, objects);
transactionManager.commit();
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback();
} finally {
transactionManager.release();
}
return null;
}
});
return accountServiceproxy;
}
什么是AOP:
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程
AOP 是 OOP(面向对象编程) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,
提高程序的可重用性,同时提高了开发的效率。
好处:
1. 在程序运行期间,在不修改源码的情况下对方法进行功能增强
2. 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
3. 减少重复代码,提高开发效率,便于后期维护
AOP底层实现:
实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。
在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
AOP相关术语:
* Target(目标对象):代理的目标对象
* Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类
* Joinpoint(连接点):所谓连接点是指那些可以被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。可以增强的方法。
* Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。真正被拦截增强的方法。
* Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。分类:前置通知、后置通知、异常通知(catch)、最终通知finally、环绕通知(前面的统称)。增强的业务逻辑。
* Aspect(切面):是切入点和通知(引介)的结合
* Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
开发明确事项:
开发阶段:
1. 编写核心业务代码(目标类的目标方法) 切入点
2. 把公用代码抽取出来,制作成通知(增强功能方法) 通知
3. 在配置文件中,声明切入点与通知间的关系,即切面
运行阶段:
Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,
根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
底层代理实现:
在 Spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
当bean实现接口时,会用JDK代理模式
当bean没有实现接口,用cglib实现( 可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
基于XML的AOP开发:
流程:
1. 创建java项目,导入AOP相关坐标
2. 创建目标接口和目标实现类(定义切入点)
3. 创建通知类及方法(定义通知)
4. 将目标类和通知类对象创建权交给spring
5. 在核心配置文件中配置织入关系,及切面
<bean id="accountService" class="com.lagou.service.impl.AccountServiceImpl"></bean>
<bean id="myAdvice" class="com.lagou.advice.MyAdvice"></bean> // 通知类
<aop:config>
<aop:aspect ref="myAdvice"> //配置目标类的transfer方法执行时,使用通知类的before方法进行前置增强
<aop:before method="before" pointcut="execution(public void com.lagou.service.impl.AccountServiceImpl.transfer())"></aop:before>
</aop:aspect>
</aop:config>
6. 编写测试代码
XML配置AOP详解:
切点表达式:
格式:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
访问修饰符可以省略
返回值类型、包名、类名、方法名可以使用星号 * 代替,代表任意
包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类
参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表
示例:
execution(public void com.lagou.service.impl.AccountServiceImpl.transfer())
execution(void com.lagou.service.impl.AccountServiceImpl.*(..))
execution(* com.lagou.service.impl.*.*(..))
execution(* com.lagou.service..*.*(..))
切点表达式抽取:
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替pointcut 属性来引用抽取后的切点表达式。
<aop:pointcut id="myPointcut" expression="execution(* com.lagou.service..*.*(..))"> </aop:pointcut>
<aop:aspect ref="myAdvice">
<aop:before method="before" pointcut-ref="myPointcut"></aop:before>
</aop:aspect>
通知类型:
格式:
<aop:通知类型 method=“通知类中方法名” pointcut=“切点表达式"></aop:通知类型>
参数:
前置通知<aop:before> 用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知<aop:afterReturning>用于配置后置通知。指定增强的方法在切入点方法之后执行
异常通知<aop:afterThrowing> 用于配置异常通知。指定增强的方法出现异常后执行
最终通知<aop:after> 用于配置最终通知。无论切入点方法执行时是否有异常,都会执行
环绕通知<aop:around> 用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行。通常情况下,环绕通知都是独立使用的
执行顺序的变化:
spring4 + springboot1下:
around、before、service、around、after、afterReturning
around、before、after、afterThrowing
spring5 + springboot2下:
around、before、service、afterReturning、after、around
around、before、afterThrowing、after
环绕通知:
public Object around(ProceedingJoinPoint pjp){ // Proceeding JoinPoint : 正在执行的连接点:切点
Object proceed = null;
try {
System.out.println("前置通知执行了");
proceed = pjp.proceed(); // 切点方法执行
System.out.println("后置通知执行了");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知执行了");
}finally {
System.out.println("最终通知执行了");
}
return proceed;
}
AOP注解开发:
流程:
1. 创建java项目,导入AOP相关坐标
2. 创建目标接口和目标实现类(定义切入点)
3. 创建通知类(定义通知)
4. 将目标类和通知类对象创建权交给spring
5. 在通知类中使用注解配置织入关系,升级为切面类
@Component
@Aspect // 升级为切面类: 配置关系
public class MyAdvice {
@Before("execution(* com.lagou..*.*(..))")
public void before() {
System.out.println("前置通知...");
}
}
6. 在配置文件中开启组件扫描和 AOP 的自动代理
<context:component-scan base-package="com.lagou"/>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
7. 编写测试代码
切点表达式:
表达式抽取:
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(* com.lagou..*.*(..))")
public void myPoint(){}
@Before("MyAdvice.myPoint()")
public void before() {
System.out.println("前置通知...");
}
}
通知类型:
格式:
@通知注解(“切点表达式")
常见:
前置通知@Before 用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知@AfterReturning 用于配置后置通知。指定增强的方法在切入点方法之后执行
异常通知@AfterThrowing 用于配置异常通知。指定增强的方法出现异常后执行
最终通知@After 用于配置最终通知。无论切入点方法执行时是否有异常,都会执行
环绕通知@Around 用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行
执行顺序:
当前四个通知组合在一起时,执行顺序如下:
@Before -> @After -> @AfterReturning(如果有异常:@AfterThrowing)
纯注解配置:
@EnableAspectJAutoProxy 替代 <aop:aspectj-autoproxy />
Spring JDBCTemplate:
概念:
JdbcTemplate是spring框架中提供的一个模板对象,是对原始繁琐的Jdbc API对象的简单封装。
类似DbUtil
常用方法:
JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);
int update(); 执行增、删、改语句
T queryForObject();查询一个
List<T> query(sql,new BeanPropertyRowMapper<>()); 查询多个
配置文件:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
示例:
String sql = "update account set name = ?,money = ? where id = ?";
jdbcTemplate.update(sql, account.getName(),account.getMoney(),account.getId());
Spring的事务:
介绍:
Spring的事务控制可以分为编程式事务控制和声明式事务控制。
编程式
开发者直接把事务的代码和业务代码耦合到一起,在实际开发中不用。
声明式
开发者采用配置的方式来实现的事务控制,业务代码与事务代码实现解耦合,使用的AOP思想。
编程式事务控制相关对象:
PlatformTransactionManager:
概念:
PlatformTransactionManager接口,是spring的事务管理器,里面提供了我们常用的操作事务的方法。
常用方法:
TransactionStatus getTransaction(TransactionDefinition definition); 获取事务的状态信息,事务管理器通过读取事务定义参数进行事务管理,然后会产生一系列的事务状态。
void commit(TransactionStatus status); 提交事务
void rollback(TransactionStatus status); 回滚事务
注意:
* PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类。
* Dao层技术是jdbcTemplate或mybatis时:DataSourceTransactionManager
* Dao层技术是hibernate时:HibernateTransactionManager
* Dao层技术是JPA时:JpaTransactionManager
TransactionDefinition:
概念:
TransactionDefinition接口提供事务的定义信息(事务隔离级别、事务传播行为等等)
常用方法:
int getIsolationLevel() 获得事务的隔离级别
int getPropogationBehavior() 获得事务的传播行为
int getTimeout() 获得超时时间,默认值是-1,没有超时限制。如果有,以秒为单位进行设置
boolean isReadOnly() 是否只读,建议查询时设置为只读
事务隔离级别:
设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读(幻读)。
* ISOLATION_DEFAULT 使用数据库默认级别
* ISOLATION_READ_UNCOMMITTED 读未提交
* ISOLATION_READ_COMMITTED 读已提交
* ISOLATION_REPEATABLE_READ 可重复读
* ISOLATION_SERIALIZABLE 串行化
事务传播行为:
事务传播行为指的就是当一个业务方法【被】另一个业务方法调用时,应该如何进行事务控制。
REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)确保有事务。
SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW 新建事务,如果当前在事务中,把当前事务挂起
NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER 以非事务方式运行,如果当前存在事务,抛出异常
NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行
REQUIRED 类似的操作
TransactionStatus:
概念:
TransactionStatus 接口提供的是事务具体的运行状态。
常用方法:
boolean isNewTransaction() 是否是新事务
boolean hasSavepoint() 是否是回滚点
boolean isRollbackOnly() 事务是否回滚
boolean isCompleted() 事务是否完成
配置:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
使用:
private PlatformTransactionManager transactionManager;
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setReadOnly(false);
def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ); // 设置事务隔离级别
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def); // 配置事务管理器,其中transactionManager为具体子类
try {
accountDao.out(outUser, money);// 转账
accountDao.in(inUser, money);
transactionManager.commit(status);// 提交事务
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback(status);// 回滚事务
}
声明式事务控制:
介绍:
在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。底层采用AOP思想来实现的。
明确事项:
核心业务代码(目标对象)
事务增强代码(Spring已提供事务管理器))
切面配置(切面如何配置?)
流程:
1. 引入tx命名空间
2. 事务管理器通知配置
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/> //任意方法都走该配置
</tx:attributes>
</tx:advice>
3. 事务管理器AOP配置
<aop:config>
<!--切面配置-->
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.serivce..*.*(..))"></aop:advisor>
</aop:config>
4. 测试事务控制转账业务代码
事务参数的配置详解:
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
* name:切点方法名称
* isolation:事务的隔离级别
* propogation:事务的传播行为
* timeout:超时时间
* read-only:是否只读
CRUD常用配置
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*"/> //其他走默认配置。
</tx:attributes>
基于注解的声明式事务控制:
流程:
1. 修改service层,增加事务注解,可以在单个方法上指定,也可以在类上指定。
@Transactional(propagation = Propagation.REQUIRED, isolation =Isolation.REPEATABLE_READ, timeout = -1, readOnly = false)
2. 修改spring核心配置文件,开启事务注解支持
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven/> <!--事务的注解支持-->
纯注解:
@Configuration // 声明为spring配置类
@ComponentScan("com.lagou") // 扫描包
@Import(DataSourceConfig.class) // 导入其他配置类
@EnableTransactionManagement // 事务的注解驱动
public class SpringConfig {
@Bean //用于sql语句,与返回类名一致,首字母小写。
public JdbcTemplate getJdbcTemplate(@Autowired DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean("transactionManager")
public PlatformTransactionManager getPlatformTransactionManager(@Autowired DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
ApplicationContext应用上下文web环境获取方式:
问题:
应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件) 方式获取的,但是每次从容器中获得Bean时都要编写 new ClasspathXmlApplicationContext(spring配置文件) 。
context.getBean()获取对象然后调用方法。
这样的弊端是配置文件加载多次,应用上下文对象创建多次。
解决:
在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,
在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。
Spring提供获取应用上下文的工具:
上面的分析不用手动实现,Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,
提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。
我们要做的事情:
1. 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
<context-param>
<param-name>contextConfigLocation</param-name> //指定名称,读取配置
<param-value>classpath:applicationContext.xml</param-value>•
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
2. 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Object obj = applicationContext.getBean("id");
SpringMVC:
概述:
SpringMVC 是一种基于 Java 的实现 MVC 设计模式的轻量级 Web 框架,属于SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 中。
SpringMVC 已经成为目前最主流的MVC框架之一,并且随着Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。
它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持 RESTful 编程风格的请求。
SpringMVC的框架就是封装了原来Servlet中的共有行为;例如:参数封装,视图转发等。
流程:
1. 创建web项目,导入SpringMVC相关坐标
2. 配置SpringMVC分配控制器 DispathcerServlet
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value> //读取配置文件
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
3. 编写Controller类和视图页面
html文件放到WEB-INF目录下可以被controller返回,如/WEB-INF/pages/index.jsp。路径不对会返回404。
默认放到webapp目录下直接可以访问,无需配置。
4. 使用注解配置Controller类中业务方法的映射地址
@Controller
public class UserController {
@RequestMapping("/quick")
public String quick() {
System.out.println("quick running.....");
return "/WEB-INF/pages/success.jsp"; //进行了请求转发。
}
}
5. 配置SpringMVC核心文件 spring-mvc.xml
<context:component-scan base-package="com.lagou.controller"/>
6.启动:
intellij添加configuration,指定tomcat目录地址(tomcat版本也会导致404),指定Deployment
选war不带exploded(不展开war包),指定URL。
选war exploded,注意展开后的路径放在tomcat目录下webapps/manager,是否和其他项目重复,先清空目录。
执行流程:
1. 用户发送请求至分配控制器DispatcherServlet。
2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3. 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4. DispatcherServlet调用HandlerAdapter处理器适配器。
5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6. Controller执行完成返回ModelAndView。
7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
9. ViewReslover解析后返回具体View。
10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11. DispatcherServlet将渲染后的视图响应响应用户。
组件解析:
1. 前端控制器:DispatcherServlet
用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。
2. 处理器映射器:HandlerMapping
HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3. 处理器适配器:HandlerAdapter
通过 HandlerAdapter 对handler进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
4. 处理器:Handler【开发者编写】
它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到Handler。由Handler 对具体的用户请求进行处理。
5. 视图解析器:ViewResolver
View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
6. 视图:View 【开发者编写】
SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。
一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
配置:
<mvc:annotation-driven></mvc:annotation-driven> // 处理器映射器和处理器适配器功能增强,支持json的读写
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> //视图解析器的相关配置
<property name="prefix" value="/WEB-INF/pages/"></property> //配置后controller返回逻辑名称即可解析具体地址
<property name="suffix" value=".jsp"></property>
</bean>
注解解析:
@Controller
SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中
注解类后,开启扫描<context:component-scan base-package="com.lagou.controller"/> //一般配置为controller级,因为spring-mvc容器为spring的子容器,只扫对应的controller层
@RequestMapping
作用:
用于建立请求 URL 和处理请求方法之间的对应关系
位置:
1.类上:请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头。它出现的目的是为了使我们的URL可以按照模块化管理。/User/xxx/
2.方法上:请求URL的第二级访问目录,和一级目录组成一个完整的 URL 路径。
属性:
1.value:用于指定请求的URL。它和path属性的作用是一样的
2.method:用来限定请求的方式
3.params:用来限定请求参数的条件
例如:params={"accountName"} 表示请求参数中必须有accountName,pramss={"money!100"} 表示请求参数中money不能是100
请求:
请求参数类型:
SpringMVC可以接收如下类型的参数:
1.基本类型参数
2.对象类型参数
3.数组类型参数
4.集合类型参数
获取基本类型参数:
Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。并且能自动做类型转换;自动的类型转换是指从String向其他类型的转换。
示例:
public String simpleParam(Integer id,String username) {}
获取对象类型参数:
Controller中的业务方法参数的POJO属性名与请求参数的name一致,参数值会自动映射匹配。
示例:
public String pojoParam(User user){}
中文乱码过滤器:
当post请求时,数据会出现乱码,我们可以设置一个过滤器来进行编码的过滤。
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
获取数组类型参数:
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。name=xxx&name=yyy
示例:
public String arrayParam(Integer[] ids){}
获取集合(复杂)类型参数:
获得集合参数时,要将集合参数包装到一个POJO中才可以。
public class QueryVo {
private String keyword;
private User user;
private List<User> userList;
private Map<String, User> userMap;
}
<form action="${pageContext.request.contextPath}/user/queryParam" method="post">
<input type="text" name="user.id" placeholder="编号">
<input type="text" name="user.username" placeholder="姓名"><br>
<input type="text" name="userList[0].id" placeholder="编号">
<input type="text" name="userList[0].username" placeholder="姓名"><br>
<input type="submit" value="复杂类型">
</form>
自定义类型转换器:
介绍:
SpringMVC 默认已经提供了一些常用的类型转换器;例如:客户端提交的字符串转换成int型进行参数设置,日期格式类型要求为:yyyy/MM/dd 不然的话会报错,
对于特有的行为,SpringMVC提供了自定义类型转换器方便开发者自定义处理。
示例:
public class DateConverter implements Converter<String, Date> {
public Date convert(String dateStr) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");//将日期字符串转换成日期对象 返回
Date date = null;
try {
date = format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
配置spring-mvc.xml
<!--处理器映射器和适配器增强-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotationdriven> //引用后就自动生效了
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.lagou.converter.DateConverter"></bean>
</set>
</property>
</bean>
业务函数
@RequestMapping("/converterParam")
public String converterParam(Date birthday) {
System.out.println(birthday);
return "success";
}
相关注解:
@PathVariable
获取请求路径中的参数
@RequestParam
当请求的参数name名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定,这时required=false
public String findByPage(@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "5") Integer pageSize) {}
@RequestHeader
获取请求头的数据。
public String requestHead(@RequestHeader("cookie") String cookie)
@CookieValue
获取cookie中的数据。
public String cookieValue(@CookieValue("JSESSIONID") String jesessionId)
@RequestBody
获取请求体内容
@ReuqestAttribute
获取其他handler设置的request.setAttribute()
@MatrixVariable
获取矩阵变量,/{path};xx=yy,aa=bb
注意默认urlPathHelper的解析移除了矩阵变量,需要setRemoveSemicolonContent(false)
获取Servlet相关API:
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:
这三个任选其中几个都可以。
示例:
public String servletAPI(HttpServletRequest request, HttpServletResponse response, HttpSession session)
响应:
响应方式:
页面跳转:
1. 返回字符串逻辑视图
直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转到指定页面
2. void原始ServletAPI
通过request、response对象实现响应,如response.getWriter().write("拉勾网");或者forward,或者sendRedirect
3. ModelAndView
包含了model(存数据,可以塞数据)和view(展示)
方式一:在Controller中方法创建并返回ModelAndView对象,并且设置视图名称
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("username", " lagou");//设置模型数据
modelAndView.setViewName("success"); //设置视图名称
return modelAndView;
方式二:在Controller中方法形参上直接声明ModelAndView,无需在方法中自己创建,在方法中直接使用该对象设置视图,同样可以跳转页面
public ModelAndView returnModelAndView2(ModelAndView modelAndView) {
modelAndView.addObject("username", "lagou");//设置模型数据
modelAndView.setViewName("success"); //设置视图名称,即逻辑视图名称
return modelAndView;
}
最新返回数据给前端:
<span style="color:red" th:inline="text">[[${tg_billing_message}]]</span>
返回数据:
1. 直接返回字符串数据(借助response对象)
2. 将对象或集合转为json返回(任务二演示)
转发:
企业开发我们一般使用返回字符串逻辑视图实现页面的跳转,这种方式其实就是请求转发。所以也可以写成:forward转发。
如果用了forward:则路径必须写成实际视图url,不能写逻辑视图。使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法。
如:
return "forward:/WEB-INF/pages/success.jsp"; //要想携带值可以用内置参数Model model.addAttribute(),不适合redirect(底层是request域)
return "forward:/User/findAll";
重定向:
return "redirect:/index.jsp";可以不写虚拟目录,springMVC框架会自动拼接,并且将Model中的数据拼接到url地址上
注解:
@SessionAttributes
介绍:
如果在多个请求之间共用数据,则可以在控制器类上标注一个 @SessionAttributes,配置需要在session中存放的数据范围,Spring MVC将存放在model中对应的数据暂存到 HttpSession 中。
@SessionAttributes只能定义在类上
示例:
@Controller
@SessionAttributes("username") //向request域存入的key为username时,同步到session域中
public class UserController {
@RequestMapping("/forward")
public String forward(Model model) { //利用了model,但无法在多个请求之间共享。
model.addAttribute("username", "子慕");
return "forward:/WEB-INF/pages/success.jsp";
}
}
静态资源访问的开启:
当有静态资源需要加载时,比如jquery文件,通过谷歌开发者工具抓包发现,没有加载到jquery文件
原因是SpringMVC的前端控制器DispatcherServlet的url-pattern配置的是 /(缺省),代表对所有的静态资源都进行处理操作,这样就不会执行Tomcat内置的DefaultServlet处理
通过以下两种方式指定放行静态资源:
方式一
<!--在springmvc配置文件中指定放行资源-->
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/img/**" location="/img/"/>
方式二
<!--在springmvc配置文件中开启DefaultServlet处理静态资源-->
<mvc:default-servlet-handler/>
JSON数据支持:
介绍:
Springmvc默认用MappingJackson2HttpMessageConverter对json数据进行转换,需要加入jackson的包;同时使用<mvc:annotation-driven />
@RequestBody:
介绍:
该注解用于Controller的方法的形参声明,当使用ajax提交并指定contentType为json形式时,通过HttpMessageConverter接口转换为对应的POJO对象。
示例:
@RequestMapping(value = "/ajaxRequest")
public void ajaxRequest(@RequestBody List<User>list) {
System.out.println(list);
}
@ResponseBody:
介绍:
该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端。
示例:
@RequestMapping(value = "/ajaxRequest")
@ResponseBody //注解后接口,还需要导入依赖
public List<User> ajaxRequest(@RequestBody List<User> list) {
return list; //对于普通的字符串,不再认为是逻辑视图,而是直接返回。
}
RESTful风格:
@PathVariable:
用来接收RESTful风格请求地址中占位符的值,如
public String get(@PathVariable Integer id) {
return "get:" + id;
}
@RestController:
介绍:
RESTful风格多用于前后端分离项目开发,前端通过ajax与服务器进行异步交互,我们处理器通常返回的是json数据所以使用@RestController来替代@Controller和@ResponseBody两个注解。
示例:
@RestController
public class RestFulController{
@GetMapping(value = "/user/{id}"){} //相当于 @RequestMapping(value = "/user/{id}",method = RequestMethod.GET) + @ResponseBody
}
文件上传:
文件上传三要素(formv表单属性):
表单项 type="file"
表单的提交方式 method="POST"(事实上get方法也能传body,只是1.GET只是用于检索,2.GET方法应该具有幂等性,如果携带了body则创建资源不能保证幂等性)
表单的enctype属性是多部分表单形式 enctype=“multipart/form-data"
文件上传原理:
enctype=“multipart/form-data"请求正文内容就变成多部分形式,文件和其他输入框分开发送
单文件上传:
配置文件上传解析器
<!--文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5242880"></property><!-- 设定文件上传的最大值为5MB,5*1024*1024 -->
<property name="maxInMemorySize" value="40960"></property><!-- 设定文件上传时写入内存的最大值,如果小于这个参数不会生成临时文件,默认为10240 -->
</bean>
接受文件:
@RequestMapping("/fileUpload")
public String fileUpload(String username, MultipartFile filePic) throws IOException {
String originalFilename = filePic.getOriginalFilename();
filePic.transferTo(new File("d:/upload/"+originalFilename));
return "success";
}
多文件上传:
@RequestMapping("/filesUpload")
public String filesUpload(String username, MultipartFile[] filePic) throws IOException {
for (MultipartFile multipartFile : filePic) {
String originalFilename = multipartFile.getOriginalFilename();
multipartFile.transferTo(new File("d:/upload/" + originalFilename));
}
return "success";
}
异常处理:
介绍:
在Java中,对于异常的处理一般有两种方式:一种是当前方法捕获处理(try-catch),这种处理方式会造成业务代码和异常处理代码的耦合。
另一种是自己不处理,而是抛给调用者处理(throws),调用者再抛给它的调用者,也就是一直向上抛。在这种方法的基础上,衍生出了SpringMVC的异常处理机制。
使用:
系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc分配控制器交由异常处理器进行异常处理
自定义异常处理器:
1. 创建异常处理器类实现HandlerExceptionResolver
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("error", ex.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}
2. 配置异常处理器
容器化,可以通过注解@Component或者xml配置bean。
3. 编写异常页面
error匹配逻辑视图即可
4. 测试异常跳转
web的处理异常机制:
在web.xml配置如下:
<!--处理500异常-->
<error-page>
<error-code>500</error-code>
<location>/500.jsp</location>
</error-page>
<!--处理404异常-->
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
请求的生命周期:
1. Filter 过滤请求处理
2. Interceptor 拦截请求处理
3. Aspect(切面) 拦截处理请求(这里不赘述)
4. 对应的 HandlerAdapter 处理请求
5. Aspect(切面) 拦截响应请求(这里不赘述)
6. Interceptor 拦截响应处理
7. Interceptor 的最终处理
8. Filter 过滤响应处理
这是请求正常的生命周期,发生错误时,会额外走ExceptionHandler。
@ExceptionHandler(Exception.class)
public void handleException(HttpServletRequest req, HttpServletResponse resp) throws Exception {
req.setAttribute("originalUri", req.getRequestURI());
req.getRequestDispatcher("/error").forward(req, resp);
}
过滤器:
自定义实现:
implements Filter
注册到容器中
拦截器:
概念:
Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(InterceptorChain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
拦截器也是AOP思想的具体实现(不修改代码实现前后增强)。
拦截器和过滤器区别
过滤器 拦截器
使用范围 servlet规范的一部分,任务JAVA web工程都可以使用 属于SpringMvc框架
拦截范围 /*对所有的资源进行拦截,只能在请求进来时进行处理,对请求和响应进行包装
只拦截控制器的方法,如果访问的是jsp、html等资源不会拦截,以在controller对请求处理之前或之后被调用,也可以在渲染视图呈现给用户之后调用
实现原理 基于函数回调 基于 Java 反射 和 动态代理。
步骤:
1. 创建拦截器类实现HandlerInterceptor接口
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return true; //继续执行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
//进行视图返回渲染之前执行,可以对ModelAndView操作
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("afterCompletion1"); // 在视图渲染后执行
}
}
2. 配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.lagou.interceptor.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
3. 测试拦截器的拦截效果
拦截器链:
里注册的顺序就代表拦截器执行的顺序。
HTML前端开发:
webapp目录:
概述:
用来存放前端的静态资源,如jsp js css。
搭建:
idea添加src/main/webapp目录、src/main/WEB-INF/web.xml文件
使用:
在webapp目录下添加子目录或者html文件
启动:
访问localhost:8080/子目录/xxx.html即可
JS文件访问:
使用相对路径: ../
运维部署:
启动流程:
依赖tomcat服务
SSM(spring+springMVC+mybatis)整合:
1.整合mybatis单独使用
开发:
Dao层使用注解@Select("select * from account")标记抽象方法,getMapper返回动态代理对象,调用对应的方法。
SqlMapConfig.xml配置数据库连接 + 扫描包:<mappers><package name="com.czl.dao"/></mappers>
测试:
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
AccountDao accountDao = sqlSession.getMapper(AccountDao.class); ORM封装,直接获取到对象。不用queryRunner或者jdbcTemplate
2.编写spring单独使用
开发:
service层impl使用@Service注入到IOC容器
applicationContext.xml配置扫描service层
测试:
通过ContextConfiguration("classpath:applicationContext.xml")来加载配置,通过@Autowired来获取实例对象。
3.spring整合mybatis
开发:
需要获取dao层对象,关键是将mybatis接口代理对象的创建权交给spring管理(sqlSessionFactory、sqlSession、dao对象变为Bean)
这一步不能使用注解,因为最终需要通过web listener导入applicationContext.xml这个配置,单独的Bean需要xml配置
第一步Bean化dataSource
第二步SqlSessionFactory,参数是dataSource。SqlSessionFactory创建交给spring的IOC容器
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/><!--数据库环境配置-->
<property name="typeAliasesPackage" value="com.czl.domain"/><!--类型别名配置,用于ResultType,默认小写-->
</bean>
第三步映射接口扫描配置,由spring创建代理对象,交给IOC容器
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.czl.dao"/>
</bean>
测试:沿用即可
4.编写springMVC在ssm环境中可以单独使用
开发:
配置web.xml,如DispatcherServlet
controller层使用@Controller + @RequestMapping配置
配置spring-mvc.xml扫描controller层<context:component-scan base-package="com.czl.controller"/>
其他配置如json、静态文件、视图解析等
5.spring整合springMVC
spring和web容器整合,让web容器启动的时候自动加载spring配置文件,web容器销毁的时候spring的ioc容器也销毁。
ContextLoaderListener加载
6.spring配置声明式事务
引入tx命名空间
在applicationContext.xml里Bean事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
通知配置 + 切面配置使用@Transactional,开启注解即可<tx:annotation-driven/>
ssm框架切换springboot:
1.根据springboot创建项目,选择mybatis和mysql driver。
2.导入连接池
3.dao层、service层、controller层编写
4.(关键)配置:
配置数据库信息(之前放到applicationContext.xml,然后由web.xml加载,现在实例化Bean也自动管理了,再之前spring阶段通过import配置类的方式手动管理)
配置扫描dao层,在项目启动类上(之前在spring-mvc.xml上配置)
之前剩下的比如controller层扫描,现在都是@Controller+自动扫描
事务这块?
SpringBoot:
spring优缺点分析:
优点:
spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。
无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单 的Java对象(Plain Old Java Object,POJO)实现了EJB的功能
缺点:
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配 置。
Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入 了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。
所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编 写配置挤占了编写应用程序逻辑的时间。
和所有框架一样,Spring实用,但与此同时它要求的回报也不少。
除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,
一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度
概念:
约定优于配置(Convention over Configuration),又称按约定编程,是一种软件设计范式。
本质上是说,系统、类库或框架应该假定合理的默认值,而非要求提供不必要的配置。比如说模型中有一个名为User的类,那么数据库中对应的表就会默认命名为user。
只有在偏离这一个约定的时候,例如想要将该表命名为person,才需要写有关这个名字的配置。
SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑 业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,
从而大大提高了开发的效率,一定程度上缩短了项目周期。
简化spring技术栈的快速开发脚手架。
优点:
对主流开发框架的无配置集成;(内嵌web服务器)
自动starter依赖,简化构建配置,简单、快速、方便地搭建项目,极大提高了开发、部署效率
提供生产级别的监控、健康检查及外部化配置。
特性:
依赖管理:
项目pom.xml文件有两个核心依赖,分别是spring-boot-starter-parent和spring-boot-starter-web
spring-boot-starter-parent依赖
1.将spring-boot-starter-parent依赖,同时作为Spring Boot项目的统一父项目来进行依赖管理,并将项目版本号统一为2.2.2.RELEASE,该版本号根据实际开发需求是可以修改的
2.spring-bootstarter-parent父项目为spring-boot-dependencies,定义了几乎所有库的版本信息
3.spring-boot-dependencies项目对一些常用技术框架的依赖文件进行了统一版本号管理,例如activemq、spring、tomcat等,都有与Spring Boot 2.2.2版本相匹配的版本,
这也是pom.xml引入依赖文件不需要标注依赖文件版本号的原因。
spring-boot-starter-web依赖
提供Web开发场景所需的底层所有依赖
当然,这些引入的依赖文件的版本号还是由spring-boot-starter-parent父依赖进行的统一管理。
其他依赖启动器:
Spring Boot除了提供有上述介绍的Web依赖启动器外,还提供了其他许多开发场景的相关依赖,我们可以打开Spring Boot官方文档,搜索“Starters”关键字查询场景依赖启动器
列出了Spring Boot官方提供的部分场景依赖启动器,这些依赖启动器适用于不同的场景开发,使用时只需要在pox.xml文件中导入对应的依赖启动器即可。
需要说明的是,Spring Boot官方并不是针对所有场景开发的技术框架都提供了场景启动器,例如数据库操作框架MyBatis、阿里巴巴的Druid数据源等,Spring Boot官方就没有提供对应的依赖启动器。
为了充分利用Spring Boot框架的优势,在Spring Boot官方没有整合这些技术框架的情况下,MyBatis、Druid等技术框架所在的开发团队主动与Spring Boot框架进行了整合,实现了各自的依赖启动器,
例如mybatis-spring-boot-starter、druid-spring-boot-starter等。我们在pom.xml文件中引入这些第三方的依赖启动器时,切记要配置对应的版本号
自动配置:
概述:
springboot的自动配置,指的是springboot,会自动将一些配置类的bean注册进ioc容器,我们可以需要的地方使用@autowired或者@resource等注解来使用它。
“自动”的表现形式就是我们只需要引我们想用功能的包,相关的配置我们完全不用管,springboot会自动注入这些配置bean,我们直接使用这些bean即可。
一般引入第三方库依赖的步骤:引入依赖 + 配置依赖。
比如spring自动配置了tomcat、springmvc及常用组件、默认springApplication同级目录下类扫描、默认配置值并绑定到类上、按需加载bean
原理:
入口是@SpringBootApplication
这个注解由三个核心注解组成:
@SpringBootConfiguration // 标明该类为配置类,注解表示Spring Boot配置类,底层是@Configuration //配置IOC容器
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan(@ComponentScan( // 包扫描器
excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})}
)
1.@SpringBootConfiguration注解
@SpringBootConfiguration注解内部有一个核心注解@Configuration
该注解是Spring框架提供的,表示当前类为一个配置类(XML配置文件的注解表现形式),并可以被组件扫描器扫描。
由此可见,@SpringBootConfiguration注解的作用与@Configuration注解相同,都是标识一个可以被组件扫描器扫描的配置类,只不过@SpringBootConfiguration是被Spring Boot进行了重新封装命名而已
2.@EnableAutoConfiguration注解
@EnableAutoConfiguration注解表示开启自动配置功能,该注解是Spring Boot框架最重要的注解,也是实现自动化配置的注解。
一个组合注解:
@AutoConfigurationPackage注解+@Import({AutoConfigurationImportSelector.class})
Spring 中有很多以Enable开头的注解,其作用就是借助@Import来收集并注册特定场景相关的bean,并加载到IoC容器。
@EnableAutoConfiguration就是借助@Import来收集所有符合自动配置条件的bean定义,并加载到IoC容器。
@AutoConfigurationPackage注解
概述:
@AutoConfigurationPackage注解的功能是由@Import注解实现的,它是spring框架的底层注解,它的作用就是给容器中导入某个组件类
@AutoConfigurationPackage注解的主要作用就是将主程序类所在包及所有子包下的组件到扫描到spring容器中。
组成:
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
其中Registrar类有个方法registerBeanDefinitions扫描同级包下的所有类
@Import({AutoConfigurationImportSelector.class})
帮助springboot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器(ApplicationContext)中
通过selectImports这个方法告诉springboot都需要导入那些组件,把ImportSelector接口selectImports()方法返回的Class名称都定义为bean。
读取一个META-INF/spring.factories的文件,读取不到会表这个错误。
将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的配置类,
并加载到IOC容器中
这些自动配置类会根据具体对应的条件@Conditional来决定是否创建。
这些自动配置类一般都有具体的properties类,用于设置某些配置。
自定义配置:
方法1.用户@Bean替换组件(灵活性更大)
方法2.修改config,使得properties类的值发生变化。从而影响组件。
在Spring Boot中以自动配置类的形式进行了预先配置。因此,在Spring Boot项目中加入相关依赖启动器后,基本上不需要任何配置就可以运行程序。
当然,我们也可以对这些自动配置类中默认的配置进行更改
3.@ComponentScan注解
@ComponentScan注解具体扫描的包的根路径由Spring Boot项目主程序启动类所在包位置决定,在扫描过程中由前面介绍的@AutoConfigurationPackage注解进行解析
从而得到Spring Boot项目主程序启动类所在包的具体位置
快速入门:
方式一:
创建Spring Initializr项目,选择web/web依赖。
方式二:
1.引入spring-boot-starter-parent的parent,spring-boot-starter-web依赖
2.启动项目启动类SpringBootDemoApplication即可,内置tomacat
3.创建controller,注解。
4.使用sprint-boot-maven-plugin插件将所有东西打包成jar即可发布
最佳实践:
引入场景依赖
查看自动配置了哪些:
看代码conditional分析
配置debug=true开启自动配置报告
修改配置文件
参考官方properties文档
自己看properties类
自定义@Bean替换组件
自定义器
spring2.0介绍:
依赖:
jdk1.8以上
maven3.3以上
单元测试:
介绍:
开发中,每当完成一个功能接口或业务方法的编写后,通常都会借助单元测试验证该功能是否正确。
Spring Boot对项目的单元测试提供了很好的支持,在使用时,需要提前在项目的pom.xml文件中添加spring-boot-starter-test测试依赖启动器,可以通过相关注解实现单元测试
流程:
1.添加spring-boot-starter-test测试依赖
2.编写单元测试类和测试方法
@RunWith(SpringRunner.class) // 测试启动器,并加载Spring Boot测试注解
@SpringBootTest // 标记为Spring Boot单元测试类,并加载项目的ApplicationContext上下文
环境
class SpringbootDemoApplicationTests {
@Autowired
private DemoController demoController;
@Test // 注意使用junit5的注解,jupiter引擎
void contextLoads() {
String demo = demoController.demo();
System.out.println(demo);
}
}
常用注解(junit5):
@DisplayName
@BeforeEach每个单元测试前运行
@BeforeAll
@Tag测试类别
@Disabled
@Timeout
@Repeated重复次数
断言:
简单断言:
比如assertEquals
数组断言:
assertArrayEquals
组合断言:
assertAll(name, Executable...)
异常断言:
assertThrows断言一定会出现异常
超时断言:
assertTimeout
快速失败:
fail()
前置条件:
assumeTrue等函数,条件不成立后skip掉。(而不是失败)
嵌套测试:
@Nested
不会驱动内层的BeforeEach等方法运行,但内层的可以驱动外层的
参数化测试:
@ValueSource、@MethodSource
热部署:
介绍:
在开发过程中,通常会对一段业务代码不断地修改测试,在修改之后往往需要重启服务,有些服务需要加载很久才能启动成功,这种不必要的重复操作极大的降低了程序开发效率。
为此,SpringBoot框架专门提供了进行热部署的依赖启动器,用于进行项目热部署,而无需手动重启项目
步骤:
1.添加spring-boot-devtools热部署依赖启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
2.IEDA配置Build project automatically
或者手动ctrl + f9
全局配置文件:
概念:
全局配置文件能够对一些默认配置值进行修改。
Spring Boot使用一个application.properties或者application.yaml的文件作为全局配置文件,该文件存放在src/main/resource目录或者类路径的/config,一般会选择resource目录
application.properties配置文件
介绍:
可以在application.properties文件中定义Spring Boot项目的相关属性,当然,这些相关属性可以是系统属性、环境变量、命令参数等信息,也可以是自定义配置文件名称和位置
示例:
@Component //将当前注入属性值的Person类对象作为Bean组件放到Spring容器中,只有这样才能被@ConfigurationProperties注解进行赋值
@ConfigurationProperties(prefix = "person") //将配置文件中以person开头的属性注入到该类中
public class Person {
private int id; //id
private String name; //名称
private List hobby; //爱好
private String[] family; //家庭成员
private Map map;
private Pet pet; //宠物
// 省略属性getXX()和setXX()方法
// 省略toString()方法
}
配置:
person.id=1
person.hobby=xx,yy
person.map.k1=v1
person.pet.name=xxx
即可通过@Autowired引入对象。
application.yaml配置文件
介绍:
YAML文件格式是Spring Boot支持的一种JSON超集文件格式,相较于传统的Properties配置文件,YAML文件以数据为核心,是一种更为直观且容易被电脑识别的数据序列化格式。
application.yaml配置文件的工作原理和application.properties是一样的,只不过yaml格式配置文件看起来更简洁一些。
使用:
YAML文件的扩展名可以使用.yml或者.yaml。
application.yml文件使用 “key:(空格)value”格式配置属性,使用缩进控制层级关系。
1.value值为普通数据类型(例如数字、字符串、布尔等)
当YAML配置文件中配置的属性值为普通数据类型时,可以直接配置对应的属性值,同时对于字符串类型的属性值,不需要额外添加引号
2.value值为数组和单列集合
当YAML配置文件中配置的属性值为数组或单列集合类型时,主要有两种书写方式:缩进式写法-和行内式写法[a,b,c]。[]”还可以进一步省略,在进行属性赋值时,程序会自动匹配和校对
3.value值为Map集合和对象
当YAML配置文件中配置的属性值为Map集合或对象类型时,YAML配置文件格式同样可以分为两种书写方式:缩进式写法和行内式写法。
person:
map:
k1: v1 缩进式写法的形式按照YAML文件格式编写即可
k2: v2
person:
map: {k1: v1,k2: v2}
示例:
person:
id: 1
name: lucy
hobby: [吃饭,睡觉,打豆豆]
family: [father,mother]
map: {k1: v1,k2: v2}
pet: {type: dog,name: 旺财}
注意:
application.properties配置文件会覆盖application.yaml配置文件
配置文件属性值的注入:
概述:
如果配置属性是Spring Boot已有属性,例如服务端口server.port,那么Spring Boot内部会自动扫描并读取这些配置文件中的属性值并覆盖默认属性。
如果配置的属性是用户自定义属性,例如刚刚自定义的Person实体类属性,还必须在程序中注入这些配置属性方可生效。
使用@ConfigurationProperties注入属性:
@ConfigurationProperties(prefix = "person")
public class Person {
private int id;
public void setId(int id) {// 属性的setXX()方法
this.id = id;
}
}
使用@Value注入属性:
概述:
@Value注解是Spring框架提供的,用来读取配置文件中的属性值并逐个注入到Bean对象的对应属性中,Spring Boot框架从Spring框架中对@Value注解进行了默认继承,
所以在Spring Boot框架中还可以使用该注解读取和注入配置文件属性值。
优缺点:
优点:使用@Value注解方式需要对每一个属性注入设置,同时又免去了属性的setXX()方法。
缺点:@Value注解对于包含Map集合、对象以及YAML文件格式的行内式写法的配置文件的属性注入都不支持
示例:
@Component
public class Person {
@Value("${person.id}") // 还可以直接给id属性赋值,这点是@ConfigurationProperties不支持的
private int id;
}
自定义配置:
概述:
几乎所有的配置都可以写在application.peroperties文件中,SpringBoot会自动加载全局配置文件从而免除我们手动加载的烦恼。
但是,如果我们自定义配置文件,SpringBoot是无法识别这些配置文件的,此时就需要我们手动加载。
使用@PropertySource指定配置文件位置:
概述:
方式一:
@PropertySource注解用于指定自定义配置文件的具体位置和名称
@Component
配合@ConfigurationProperties(prefix="xxx")或者@Value()注解进行属性值注入
方式二:
@PropertySource注解用于指定自定义配置文件的具体位置和名称
@ConfigurationProperties
自定义类
@EnableConfigurationProperties(自定义类.class)
如何配置提示yml文件:
引入依赖spring-boot-configuration-processor
示例:
@Component // 自定义配置类,只有容器中的实例才可以
@PropertySource("classpath:test.properties") // 指定自定义配置文件位置和名称
@ConfigurationProperties(prefix = "test") // 指定配置文件注入属性前缀
public class MyProperties {
private int id;
private String name;
// 省略属性getXX()和setXX()方法
// 省略toString()方法
}
自定义Bean:
使用@Configuration编写自定义配置类:
概述:
在Spring Boot框架中,推荐使用配置类的方式向容器中添加和配置组件(自定义对象)
在Spring Boot框架中,通常使用@Configuration注解定义一个配置类,Spring Boot会自动扫描和识别配置类,从而替换传统Spring框架中的XML配置文件这样的配置方式。
当定义一个配置类后,还需要在类中的方法上使用@Bean注解进行组件配置,将方法的返回对象注入到Spring容器中,并且组件名称默认使用的是方法名,
当然也可以使用@Bean注解的name或value属性自定义组件的名称
示例:
@Configuration(proxyBeanMethods=true) // 定义该类是一个配置类,被Spring Boot自动扫描识别。本身也是组件。
public class MyConfig {
@Bean // 将返回值对象作为组件添加到Spring容器中,该组件id默认为方法名。
public MyService myService(){ // 该方法在proxyBeanMethods=true的情况下,会被代理,即调用该方法会执行代理后的方法,返回容器中的实例
return new MyService();
}
}
组件依赖问题:
proxyBeanMethods=false的情况下,lite mode,不会代理@Bean注解的方法,多次调用方法会返回不同的实例。
如果组件需要依赖同一个对象,那么需要proxyBeanMethods=true。
使用@Import({xxx.class})注解:
概述:
往IOC容器导入该class的无参构造实例对象,名字为全类名。
使用@Conditional系列注解:
概述:
比如ConditionalOnBean存在组件时,ConditionalOnMissingBean
示例:
@ConditionalOnBean(name="tom") //当存在tom的bean时才创建该bean
@Bean
public xxx
使用@ImportResource注解:
概述:
导入xml配置文件定义的bean
示例:
@ImportResource("classpath:beans.xml")
web开发:
springmvc自动配置:
兼容ContentNegotiatingViewResolver和BeanNameViewResolver
自动配置了静态资源,包括webjars
自动注册Converter, GenericConverter, and Formatter
支持HttpMessageConverters
自动注册MessageCodesResolver
支持index.html
自定义favicon
自动使用ConfigurableWebBindingInitializer
静态资源访问:
静态资源路径:
方式一:
类似ssm的配置,配置webapp目录。
方式二:
默认resource路径下static、public、resources、META-INF/resources路径下的静态文件,可以直接通过静态文件名后缀访问。
可以通过spring.resources.static-locations: [classpath:/path/]配置
静态资源访问前缀:
默认映射/**,请求过来的时候先判断controller是否有方法,否则尝试map到静态文件。通过spring.mvc.static-path-pattern控制。
首页:
默认寻找静态资源路径下的index.html,或者controller能处理/index
受静态资源访问前缀影响
图片favicon:
默认寻找静态资源路径下favicon.ico
受静态资源访问前缀影响
静态资源原理:
WebMvcAutoConfiguration自动配置类,内部有一个静态内部类WebMvcAutoConfigurationAdapter,读取spring.mvc和spring.web配置。
WebMvcAutoConfigurationAdapter里的addResourceHandlers方法负责添加静态mapping,映射了webjars和默认resources路径。
另一个静态内部类EnableWebMvcConfiguration里面创建了WelcomePageHandlerMapping组件,if else判断了index的处理逻辑。
模板渲染:
路径:
默认寻找templates目录下的文件,子目录通过subpath/yy的方式
引擎:
支持引擎Thymeleaf, FreeMarker, and JSPs,需要引入第三方库
Thymeleaf:
概述:
性能一般
表达式:
字面量:
文本操作:
数学运算:
使用:
引入maven依赖,自动配置了ThymeleafViewResolver等,读取templates目录下.html后缀的文件
html引入名称空间,开发代码,返回字符串,利用Model参数传值,进行渲染
视图解析原理:
handlerReturnValue方法里的selectHandler获取到视图解析器ViewNameMethodReturnValueHandler
ViewNameMethodReturnValueHandler.handleReturnValue给mavContainer(包含了model和attribute)设置viewname视图地址和重定向属性。
processDispathResult方法负责render渲染
根据viewname遍历所有的视图解析器,尝试拿到view(比如thymeleaf通过createView方式创建view)
view调用render方法
获取目标url
如果是重定向视图,调用response.setRedirect方法
如果是普通视图,读取文件,引擎渲染buffer,返回html结果
Rest映射:
表单使用REST:
概述:
表单只能提交GET、POST请求,要想提交PUT、DELETE请求,可以通过_method字段,且开启HiddenHttpMethodFilter的相关配置
配置:
spring.mvc.hiddenmethod.filter.enabled=true
原理:
HiddenHttpMethodFilter判断开启后,会将request包装,根据_method字段改为真正的PUT和DELETE请求,再给controller处理。
自定义HiddenHttpMethodFilter:
可以修改逻辑,比如改_method字段
请求映射原理:
DispatcherServlet的doService方法,调用doDispatch方法,getHandler通过遍历5个handlerMappings获取对应的handler
内置handlerMapping有RequestMappingHandlerMapping、WelcomePageHandlerMapping等
可以自定义handlerMapping来处理自定义的请求
请求参数解析原理:
DispatcherServlet的getHandlerAdapter方法遍历handlerAdapters获取handler对应的adapter,adapter.handle()方法处理handler
内置的handlerAdapters有RequestMappingHandlerAdapter等
handle方法会调用handleInternal方法->invokeHandlerMethod方法,
里面会调用setHandlerMethodArgumentResolvers方法设置参数解析器。
invokeAndHandle方法会调用getMethodArgumentValues->this.resolvers.resolveArgument方法尝试解析
resolvers有多个,分别对应请求参数,ServletApi参数(比如HttpServletRequest),复杂参数(比如map、model,会放在请求域中传递),自定义对象参数。
比如ServletModelAttributeMethodProcessor处理自定义对象参数,先创建空对象,然后一一通过converters反射将http文本转换为成员变量。
converter解析原理:
寻找对应的默认converter,能将字符串转化为目标类型,用于banding。
自定义:
继承WebMvcConfigurer类,重写addFormatters方法,registry.addConverter方法添加自定义实例,实例的convert方法可以将字符串转化为指定类型。
响应ReturnValueHandler原理:
invokeAndHandle方法调用invokeForRequest方法返回Object值后,根据返回类型判断
如果是null,直接返回
如果是字符串,
否则,returnValueHandlers.handlerReturnValue处理返回值
handlerReturnValue方法会selectHandler,获取返回值类型对应的handler,再handler.handlerReturnValue执行。
其中的writeWithMessageConverters会进行内容协商,根据请求accept、响应mediaType和支持的mediaType,选择适合的HttpMessageConverter(通过canWrite方法),然后converter.write写入。
比如对象,通过MappingJackson2HttpMessageConverter把对象转为JSON
基于请求参数的内容协商:
默认策略基于header,可以添加基于参数的策略,来确定请求的mediaType
配置:
spring.mvc.contentnegotiation.favor-parameter: true
示例:
?format=json
自定义MessageConverter:
概述:
自定义converter继承HttpMessageConverter,支持application/xxxx,这样header中accept为application/xxxx的请求即可返回。
然后继承WebMvcConfigurer类,重写extendMessageConverters方法
示例:
getSupportMediaTypes() { return MediaType.parseMediaTypes("application/xxxx")}
自定义ContentNegotiationStrategy:
概述:
默认的请求参数策略只支持xml和json。
需要扩展来支持自定义格式
示例:
ParameterContentNegotiationStrategy构造函数传入自定义格式的map,key为format的值,value为application/xxx,得到实例
继承WebMvcConfigurer类,重写configureContentNegotiation方法,添加有参构造后的ParameterContentNegotiationStrategy实例和无参的HeaderContentNegotiationStrategy
拦截器:
概述:
自定义拦截器,registry.addInterceptor方法添加,定义拦截规则
原理:
getHandler方法拿到mapperHandler,包含对应的ctonroller handler和interceptorList
mapperHandler执行applyPreHandle方法,遍历所有的interceptor顺序执行preHandler方法
执行ha.handle方法
mapperHandler执行applyPostHandle方法,倒序执行interceptor的postHandler方法
中途有任何异常都会触发triggerAfterCompletion
文件上传:
概述:
@RequestPart("value") MultipartFile[] xxx
@RequestPart("value") MultipartFile xxx
原理:
MultipartAutoConfiguration自动配置类创建了bean StandardServletMultipartResolver
doDispatch方法里
checkMulti判断请求是否文件上传(通过resolver),然后包装request。
resolver通过判断contentType
封装为MultipartHttpServletRequest
getHandler方法拿到multipartHttpServletHandler
ha.handle方法里拿到RequestPartMethodArgumentResolver,解析请求参数,拿到files
错误处理:
概述:
默认/error处理所有的错误,对于机器client,返回json,对于浏览器,返回一个空白页
定义错误页:
方法一:
可以自定义/error controller
方法二:
静态资源路径下,存放error/404.html页面
templates目录下,存放error/5xx.html用于渲染,html可以读取展示返回的json信息
方法三:
继承ErrorViewResolver类,添加Bean实例,重写resolveErrorView方法,自定义根据传入request、model等信息如何解析view,返回modelAndView,
方法四:
@ExceptionHandler(要处理的异常)+@ControllerAdvice标注的方法可以处理错误
方法五:
继承HandlerExceptionResolver,添加Bean到容器,重写resolveException方法返回modelAndView即可
@Order可以调整resolver遍历顺序。
错误处理原理:
ErrorMvcAutoConfiguration自动配置类,
添加了Bean BasicErrorController,默认监听/error路径,里面有多个方法,分别处理不同的mediaType、错误类型,比如html返回error视图
静态内部类添加了Bean defaultErrorView,bean name是error,类型是StaticView,条件是ConditionalOnMissingBean( name = {"error"}),里面有个render方法返回了html内容。
这个内部类还添加了Bean BeanNameViewResolver,可以根据bean name解析到error视图(负责解析BasicErrorController的error视图)
静态内部类DefaultErrorViewResolverConfiguration添加了DefaultErrorViewResolver组件,它的resolveErrorView方法返回error/状态码的视图
添加了Bean DefaultErrorAttributes,getErrorAttributes方法配置了返回什么JSON错误信息。
异常处理流程:
getHandlerAdapter方法拿到handler对应的adapter,然后执行ha.handle方法,发生异常会用dispatchException封装,调用processDispatchResult进行视图渲染流程
processDispatchResult方法判断有exception,调用processHandlerException方法,返回mv
遍历HandlerExceptionResolver列表,尝试resolveException处理错误返回mv。列表包括DefaultErrorAttributes、HandlerExceptionResolverComposite等。
DefaultErrorAttributes的resolveException会将异常放到request域中,返回null
HandlerExceptionResolverComposite包含三个resolver,遍历尝试解析
第一个是ExceptionhandlerExceptionResolver,使用了@ExceptionHandler注解才可以
第二是ResponseStatusExceptionResolver,使用了@ResponseStatus注解错误,并抛出了该错误才可以。
AnnotatedElementUtils.findMergedAnnotation方法判断异常是否使用了该注解。
response.sendError(statusCode, resolvedReason)调用底层api,tomcat收到error请求,进入下一轮请求
第三个是DefaultHandlerExceptionResolver解析spring底层相关的异常,比如mediaType不支持异常、参数缺失异常等
如果能拿到mv,尝试render mv,返回response。
如果都不能解析,抛出异常
如果没有任何能处理错误,最终底层servlet会发送/error请求,这时候getHandler就会拿到BasicErrorController,handle方法会调用内容协商后的方法处理。
如果是视图,里面的resolveErrorView方法会遍历errorViewResolvers,找到对应的ErrorViewResolver(默认只有DefaultErrorViewResolver)解析视图
原生组件注入:
servlet:
方式一:
继承HttpServlet,实现类,使用@WebServlet注解,同时启动类使用@ServletComponentScan(basePackages="")
方式二:
添加Bean ServletRegistrationBean
filter:
方式一:
继承Filter,使用注解WebFilter,同时启动类使用@ServletComponentScan(basePackages="")
方式二:
添加Bean FilterRegistrationBean
listener:
方式一:
继承ServletContextListener,使用注解WebListener,同时启动类使用@ServletComponentScan(basePackages="")
方式二:
添加Bean ServletListenerRegistrationBean
DispatchServlet组件原理:
DispatcherServletAutoConfiguration自动配置类,
内部静态类DispatcherServletConfiguration注入Bean DispatcherServlet,使用WebMvcProperties配置类
DispatcherServletRegistrationConfiguration注入DispatcherServletRegistrationBean,封装了DispatcherServlet和拦截的path(默认/)
而原生的ServletRegistrationBean,path不同,会根据更精确原则来匹配path。
嵌入式servlet容器:
ServletWebServerFactoryAutoConfiguration自动配置类,导入ServletWebServerFactoryConfiguration配置类。
ServletWebServerFactoryConfiguration动态判断系统导入了tomcat、还是其他web服务器的包,对应生成EmbeddedJetty、EmbeddedTomcat、EmbeddedUndertow等Factory
这些Factory负责创建对应的服务器并启动服务
如何定制化:
实现WebServerFactoryCustomizer
修改配置文件server.xxx
自定义ConfigurableServletWebServerFactory
定制化原理:
原理套路:
场景starter -> xxxAutoConfiguration -> 导入Bean组件 -> 绑定xxxProperties -> 绑定配置文件项
定制化方式:
修改配置文件
xxxxCustomizer,实现WebServerFactoryCustomizer,注解@Conponent,来修改某些配置
编写自定义的配置类,使用@Bean替换、增加组件到容器中(比如视图解析器)
实现WebMvcConfigurer,使用注解@Configuration,重写某些方法,以调用add方法添加某些组件;
@EnableWebMvc可以全面接管springMvc,不导入WebMvcAutoConfiguration组件,所有规则全部自己重新配置,实现定制和扩展功能。
数据访问:
概述:
引入依赖场景spring-boot-starter-data-xxxx
数据库:
依赖:
spring-boot-starter-data-jdbc
里面包含spring-jdbc、spring-tx、hikariCP连接池等
驱动:
导入具体数据库驱动,版本默认由starter确定,可以指定(就近依赖原则、属性就近)
自动配置:
DataSourceAutoConfiguration自动配置类,DataSourceProperties配置类读取spring.datasource下的配置
配置类PooledDataSourceConfiguration,@ConditionalOnMissingBean({DataSource.class, XADataSource.class}),当没有dataSource的时候才自动配置。
JdbcTemplateAutoConfiguration自动配置类,读取JdbcProperties配置类,主要配置jdbc相关,比如fetchSize,queryTimeout,注入组件JdbcTemplate Bean。
使用Druid连接池:
自定义方式:
1.引入druid依赖
2.使用@Bean注入自定义的DataSource
3.使用ConfigurationProperties("spring.datasource")注解DataSource返回方法,自动配置。
4.添加方法返回ServletRegistrationBean,这个Bean封装StatViewServlet示例,设置ds.setFilters("stat")即可打开druid监控。
也可以添加FilterRegistrationBean监控web应用,比如请求时间;
防火墙。
使用starter方式:
1.引入依赖druid-spring-boot-starter
自动配置类DruidDataSourceAutoConfigure使用注解@AutoConfigureBefore({DataSourceAutoConfiguration.class}),提前注入DataSource
读取spring.datasource.druid和spring.datasource配置
Import了DruidStatViewServletConfiguration和DruidWebStatFilterConfiguration等,根据配置来决定是否开启web监控和sql统计
2.配置
Redis:
流程:
1.引入依赖spring-boot-starter-redis。
2.配置url、密码、客户端类型jedis(需要依赖)等
3.使用已经注入的RedisTemplate或者stringRedisTemplate
自动配置:
RedisAutoConfiguration自动配置类,配置类RedisProperties,读取spring.redis前缀
Import LettuceConnectionConfiguration或者JedisConnectionConfiguration(根据配置)
注入了RedisTemplate<Object, Object>
注入了stringRedisTemplate
springboot整合MyBatis:
介绍:
MyBatis 是一款优秀的持久层框架,Spring Boot官方虽然没有对MyBatis进行整合,但是MyBatis团队自行适配了对应的启动器,进一步简化了使用MyBatis进行数据的操作
因为Spring Boot框架开发的便利性,所以实现Spring Boot与数据访问层框架(例如MyBatis)的整合非常简单,主要是引入对应的依赖启动器,并进行数据库相关参数设置即可
流程:
1.引入相应的starter
mybatis-spring-boot-starter。
2.编写domain层
3.编写配置文件
在application.properties配置文件中进行数据库连接配置,名字注意对应。
spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
也配置mybatis的全局属性,比如
4.dao层注解开发
编写mapper并注解@Mapper,编写sql
如果是xml配置开发,需要配置mybatis.mapper-locations=classpath: mapper/*.xml,扫描sql
mybatis.config-locations全局配置文件位置(可选)
别名mybatis.type-aliases-package=com.lagou.pojo
如果是注解开开发,通过@Select + @Options等注解即可写sql。
可以混合开发。
5.配置扫描dao层(可选,不用每个类注解@Mapper)
在Spring Boot项目启动类上添加@MapperScan("xxx")注解
如@MapperScan("com.lagou.dao")
6.测试
从容器取出dao对象调用方法即可。
一些配置:
驼峰式命名:
mybatis.configuration.map-underscore-to-camel-case=true
自动配置类:
MybatisAutoConfiguration读取配置mybatis前缀,配置了全局的属性
自动注入SqlSessionFactory和SqlSessionTemplate,Import了AutoConfiguredMapperScannerRegistrar,扫描@Mapper注解的类
整合MyBatisPlus:
概述:
内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
使用流程:
1.引入依赖mybatis-plus-boot-starter
自动配置类MybatisPlugsAutoConfiguration
使用配置类MybatisPlugsProperties,使用前缀mybatis-plugs
默认配置了mapperLocations,为classpath*:/mapper/**/*.xml(默认配置)
自动注入了SqlSessionFactory和SqlSessionTemplate,Import了AutoConfiguredMapperScannerRegistrar,会自动扫描@Mapper注解的类
2.配置:
在 application.yml 配置文件中添加数据库的相关配置
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹,@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
3.编写mapper接口
extends BaseMapper<Entity>,BaseMapper里面预定义了常见select等crud方法
示例:
entity层
实体类
Mapper层
public interface UserMapper extends BaseMapper<User> {
}
service层:
@Autowired
private UserMapper userMapper;
userList.forEach(System.out::println);
4.service层:
XXXService implement IService接口,里面定义了增删改查等方法
XXXServiceImpl extends ServiceImpl<UserMapper, User> implement XXXService,即可快速实现CRUD
分页功能:
以拦截器的方式
多数据源:
引入依赖dynamic-datasource-spring-boot-starter支持多数据源
依赖druid-spring-boot-starter使用连接池,引入后不支持。配置要对齐第二个datasource下的username栏。
示例:
spring:
datasource:
dynamic:
primary: clickhouse-sales-dws
datasource:
clickhouse-sales-dws:
driver-class-name: ru.yandex.clickhouse.ClickHouseDriver
url: jdbc:clickhouse://106.52.78.85:8123/SALES_DWS
username: root
password: Pswdforx123!
druid:
initial-size: 3
max-active: 10
min-idle: 3
Actuator监控:
概述:
Spring Boot提供用于对应用系统进行自省和监控的功能模块,基于此开发人员可以方便地对应用系统某些监控指标进行查看、统计、审计、指标收集等。
通过Actuator可以监控应用程序的Health健康信息、Info应用信息、HTTP Request跟踪信息、Metrics信息、@RequestMapping的路径信息、应用程序的各种配置信息、程序请求的次数时间等各种信息。
使用流程:
1.引入依赖spring-boot-starter-actuator。
2.配置开放监控端点
management:
endpoints:
web:
base-path: /actuator # 配置端点访问前缀
exposure:
include: '*' # 暴露所有端点,默认JMX全部开放,HTTP只开放info和health
3.访问/actuator
Endpoint:
health,默认不开启详细状态
开启详细状态:
management:
endpoint:
health:
show-details: always
定制状态:
继承AbstractHealthIndicator,重写doHealthCheck方法,可以控制status和返回详细信息
metrics指标
自定义指标:
使用注入的MeterRegistry对象,调用方法汇报指标即可。
info,展示yml配置中填写的info
endpoint端点:
默认内置了一些端点
自定义端点:
使用注解@Endpoint+@Component标注类,有两个方法,分别@ReadOperation(访问返回)和@WriteOperation(jmx调用)
Admin Server:
概述:
定制抓取其他服务的actuator情况
流程:
1.引入依赖spring-boot-admin-starter-server
2.其他服务引入spring-boot-admin-starter-client
配置spring.boot.admin.client.url=xxx
spring.boot.admin.client.instance.prefer-ip=true
httpTrace:
概述:
看到http的请求情况
流程:
1.确保开放端点。
2.增加HttpTraceRepository Bean实例:
@Configuration
public class BeanConfiguration {
@Bean
public HttpTraceRepository getHttpTraceBean() {
return new InMemoryHttpTraceRepository();
}
}
profile环境切换:
概述:
application-{prod/dev/test}.yml
配置:
spring.profiles.active=prod
环境变量设置:
-Dspring.profiles.active=prod
代码切换:
注解@Profile("prod")
环境分组:
spring.profiles.group.xxxx[0]=prod
spring.profiles.group.xxxx[0]=test
那么spring.profiles.active=xxxx会读取两个文件
外部化配置:
概述:
配置可以从yml文件、环境变量、命令行参数读取
配置文件查找顺序(前面的会被后面覆盖):
classpath(resources目录、java目录)
classpath下config目录
jar包当前目录
jar包当前目录的config目录
/config目录的直接子目录
配置文件的加载顺序:
当前jar包内部的application.properties和application.yml
当前jar包内部的application-{profile}.properties和application-{profile}.yml
引用外部jar包的application.properties和application.yml
引用外部jar包的application-{profile}.properties和application-{profile}.yml
自定义starter:
步骤:
1.新建项目starter,引用starter-autoconfigure
2.项目starter-autoconfigure编写逻辑类,依赖Properties类(注解@ConfigurationProperties标记配置文件前缀xx.yy)来使用配置
编写一个配置类xxxAutoConfiguration,@Configuration开启,@Conditional根据自定义条件开启,@EnableConfigurationProperties引入配置Properties类并放入容器中
有一个方法使用@Bean注入逻辑类,使用容器中的Properties实例来读取配置。
创建文件META-INF/spring.factories,指定自定义类的全限定名,xxxAutoConfiguration
3.点击install安装到本地maven仓库
4.其他项目引用依赖,
springboot启动流程:
1.创建springbootApplication
判断server应用类型(servlet还是响应式)
去spring.factories文件中找Boostraper
找ApplicationContextInitializers
找ApplicationListeners
2.运行springbootApplication
createBootstrapContext方法创建引导上下文
遍历BootstrapRegistryInitializer,执行initialize(new DefaultBootstrapContext())方法,完成引导启动器上下文环境设置。
configureHeadlessProperty方法设置java.awt.headless属性
getRunListeners方法获取所有的ApplicationRunListeners,遍历执行starting方法
prepareEnvironment方法
getOrCreateEnvironment创建环境ApplicationServletEnvironment
configureEnvironment读取所有的外部化配置,用以配置environment
listeners.environmentPrepared方法通知listener环境就绪
createApplicationContext方法创建IOC容器
根据应用类型创建容器
prepareContext方法准备IOC容器
保存环境信息
applyInitializers方法遍历Initializers执行initialize方法对IOC容器初始化
listeners.contextPrepared(context)通知listener容器就绪
listeners.contextLoaded(context)通知listener容器加载完成
this.refreshContext(context)方法创建容器中的所有组件
listeners.started(context, timeTakenToStartup)通知listener context启动
this.callRunners(context, applicationArguments)遍历所有runners调用run方法(执行一些事情)
ApplicationRunner和CommandLineRunner
如果有异常,调用listeners.failed通知listener失败
最终,调用listeners.ready(context, timeTakenToReady)通知listener进入进行状态
自定义事件监听组件:
实现ApplicationContextInitializer接口
实现ApplicationListener接口
实现ApplicationRunListener接口
实现ApplicationRunner接口,使用@Component注解
实现CommandLineRunner接口,使用@Component注解
在META-INF/spring-factories文件添加实现后的类