一、Spring概述
1.Spring是一个控制反转和面向切面的轻量级的开源的JavaEE框架
2.Spring可以解决企业应用开发的复杂性
3.Spring有两个核心部分:IOC和AOP
(1)IOC:控制反转,把创建对象的过程交给Spring管理
(2)AOP:面向切面编程,不修改源代码的情况下进行功能的增强和增加
4.Spring特点
(1)方便解耦,简化开发
(2)AOP编程的支持
(3)声明式事务的支持
(4)方便程序的测试
(5)方便和其他框架进行整合
(6)降低Java EE API的使用难度
(7)Java 源码是经典学习范例
二、IOC底层原理
1.xml解析、工厂模式、反射
2.使用IOC的目的:降低耦合度
原始方式:一个类调用另外一个类的对象使用
工厂模式:解耦,但不是完全解耦
3.IOC过程
4.IOC接口
(1)IOC思想
基于IOC容器完成
(2)IOC容器底层
对象工厂
(3)Spring提供IOC容器实现两种方式:(两个接口)
BeanFactory:IOC容器基本实现,是Spring 内部的使用接口,不提供开发人员进行使用
"加载配置文件的时候不会创建对象,在获取对象(也就是说在使用的时候)才会去创建对象"(感觉像懒汉式)
ApplicationContext,是BeanFactory 接口的子接口,提供更多强大的功能,一般由开发人员使用
“加载配置文件的时候会把在配置文件中的对象进行创建”(感觉像饿汉式)
(4)ApplicationContext 接口的实现类
5.IOC操作Bean管理(基于xml配置文件方式实现Spring框架中创建对象和注入属性)(参考大佬来源:blog.csdn.net/Tough_dxx/a…)
(1)基于xml方式创建对象
1.在Spring框架配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象的创建
2.在bean标签中有很多属性,常用的属性介绍:id属性——唯一标识\ class属性——类全路径
3.创建对象时默认执行无参构造方法完成对象的创建
(2)基于xml方式注入属性
先了解一下DI:依赖注入,也就是注入属性
第一种注入方式:使用set方法进行注入
第一步:
第二步:
第二种注入方式:使用有参构造进行注入
第一步:
第二步:
6.IOC操作bean管理
(1)、Spring有两种类型 bean,一种普通bean,另外一种工厂bean (FactoryBean)
(2)、普通bean:在配置文件中定义bean类型就是返回类型。
(3)、工厂bean:在配置文件定义bean类型可以和返回类型不一样
第一步创建类,让这个类作为工厂bean,实现接口FactoryBean 第二步实现接口里面的方法,在实现的方法中定义返回的bean类型
(4)自动装配
(5)外部属性文件
7.bean的作用域
- 在Spring容器中,Bean包含五种作用域:singleton、prototype、request、session、globalSession。
- 在Spring里面,默认情况下,bean是单实例
3. 如何设置多实例
1.在Spring配置文件bean标签中有属性scope用于设置单实例还是多实例
2.默认值——singleton(单实例) prototype(多实例)
单实例和多实例的区别?
单实例是在加载Spring配置文件时候就会创建单实例对象
多实例不是在加载配置文件时创建对象,而是在调用getBean方法时创建对象
8.bean的生命周期
- 生命周期——指对象从创建到销毁的过程
- bean的生命周期
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和对其他bean引用(使用set方式)
- 调用bean的初始化方法(需要进行配置)
- 获取bean对象(bean可以使用了)
- 当容器关闭时,调用bean的销毁方法destroy(需要配置销毁方法)
public class Orders {
//无参数构造
public Orders() {
System.out.println("第一步 执行无参数构造创建bean实例");
}
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步 调用set方法设置属性值");
}
//创建执行的初始化的方法
public void initMethod() {
System.out.println("第三步 执行初始化的方法");
}
//创建执行的销毁的方法
public void destroyMethod() {
System.out.println("第五步 执行销毁的方法");
}
}
<bean id="orders" class="com.example.controller1.spring.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="芜湖"></property>
</bean>
@Test
public void testBean3() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("User.xml"); Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步 获取创建bean实例对象");
System.out.println(orders);
//手动让bean实例销毁
context.close();
}
9.基于注解实现IOC操作Bean管理
(1)什么是注解
1.注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值..)
2.使用注解,注解作用在类上面,方法上面,属性上面
3.使用注解目的:简化xml配置。
(2)Spring针对bean管理中创建对象提供的注解
- @Commponent——放在类上,把普通类实例化到spring容器中。
- @Service——一般使用在Service层
- @Controller——一般使用在Web层
- Repository——一般使用在Dao层
第一步:引入依赖
第二步:开启组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/beans/spring-context.xsd">
<!--开启组件扫描 1 如果扫描多个包,多个包使用逗号隔开 2 扫描包上层目录 -->
<context:component-scan base-package="com.example"></context:component-scan>
第三步:创建类,在类上面添加创建对象注解
//在注解里面value属性值可以省略不写,
//默认值是类名称,首字母小写
//即 UserService -- userService
@Component(value = "userService") //<bean id="userService" class=".."/>
public class UserService {
public void add() {
System.out.println("service add");
}
}
(3)使用注解方式实现属性的注入
@Autowired:根据属性类型进行自动装配
@Service
public class UserService {
//定义dao类型属性
//不需要添加set方法
//添加注入属性注解
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add");
userDao.add();
}
@Qualifier:根据名称进行注入 这个@Qualifier注解的使用,和上面@Autowired一起使用
//定义dao类型属性
//不需要添加set方法
//添加注入属性注解
@Autowired
//根据类型进行注入 因为userDao接口的实现类可以有很多个
@Qualifier(value = "userDaoImpl1")
//根据名称进行注入
private UserDao userDao;
@Resource:可以根据类型注入,可以根据名称注入
@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
@Value:注入普通类型属性
@Value(value = "lallala")
private String name;
(4)纯注解开发
1.创建配置类,替代xml文件
@Configuration//作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.example"})
public class SpringConfig{}
2.编写测试类
@Test
public void testService2(){
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService",UserService.class);
System.out. println(userService) ;
userService.add() ;
}
三、AOP
1.什么是AOP
(1)面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)不修改源代码的情况下,在主干功能里添加新功能。
2.AOP底层原理
(1)AOP底层使用动态代理
有两种情况动态代理
第一种:有接口情况,使用JDK动态代理
- 创建接口实现类代理对象,增强类的方法
第二种:没有接口情况,使用CGLIB动态代理
- 创建子类的代理对象,增强类的方法
(2)JDK动态代理(使用Proxy类里面的方法创建代理对象)JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口
其中有个newProxyInstance方法
方法有三个参数:
第一参数,类加载器
第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
第三参数,实现这个接口InvocationHandler,创建代理对象,写增强的
第一步:创建接口
public interface Person {
//交作业
void giveTask();
}
第二步:创建需要被代理的实际类
public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
public void giveTask() {
System.out.println(name + "交作业");
}
}
第三步:使用Proxy 类创建接口代理对象
//Proxy中有个InvocationHandler接口,这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。
public class StuInvocationHandler<T> implements InvocationHandler {
//invocationHandler持有的被代理对象
T target;
public StuInvocationHandler(T target) {
this.target = target;
}
/**
* proxy:代表动态代理对象
* method:代表正在执行的方法
* args:代表调用目标方法时传入的实参
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行" +method.getName() + "方法");
Object result = method.invoke(target, args);
return result;
}
}
第四步:测试
public class Test {
public static void main(String[] args) {
//创建一个实例对象,这个对象是被代理的对象
Person linqian = new Student("aaa");
//创建一个与代理对象相关联的InvocationHandler
InvocationHandler stuHandler = new StuInvocationHandler<Person>(bbb);
//创建一个代理对象stuProxy来代理linqian,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
//代理执行交作业的方法
stuProxy.giveTask();
}
}
(3)CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
(4)AOP术语
- Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。(把通知应用到切入点过程)
- Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。(类里面哪些方法可以被增强)
- Pointcut(切入点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。(实际被真正增强的方法)
- Advice(增强或者叫通知):(实际增强的逻辑部分)
前置通知:@Before 在目标业务方法执行之前执行
后置通知:@After 在目标业务方法执行之后执行
返回通知:@AfterReturning 在目标业务方法返回结果之后执行
异常通知:@AfterThrowing 在目标业务方法抛出异常之后
环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行- Target(目标对象):织入 Advice 的目标对象.。 Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
(5)切入点表达式
Pointcut("execution(* com.cn.springaop.service...* . * (...))"): 第一个表示匹配任意的方法返回值,上面的第一个..表示service包及其子包,第二个表示所有类,第三个*表示所有方法,第二个..表示方法的任意参数个数
四.事务
1.概念
事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
2.事务四个特性
- 原子性:事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
- 一致性:事务在完成时,必须是所有的数据都保持一致状态。
- 隔离性:并发事务执行之间无影响,在一个事务内部的操作对其他事务是不产生影响,这需要事务隔离级别来指定隔离性。
- 持久性:一旦事务完成,数据库的改变必须是持久化的。
3.事务管理操作
(1)编程式 (2)声明式(一般使用这个)
声明式事务管理(两个)
- 基于注解方式
(1)在spring配置文件配置事务管理器
<!--配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--数据库连接地址-->
<property name="url" value="xxx"/>
<!--数据库的用户名-->
<property name="username" value="xxx"/>
<!--数据库的密码-->
<property name="password" value="xxx"/>
<!--数据库驱动-->
<property name="driverClassName" value="xxx"/>
</bean>
<!--配置事务管理器,以 JDBC 为例-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
(2)在spring中配置文件,开启事务注解
引入 tx 命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--开启事务注解-->
<tx; annotation-driven transaction manager="transactionManager"></tx;annotation-driven>
(3).在service类上面(或者service类里面方法上面)添加事务注解@Transactional
(1)@Transactional,这个注解添加到类上面,也可以添加方法上面 (2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务 (3)如果把这个注解添加方法上面,为这个方法添加事务
(4)事务的传播行为
(5)事务的隔离级别
1)事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
2)有三个读问题:脏读、不可重复读、虚(幻)读
- 1.脏读:一个事务读到另一个事务未提交的更新数据。
- 2.不可重复读:一个事务两次读同一行数据,可是这两次读到的数据不一样。
- 3.幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。
- READ UNCOMMITTED:读未提交。
- READ COMMITTED:读已提交。
- REPEATABLE READ:可重复读。
- SERIALIZABLE:序列化。
- 基于xml配置文件方式
(1)配置事务管理器
基于注解中的(1)和(2)中的引入 tx 命名空间部分
(2)配置事务通知
<!--配置通知-->
<tx:advice id="tx-advice" transaction-manager="transactionManager">
<!--配置事务参数-->
<tx:attributes>
<tx:method name="create*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="10"/>
</tx:attributes>
</tx:advice>
(3)配置切入点和切面
<!--配置切点和切面-->
<aop:config>
<!--配置切点-->
<aop:pointcut id="tx-pt" expression="execution(* net.biancheng.c.service.impl.OrderServiceImpl.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="tx-advice" pointcut-ref="tx-pt"></aop:advisor>
</aop:config>
4、在 Spring进行声明式事务管理,底层使用AOP原理
5、Spring事务管理API
提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
五.spring5框架新功能
1.整个spring5框架的代码基于Java8,运行是兼容jdk9,许多不建议使用的类和方法在代码库中删除
2.spring5框架自带了通用日志封装
- (1)Spring5已经移除Log4jConfigListerer,官方建议使用Log4j2
- (2)Spring5框架整合Log4j2
第一步:引入jar包
第二步:编写配置文件log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
<!--先定义所有的appender-->
<appenders>
<!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
第三步: 测试类
public class TestLog {
@Test
public void test_logbymyself(){
Logger logger = LoggerFactory.getLogger(TestLog.class);
logger.info("log by myself grade is info");
logger.warn("warn");
}
}
3.支持@Nullable注解
@Nullable,注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以为空,参数值可以为空
4.函数式风格GenericApplicationContext/AnnotationConfigApplicationContext
5.整合Junit5
第一步:引入Junit依赖
第二步:创建测试类,使用注解
6.使用Spring WebFlux执行集成测试
1、Spring Webflux介绍
(1) 是Spring5添加新的模块,用于 web 开发的,功能和EpringMVC.类似的,Webflux.使用当前一种比较流程响应式编程出现的框架。
(2)使用传统web框架,比如SpringMVC,这些基于Servlet,容器,Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关 API实现的(3)Webflux
- 第一非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程
- 第二函数式编程: Spring5框架基于java8,Webflux使用Java8函数式编程方式实现路由请求
(4)比较springmvc
2.响应式编程(Reactor实现)
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
第一步:引入依赖
第二步:基本特点