2020.06.03
spring概述
Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。
程序的耦合
耦合:程序间的依赖关系,包括类之间的耦合、方法间的耦合。
解耦:降低程序间的依赖关系。
实际开发应该做到:编译期不依赖,运行时才依赖。
解耦的思路:
- 使用反射来创建对象,而避免使用new关键字。
- 通过读取配置文件来获取要创建的对象的全限定类名。
工厂类
Bean:在计算机英语中,有可重用组件的含义。
JavaBean:用java语言编写的可重用组件。javabean的范围大于实体类范围。
它就是创建我们的service和dao对象的。
第一个:需要一个配置文件来配置 service和dao。
第二个:通过读取配置文件中配置的内容,反射创建对象(配置的内容:唯一标识=全限定类名也就是key=value的形式)。
配置文件可以使xml也可以是properties。
简单来说,就是用配置文件设定好路径,在工厂初始化的时候就将配置文件的路径加载并读取,接下来在方法中return一个对象,返回的对象要看传入这个方法的参数,通过这个参数去配置文件中调取对应的类,最终生成对象并返回。
IOC
控制反转(Inversion of Control)把创建的对象的权利交给框架,是框架的重要特征,并非面向对象编程的专用术语。
控制反转,降低依赖关系,削减计算机的耦合。
Spring中的IOC
首先进行bean的配置,具体方式为
接下来在文件中配置:
- 通过ApplicationContext获取核心容器对象。
ApplicationContext的三个常用实现类:
-
ClassPathXmlApplicationContext:它可以加载类路径下的配置,要求配置文件必须在类路径下,不在的话加载不了。
-
FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
-
AnnotationConfigApplicationContext:它是用于读取注解创建容器的。
- 将要获取的对象的类名传入工厂,获取对象,可以将返回的对象强转,也可以将接口的class文件传入。
核心容器的两个接口引发出的问题
- ApplicationContext(单例对象适用):它在构建核心容器时,创建对象采取的策略是采用立即加载的方式,也就是说只要读取完配置文件马上就创建配置文件中配置的对象
- BeanFactory(多例对象适用):它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式,也就是说什么时候根据id获取对象了,什么时候才真正的创建对象
spring对bean的管理细节
1.创建bean的三种方式
- 第一种方式:使用默认构造函数创建。在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
<bean id="accountService" class="com.recruit.service.impl.AccountServiceImpl"></bean>
- 第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)。factory-bean为调取以获取的类,factory-method为调取以获取的类中的方法。
- 第三种方式:使用静态方法创建对象(使用某个类中的静态方法创建对象,并存入容器)。
<bean id="accountService" class="com.recruit.factory.StaticFactory" factory-method="getAccountService"></bean>
2.bean对象的作用范围
bean标签的scope属性:
- 作用:用于指定bean的作用范围
- 取值:常用的就是单例的和多例的
- singleton:单例的(默认值)
- prototype:多例的
- request:作用于web应用的请求范围
- session:作用于web应用的会话范围
- global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境是,它就是session。
<bean id="accountService" class="com.recruit.service.impl.AccountServiceImpl" scope="prototype"></bean>
3.bean对象的生命周期
单例对象:
- 出生:当容器创建时对象出生
- 活着:只要容器还在,对象一直活着
- 死亡:容器销毁,对象消亡
- 总结:单例对象的生命周期和容器相同
多例对象:
- 出生:当使用对象时spring框架才为我们创建
- 活着:对象只要是在使用过程中就一直活着
- 死亡:当对象长时间不用且没有别的对象引用时,由java垃圾回收进行回收
init-method=""#初始化对象时运行的方法,
destroy-method=""销毁对象时运行的方法。
spring的注入
一般用来在类创建时注入的。类中方法的注入可以调取类,再调取方法即可。
1. 构造函数注入:
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性:
- type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型。
- index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。参数索引的位置从0开始。
- name:用于指定给构造函数中指定名称的参数赋值(常用)
--------------------以上三个用于指定给构造函数中那个参数赋值----------------------------------- - value:用于提供基本类型和String类型的数据
- ref:用于指定其他的bean类型数据,它指的就是在spring中的Ioc核心容器中出现过的bean对象。
优势:在获取bean对象时,注入数据时必须的操作,否则对象无法创建成功。
劣势:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
2. Set注入:
使用的标签:property
标签出现的位置:bean标签的内部
标签中的属性:
- name:用于指定注入时所调用的set方法名称
- value:用于提供基本类型和String类型的数据
- ref:用于指定其他的bean类型数据,它指的就是在spring中的Ioc核心容器中出现过的bean对象。
优势:创建对象没有明确的限制,可以直接使用默认构造函数
劣势:如果有某个成员必须有值,则获取对象是有可能set方法无法执行。
3. 复杂类型的注入/集合类型的注入:
用于给List结构集合注入的标签:list array set
用于Map结构集合注入的标签:map props
结构相同,标签可以互换。
2020.06.04
spring中的注解
一、用于创建对象的注解:他们的作用就和在XML配置文件中编写一个bean标签实现的功能是一样的
@Component:
作用:用于把当前类对象存入spring容器中
属性:value:用于指定bean的id,当我们不写值,它的默认值是当前类名,并且首字母改小写
以下三个注解他们的作用与属性与Component时一模一样的。他们三个是Spring框架为我们提供的明确三层使用的注解,使我们的三层对象更加清晰
- @Controller:一般用在表现层
- @Service:一般用在业务层
- @Repository:一般用于持久层
二、用于注入数据的:他们的作用就和在XML配置文件中的bean标签中写一个property标签的作用是一样的
@Autowired:
作用:
- 自动按照类型注入,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
- 如果ioc容器中没有任何bean的类型与要注入的变量类型匹配,则报错。
- 如果ioc容器中有多个bean的类型与要注入的变量类型匹配,则报错。
出现位置:可以在变量上,可以在方法上
细节:在使用注释注入时,set方法就不是必须的了
@Qualifier
作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以。
属性:value用于指定注入bean的id。
@Resource
作用:直接按照bean的id注入。它可以独立使用
属性:name用于指定bean的id。
以上三个注解都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现
另外集合类型的注入只能通过xml来实现。
@Value
作用:用于注入基本类型和String类型的数据
属性:value用于指定数据的值。它可以使用spring中的SpEL(spring的el表达式)(SpEL的写法:${表达式})
三、用于改变作用范围的:他们的作用就和在bean标签中使用的scope属性的功能是一样的
@Scope
作用:用于指定Bean的作用范围
属性:value:指定范围取值。常用取值:singleton,prototype
四、和生命周期相关的:他们的作用就和在bean标签中使用init-method标签和destroy标签是一样的
@PerDestroy
作用:用于指定销毁方法
@PostConstruct
作用:用于指定启动方法
2020.06.05
五、通过注解配置包等
@Configuration
作用:该类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
@ComponentScan
作用:用于通过注解指定spring在创建容器时要扫描的包
属性:
value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包,我们使用此注解就等同于在xml中配置了。<context:component-scan base-package=“com.recruit”></context:component-scan>。因为里面是String[]类型的,所以加大括号,如果只有一个的话可以省略。
@Bean
作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
属性:name:用于指定bean的id。当不写时,默认值是当前方法的名称
细节:当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。查找的方式和Autowired注解的作用是一样的
@Import
作用:用于导入其他类,xxx.class
属性:value:用于指定其他配置类的字节码,当我们使用import注解之后,有import注解的类就是父配置类,而导入的都是子配置类
@PropertySource
作用:用于指定Properties文件的位置
属性:value:指定文件的名称和路径,关键字:classpath,表示类路径下
spring整合junit问题
- 应用程序的入口:main方法。
- junit单元测试中,没有main方法也能执行:junit集成了一个main方法,该方法就会判断当前测试类中那些方法有@Test注解,junit就让有Test注解的方法执行
- junit不会管我们是否采用spring框架:在执行测试方法时,junit根本不知道我们是不是使用了spring框架,所以也就不会为我们读取配置文件/配置类创建spring容器。
- 由以上三点可知:当测试方法执行时,没有Ioc容器,就算写了Autowired注解,也无法实现注入。
使用sprinmg整合junit的配置
- 导入spring整合junit的jar坐标
- 使用junit提供的一个注解把原有的main方法替换了,替换成spring提供的@Runwith
- 告知spring的运行器,spring和ioc创建时基于xml还是注释,并且说明位置
@ContextConfiguration,locations:指定xml文件的位置,加上classpath关键字,表示在类路径下,classes:指定注解类所在的位置
当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
2020.06.08
动态代理
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强
分类:
基于接口的动态代理
- 涉及的类:Proxy
- 提供者:JDK官方
- 如何创建代理对象:使用Proxy类中的newProxyInstance方法
- 创建代理对象的要求:被代理类最少实现一个接口,如果没有则不能使用
- newProxyInstance方法的参数:
- ClassLoader:类加载器:用于加载代理对象字节码的,和被代理对象使用相同的类加载器。固定写法。
- Class[]:字节码数组:它是用于让代理对象和被代理对象有相同的方法。固定写法。
- InvocationHandler:用于提供增强的代码,它是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。此接口的实现类都是谁用谁写
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* 方法参数的含义
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数,以数组形式存在
* @return 和被代理对象方法有相同的返回值
* @throws Throwable
*/
基于子类的动态代理
- 涉及的类:Enhancer
- 提供者:第三方cglib库
- 如何创建代理对象:使用Enhancer类中的create方法
- 创建代理对象的要求:被代理类不能是最终类
- create方法的参数:
- Class: 字节码:它是用于指定被代理对象的字节码。
- Callback:用于提供增强的代码,它是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。此接口的实现类都是谁用谁写。我们一般写的都是该接口的子接口实现类:MethodInterceptor
/**
* 执行被代理对象的任何方法都会经过该方法
* @param proxy
* @param method
* @param args
* 以上三个参数和基于接口的动态代理中invoke方法的参数一样的
* @param methodProxy:当前执行方法的代理对象
* @return
* @throws Throwable
*/
AOP
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
作用:在程序运行期间,不修改源码对已有方法进行增强。
优势:减少重复代码,提高开发效率,维护方便。
Spring中的AOP
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,是因为spring只支持方法类型的连接点。
- Pointcut(切入点):所谓切入点是指我们要对那些Joinpoint进行拦截的定义。
- Advice(通知/增强):所谓通知是指拦截到Joinpoint之后要做的事情就是通知。分为:前置通知、后置通知、异常通知、最终通知、环绕通知。
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一下方法或Field。
- Target(代理对象):代理目标对象。
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载织入。
- Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。
- Aspect(切面):是切入点和通知(引介)的结合。
2020.06.09
基于xml的AOP配置
- 把通知Bean也交给spring来管理
- 使用aop:config标签表明开始AOP的配置
- 使用aop:aspect标签表明配置切面:id属性是给切面提供一个唯一标志,ref属性是指定通知类bean的唯一id。
- 在aop:aspect标签的内部使用对应的标签来配置通知的类型。
aop:before:表示这是前置通知,method属性:用于指定Logger类中那个方法是前置通知
aop:after-returning:表示这是后置通知,它和异常通知只能有一个
aop:after-throwing:表示这是异常通知,它和后置通知只能有一个
aop:after:表示这是最终通知 - pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中那些方法增强
切入点表达式的写法:
关键字:execution(表达式)
表达式:访问修饰符 返回值 包名.包名.包名…包名.类名.方法名(参数列表)
标准的表达式写法:
public void com.recruit.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以省略:
void com.recruit.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符表示任意返回值:
* com.recruit.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符表示任意包,但是有几级包就需要写几个*:
* ....AccountServiceImpl.saveAccount()
包名可以使用…表示当前包及其子包:
* ….AccountServiceImpl.saveAccount()
类名和方法名都可以使用来实现通配:
* ….()
参数列表:
可以直接写数据类型:基本类型直接写名称 int;引用类型写包名.类名的方法 java.lang.String
可以使用通配符表示任意类型,但是必须有参数
全通配写法:
* ….(…)
实际开发中切入点表达式的通常写法:切到业务层实现类下的所有方法 * com.recruit.service.impl..*(…) - 切入点表达式 aop:pointcut id=" " expression=" ",此标签可写在切面标签内部,也可写在切面标签外部。
环绕通知
- 问题:当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
- 分析:通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
- 解决:Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法
- 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供接口的实现类供我们使用。
- Spring中的环绕通知:它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
基于注解的AOP配置
配置开启注解Aop的支持:aop:aspectj-autoproxy
- @Aspect:表示当前类是一个切面类
- @Before:前置通知
- @AfterReturning:后置通知
- @AfterThrowing:异常通知
- @After:后置通知
- @Around:环绕通知
- @Pointcut:切入点表达式
- @EnableAspectJAutoProxy:不适用XML的配置方式
spring中事务控制的一组API
PlatformTransactionManager接口提供事务操作的方法,包含有三个具体的操作。
- 获取事务信息状态:TransactionStatus getTransaction(TransactionDefinition definition)
- 提交事务: void commit(TransactionStatus status)
- 回滚事务:void rollback(TransactionStatus status)
TransactionDefinition
- 获取事务对象名称:String getName()
- 获取事务隔离级别: int getsolationLevel()
- 获取事务传播行为:int getPropagationBehavior()
- 获取事务超时时间:int getTimeout()
- 获取事务是否只读:boolean isReadOnly()
TransactionStatus
- 刷新事务:void flush()
- 获取是否存在储存点:boolean hasSavepoint()
- 获取事务是否完成:boolean isCompleted()
- 获取事务是否为新的事务:boolean isNewTransaction()
- 获取事务是否回滚:boolean isRollbackOnly()
- 设置事务回滚:void setRollbackOnly()
spring中基于XML的声明式事务控制配置步骤
- 配置事务管理器
- 配置事务的管理器:此时我们需要导入事务的约束,tx名称空间和约束,同时也需要aop的。使用tx:advice标签配置事务通知属性:id:给事务通知起一个唯一标识,transaction-manager:给事务通知提供一个事务管理器引用。
- 配置AOP中的通用切入点表达式
- 建立事务通知和切入点表达式的对应关系
- 配置事务的属性:是在事务的通知tx:advice标签的内部
isolation:用于指定事务的隔离级别,默认值为DEFAULT,表示使用是数据库的默认隔离级别
propagatiob:用于指定事务的传播行为,默认值是REQUIRED,表示一定会有事务,增删改的选择,查询方法可以选择SUPPORTS。
read-only:用于指定事务是否只读,只有查询才能设置为true,默认值为false表示读写。
timeout:用于指定事务的超时时间,默认值为-1,表示永不超时,如果指定了数值,以秒为单位。
rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不会滚,没有默认值。表示任何异常都回滚。
no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务都回滚,没有默认值。表示任何异常都回滚。
spring中基于XML的声明式事务控制配置步骤
- 配置事务管理器
- 开启spring对注解事务的支持
- 在需要事务支持的地方使用@Transactional注解