Spring
1.一个创建Bean对象的工厂
JavaBean:用java语言编写的可重用组件(javaBean > 实体类)
Bean:在计算机语言中,有可重用组件的含义
Bean是创建service和dao对象的
创建方式:
*第一个:需要一个配置文件来配置service和dao
配置的内容:唯一标识=全限定类名(key=value)
*第二个:通过读取配置文件中的配置内容,反射创建对象
配置文件可以是xml或者properties
2.程序的耦合
耦合:程序间的依赖关系
包括:类之间的依赖;方法之间的依赖
解耦:降低程序间的依赖关系
实际开发中应该做到,编译器不依赖,运行时才依赖
解耦的思路:
*第一步:使用反射来创建对象,而避免使用new关键字
*第二步:通过读取配置文件来获取要创建的对象全限定类名
3.获取spring的IOC核心容器,并根据id获取对象
ApplicationContext的三个常用实现类:
*ClassPathXmlApplicationContext:可以加载类路径下的配置文件,要求配置文件必须在类路径下,如果不在加载不了
*FileSystemXmlApplicationContext:可以加载磁盘任意路径下的配置文件(必须有访问权限)
*AnnotationConfigApplicationContext:用于读取注解创建容器的核心容器的两个接口引发出的问题:
ApplicationContext:单例对象使用 采用此接口
在构建核心容器时创建对象采用的策略是采用立即加载的方式。只要一读取完配置文件马上就创建配置文件中配置的对象
BeanFactory:多例对象使用
在构建核心容器时创建对象采用的策略是采用延迟加载的方式,什么时候根据id获取对象了,什么时候才是真正的创建对象
4.Spring对bean的管理细节
1.创建bean的三种方式
*使用默认构造函数创建:
在Spring的配置文件中使用bean标签,配以id和class属性后,且没有其他属性和标签时,采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
*使用普通工厂中的方法创建对象:
使用某个类中的方法创建对象,并存入spring容器
*使用工厂中的静态方法创建对象:
使用某个类中的静态方法创建对象,并存入spring容器中
2.bean对象的作用范围调整
bean标签的scope属性:
作用:用于指定bean的作用范围
取值:常用的是单例的和多例的
singleton;单例的(默认值)
prototype:多例的
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
3.bean对象的生命周期:
*单例对象:
出生:当容器创建时对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象死亡
总结:单例对象的声明周期和容器相同
*多例对象:
出生:当使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着
死亡:当对象长期不使用,且没有别的对象引用时,由java的垃圾回收器回收
5.Spring中的依赖注入
依赖注入:Dependency Injection,依赖关系的维护就称之为依赖注入
IOC作用:降低程序间的耦合(依赖关系)
依赖关系的管理:以后都交给spring来维护
在当前类需要用到其他类的对象,由Spring为我们提供,我们只需要在配置文件中说明
依赖注入能注入的数据有三类:
*基本类型和String
*其他Bean类型(在配置文件中或者注解配置过的bean)
*复杂类型/集合类型
注入的方式有三种
*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对象
优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功
弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,就必须提供。
*复杂类型的注入/集合类型的注入
*用于给List机构集合注入的标签:
list array set
*用于给Map结构集合注入的标签:
map props
*结构相同,标签可以互换
*3.使用注解提供
6.注解配置
曾经XML的配置:
<bean id="accountService" class="eg.service.impl.AccountServiceImpl"
scope="" init-method="" destroy-method="">
<property name="" value="" ref=""></property>
</bean>
1.用于创建对象的
作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
*Component:
作用:用于把当前类对象存入spring容器中
属性:
value:用于指定bean的id,不写默认值是当前类名,且首字母改为小写
*Controller:一般用在表现层
*Service:一般用在业务层
*Repository:一般用在持久层
以上三个注解的作用和属性与Component是一模一样,他们三个是Spring框架为我们提供明确的三层使用的注解,使三层对象更加清晰
2.用于注入数据的
作用就和在XML配置文件中的<bean>标签中写一个<property>标签的作用是一样的
*Autowired:
作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错
如果ioc容器汇总有多个类型匹配时,则根据bean对象的name属性一一对应获得
出现位置:可以是变量上,也可以是方法上
细节:在使用注解注入时,set方法就不是必须的了
*Qualifier:
作用:在按照类中注入的基础上再按照名称注入。他在给类成员注入时不能单独使用。但是在给方法参数注入时可以
属性:
value:用于指定注入的bean的id
*Resource:
作用:直接按照bean的id注入,它可以独立使用
属性:
name:用于指定bean的id
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
另外,集合类型的注入只能通过XML来实现
*Value:
作用:用于注入基本类型和String类型的数据
属性:
value:用于指定数据的值,它可以使用spring中的SpEL(也就是spring的el表达式)
SpEl的写法:${表达式}
3.用于改变作用范围的
作用就和在bean标签中使用scope属性实现的功能是一样的
*Scope
作用:用于指定bean的作用范围
属性:
value:指定范围的取值。常用取值:singleton prototype
4.和生命周期相关(了解)
作用就和在bean标签中使用init-method和destroy-method的作用是一样的
*PreDestroy:用于指定销毁方法
*PostConstruct:用于指定初始化方法
7.Spring中的新注解
1.Configuration
作用:指定当前类是一个配置类
细节:当配置类作为AnnotationConfigApplicationContext对象创建参数时,该注解可以不写
2.ComponentScan
作用:用于通过注解指定spring在创建容器时要扫描的包
属性:
value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
使用此注解等同于在xml中配置了:<context:component-scan base-package="eg"></context:component-scan>
3.Bean
作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
属性:
name:用于指定bean的id,当不写时,默认值是当前反复噶的名称
细节:
当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象
查找的方式和Autowired注解的作用是一样的
4.Import
作用:用于导入其他配置类
属性
value:用于指定其他配置类的字节码
当我们使用Import的注解之后,有Import注解的类就是父配置类,而导入的就是子配置类
5.PropertySource
作用:用于指定properties文件的位置
属性:
value:指定文件的名称和路径
关键字:classpath,表示类路径下
8.Spring整合junit的配置
1.导入spring整合junit的jar(坐标)spring-test
2.使用junit提供的一个注解把原有的main方法替换成spring提供的
@Runwith
3.告知spring的运行器,spring和ioc创建时基于xml还是注解的,并且说明位置
@ContextConfiguration
locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
classes:指定注解类所在的位置
9.动态代理
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强
分类:
1.基于接口的动态代理
涉及的类:Proxy
提供者:JDK官方
如何创建代理对象:
使用proxy类中的newProxyInstance方法
创建代理对象的要求:
被代理类最少实现一个接口,如果没有则不能使用
newProxyInstance方法的参数:
ClassLoader:类加载器
用于让代理对象和被代理对象使用相同的类加载器。固定写法
Class[]:字节码数组
用于让代理对象和被代理对象有相同方法。固定写法
InvocationHandler:用于提供增强的代码
它是让我们写如何代理。我们一般都是写一些该接口的实现类,通常情况下都是匿名内部类,但不是必须的
此接口的实现类都是谁用谁写
2.基于子类的动态代理
涉及的类:Enhancer
提供者:第三方库cglib
如何创建代理对象:
使用Enhancer类中的create方法
创建代理对象的要求:
被代理类不能是最终类
create方法的参数:
Class:字节码
用于指定被代理对象的字节码
Callback:用于提供增强的代码
它是让我们写如何代理。我们一般都是写一个该接口的实现类MethodInterceptor,通常情况下都是匿名内部类,但不是必须的。
此接口的实现类都是谁用谁写
10.spring中基于XML的AOP配置步骤
1.把通知Bean也交给spring来管理
2.使用aop:config标签表明开始AOP的配置
3.使用aop:aspect标签表明配置切面
id属性:是给切面提供一个唯一的标识
ref属性:是指定通知类bean的id
4.在aop:aspect标签的内部使用对应标签来配置通知的类型
我们现在示例是让printLog方法在切入点方法执行之前执行,所以是前置通知
aop:before:表示配置前置通知
method属性:用于指定Logger类中哪个方法是前置通知
pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
11.切入点表达式的写法
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表)
标准的表达式写法:
public void eg.service.impl.AccountServiceImpl.saveAccount()
访问修饰符可以忽略
void eg.service.impl.AccountServiceImpl.saveAccount()
返回值可以使用通配符,表示任意返回值
* eg.service.impl.AccountServiceImpl.saveAccount()
包名可以使用通配符,表示任意包,但是有几级包,就需要写几个*
* *.*.AccountServiceImpl.saveAccount()
包名可以使用..表示当前包及其子包
* *..AccountServiceImpl.saveAccount()
类名和方法名都可以使用*来实现通配
* *..*.*()
参数列表
可以直接写数据类型
基本类型直接写名称 int
引用数据类型写包名.类名的方式 java.lang.String
可以使用通配符表示任意类型,但是必须有参数
可以使用..表示有无参数均可,有参数可以是任意参数
全通配符写法
* *..*.*(..)
实际开发中切入点表达式的通常写法
切到业务层实现类下的所有方法
* eg.service.impl.*.*(..)
12.配置通知的类型,并且建立通知方法和切入点方法的关联
1.配置前置通知:在切入点方法执行之前执行–>
<aop:before method="beforePrintLog" pointcut="execution(* eg.service.impl.*.*(..))"></aop:before>
<aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>
2.配置后置通知:在切入点方法正常执行之后执行,它和异常通知永远只能执行一个
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>
3.配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>
4.配置最终通知:无论切入点方法是否正常执行他都会在其后面执行
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
5.环绕通知问题:
当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
分析;通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有
解决:Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法
该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用
Spring中的环绕通知:它是spring框架为我们提供的一种可以在代码中手动增强方法何时执行的方式
13.spring中的声明式事务控制配置
1.基于注解的声明式事务控制配置步骤
1.配置事务管理器
2.开启spring对注解事务的支持
3.在需要事务支持的地方使用@Transactional注解
2.基于XML的声明式事务控制配置步骤
1.配置事务管理器
2.配置事务的通知
此时我们需要导入事务的约束 tx名称空间和约束,同时也需要aop的
使用tx:advance标签配置事务通知
属性:
id:给事务通知起一个唯一的标识
transaction-manager:给事务通知提供一个事务管理器引用
3.配置AOP中的通用切入点表达式
4.建立事务通知和切入点表达式的对应关系
5.配置事务的属性
是在事务的通知tx:advice标签的内部
isolation=""用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别
propagation=""用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS
read-only=""用于指定事务是否只读,只有查询方法才能设置为true。默认值是false,表示读写。
timeout=""用于指定事务的超时时间,默认值是-1,表示永不超时,如果指定了数值,以秒为单位
rollback-for=""用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值,表示任何异常都回滚
no-rollback-for=""用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚,没有默认值,表示任何异常都回滚