-
- 2.1. 构造对象的方法
- 2.2. ioc在javaweb中的体现
- 2.3. DI依赖注入
- 2.4. 使用spring创建对象(使用xml)
- 2.5. 获取ApplicationContext容器中的信息
- 2.6. 设值注入
- 2.7. 构造注入
- 2.8. 自动注入
-
- 4.1. 注解使用步骤
- 4.2. 注解实现简单类型属性赋值
- 4.3. 注解实现引用类型赋值
- 4.4. 常用注解
-
- 5.1. 使用jdk动态代理实现aop
- 5.2. aspectj来实现aop
- 5.2.1. 使用aspectj的步骤
- 5.2.2. aspectj的五个通知注解
- 5.2.3. 切入点表达式
- 5.2.4. 通知中的参数JoinPoint
- 5.2.5. 前置通知
- 5.2.6. 后置通知
- 5.2.7. 环绕通知
- 5.2.8. 异常通知
- 5.2.9. 最终通知
- 5.2.10. 定义和管理切入点
- 5.2.11. cglib代理方式
-
- 6.1. 整合步骤
-
- 7.1. 事务的类型
- 7.2. spring框架中提供的事务处理方案
- 7.2.1. 使用注解@Transactional
- 7.2.2. 使用aspectj框架配置事务
spring
Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。主要应用于service层
1. J2EE三层架构
- 表现层(spring-mvc)
主要是JSP和HTML页面,用于接收du用户的zhi请求,以及返dao回操作数据,是应用专程序访问的入属口。 - 业务逻辑层service(spring)
主要是对数据层进行操作,对数据逻辑层进行处理,如果数据层是积木,那么逻辑层就是堆积木的搭建。 - 数据访问层dao(mybatis)
主要是对原始数据的操作层,具体为业务逻辑层或表现层提供数据服务。2. ioc(xml实现)
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
正转:用new来创建对象。
ioc可以减少对代码的改动。
2.1. 构造对象的方法
- 使用new来创建对象
- 使用反射创建对象
- 序列化
- 克隆
- ioc:使用容器创建对象
2.2. ioc在javaweb中的体现
servlet:在使用servlet时,没有主动创建对象,而是容器Tomcat帮你创建好servlet对象
1 | <!--使用xml注册servlet--> |
在使用的时候我们可以直接使用servlet,而不用使用
Servlet s = new Servlet();来创建对象,这些对象都被Tomcat创建好。
2.3. DI依赖注入
DI是ioc技术的实现,只需要在程序中提供要使用的对象的名称就可以,至于对象如何在容器中创建、赋值、查找都是容器内部实现
spring使用di实现了ioc的功能,spring底层创建对象,使用的是反射机制。
spring是一个容器,管理对象,给属性赋值,底层是反射创建对象。
2.4. 使用spring创建对象(使用xml)
1.首先创建一个maven项目,在pom文件中加入spring的依赖,并修改jdk版本
1 | <dependency> |
1 | <properties> |
2.创建接口和实现类
1 | public interface SomeService { |
1 | public class SomeServiceImpl implements SomeService { |
3.在main文件下创建resource目录,在里面创建spring配置xml文件,声明bean标签,一个bean标签即为一个对象
1 |
|
4.在程序中创建容器对象ApplicationContext,使用bean标签的id来获取容器中创建好的对象。创建对象的时间为创建ApplicationContext的时候。
1 | /** |
注意:spring不止能够创建自定义对象,也能够创建那些已存在的对象,只要class属性给出类的路径
2.5. 获取ApplicationContext容器中的信息
ApplicationContext对象中有许多方法,可以获取容器中对象的信息
1 | //获取容器中创建的对象的数目 |
2.6. 设值注入
spring调用类的set方法,在set方法可以实现属性的赋值,因此需要要创建的类中必须有set方法
在bean标签中使用property标签来注入值,set注入会在指定类中寻找set方法,使用set方法将value属性赋给方法参数来给属性赋值。
寻找的格式是set+name属性的首字母大写,所以即使类中没有对应的属性,只要有格式setName(value) 的方法存在,就可以使用set注入。
1 | <bean id="student" class="com.hjznb.ba01.Student"> |
Student类
1 | public class Student { |
注意:设置注入只是使用set方法!
引用类型的设置注入不是使用value,而是使用ref属性来指定注入的类,被注入的类要在容器中先被创建好。
下面将Student类中添加一个School属性,使用set注入给其设值
School类
1 | public class School { |
在Student类中添加School属性和对应的set方法
1 | public class Student { |
然后在配置文件中先配置好School的bean,先创建好School类的对象,再将其注入到Student类中
1 |
|
2.7. 构造注入
spring容器生成对象默认调用无参构造方法,如果需要使用有参构造方法来构造对象,需要使用构造注入。
构造注入可以使用参数名和下标来指定注入的值,使用
constructor-arg标签注入
1 | <!-- |
2.8. 自动注入
引用类型自动注入有两种方式:byName和byType
使用bean标签中的autowire属性来设置注入方式。
2.8.1. byName
byName是通过id名来进行注入的,如果属性名和bean标签id的一致,spring就会将对应的bean对象注入。
1 | <bean id="myStudent" class="com.hjznb.ba03.Student" autowire="byName"> |
在这里,Student类中有一个School引用类型的属性school,通过byName自动注入方式,spring会在容器中寻找id名为school的对象,然后将其注入到bean中。
spring会对类中的每个引用类型都进行注入。
2.8.2. byType
byType是按类型注入,spring会扫描容器中的类,如果引用类型的数据类型和bean标签中的class属性中是同源关系的,spring会进行注入
同源关系指:
- 引用类型的数据类型和class属性一致
- 引用类型的数据类型和class属性为父子关系
- 引用类型的数据类型和class属性为接口和实现类关系
autowire属性值为byType
1 | <!--byType按类型注入引用类型--> |
PrimarySchool为School类型的子类,spring会将其创建对象赋给Student类的School属性
下面来测试是否将其注入
1 | public class MyTest3 { |
注意:使用byType自动注入时,只能有一个同源类型的类满足要求,如果有多个类满足注入的要求,spring会报错。
3. 使用多配置文件
使用多配置文件可以将功能不同的类(或不同模块中的类)放在不同的配置文件之中。
例如在这里有两个类:Student类和School类。我将他们放在两个不同的配置文件中创建bean,然后使用一个总配置文件将他们包含进来。
1 |
|
1 |
|
1 |
|
需要在总配置文件中使用import标签将其他配置文件包含进来,在配置文件中指定文件位置需要使用classpath。
也可以在指定路径时使用通配符*
1 | <import resource="classpath:ba05/spring-*.xml"/> |
它会寻找以spring-开头的文件
在使用时只需加载总配置文件就可以了
1 | public class MyTest4 { |
即使两个bean在不同的配置文件中,仍然可以进行自动注入。
使用的时候尽量使用多配置文件,效率更高,避免冲突
4. ioc的注解实现
spring能用注解实现ioc,代替xml
4.1. 注解使用步骤
先加入依赖
1.首先在resource目录下创建spring的配置文件,在配置文件中声明组件扫描器,在扫描器中指定要扫描的包
1 | <!--声明组建扫描器,组件就是java对象 |
2.创建类,在需要spring创建对象的类上方添加对应的注解,@Component注解和bean标签的功能类似,括号中的value属性对应bean标签的id
1 | //@Component(value = "myStudent") |
3.在测试类中创建容器对象,使用容器通过注解中的属性获取指定的对象
1 | public class MyTest1 { |
4.2. 注解实现简单类型属性赋值
使用@Value(“属性值”)给属性赋值,位置可以在set方法的上方或者对应的属性上方
1 |
|
如果在set方法的上方,其会传给方法参数
4.3. 注解实现引用类型赋值
@Autowired(required = true)注解给引用类型赋值,默认为byType自动注入,括号中的属性值是一个boolean类型的,表示当赋值失败后的操作,如果为true,则会报错;如果为false,会给引用类型赋值为null,默认为true
1 |
|
如果要使用byName来进行自动注入,就要使用@Qualifier(“属性”)标签,其中属性值为bean的id值,也就是注解中的value属性
该标签要和@Autowired注解一起使用。
1 |
|
该注解的位置可以在属性上方或set方法上方
4.4. 常用注解
- @Component:创建对象
- @Repository,创建dao对象,用来访问数据库
- @Service,创建Service对象,处理业务逻辑,可以有事务功能
- @Controller,创建控制器对象,接受请求,处理结果
- @Value简单类型的属性赋值
- @Autowired:srping框架中的引用类型的赋值注解,支持byType和byName,默认为byType
- @Resource:jdk中的注解,使用自动注入,默认为byName
其中@Resource(name =” id”)注解需要额外加入依赖
1 | <dependency> |
使用注解创建的对象也在容器中,所以注解能和xml一起使用
1 |
|
5. aop
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
5.1. 使用jdk动态代理实现aop
如果我要给业务方法增加一个提交事务和打印日志的功能,并且不改变源代码,那么可以使用jdk的动态代理
要使用动态代理,首先要实现InvocationHandler类,在这个类的invoke方法中给目标方法增加功能
1 | public class MyInvocationHandler implements InvocationHandler { |
里面使用的工具类
1 | public class ServiceTools { |
创建InvocationHandler对象,使用该对象来代理目标类
1 | public class App |
class com.sun.proxy.$Proxy0,可见得到的目标类的对象为代理对象,并且在没有修改目标类的代码的情况下增加了功能。
5.2. aspectj来实现aop
aspectj为一个框架,用来实现aop,spring框架中集成了aspectj框架,通过使用spring就能使用aspectj的功能,其实现aop有两种方式:
- 使用xml配置文件:配置全局事务
- 使用注解,在我们项目中要做aop功能,一般都使用注解,aspectj有五个注解
5.2.1. 使用aspectj的步骤
- 加入aspectj的依赖
1 | <dependency> |
- 创建目标类
1 | public interface SomeService { |
- 创建切面类,在类上边加入@Aspect注解,代表是切面类
1 |
|
- 在切面类中写要插入的方法,用通知注解和切入点表达式execution指明要插入的时间和位置
1 | /*要插入的方法*/ |
- 在容器中声明目标类和切面类的对象的bean(此处使用xml)
1 | <!--目标类--> |
- 声明自动代理生成器
1 | <!-- |
- 在测试代码中创建的目标类即使其的代理类
1 | public class MyTest1 { |
5.2.2. aspectj的五个通知注解
通知注解表示切面的执行时间
@Before:前置通知注解。特点:
1.在目标方法之前执行
2.不会改变目标方法的执行结果
3.不会影响目标方法的执行
@AfterReturning:后置通知。特点:
1.在目标方法之后执行
2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
Object res = doOther();
3.可以修改这个返回值
@Around :环绕通知。 特点:
1.它是功能最强的通知
2.在目标方法的前和后都能增强功能
3.控制目标方法是否被调用执行
4.修改原来的目标方法的执行结果,影响最后的结果
等同于jdk的动态代理
环绕通知还能决定目标方法是否执行!!!!
@AfterThrowing 异常通知。特点:
1.在目标方法抛出异常时执行
2.可以做异常的监控程序,检测目标方法执行时是否有异常
@After 最终通知。特点:
语法:修饰符.返回值.包名.类名.方法(参数类型)
execution(public void com.hjznb.ba01.SomeServiceImpl.doSome(String,Integer))
可以使用通配符’*’和’..’,前者代表任意字符,后者代表任意层包。下面这个表达式表示插入位置为任意修饰符,任意包下的SomeServiceImpl类中的以doOther开头的方法,并且方法中的参数是任意
execution(* ..SomeServiceImpl.doOther(..)
5.2.4. 通知中的参数JoinPoint
指定通知方法中的参数:JoinPoint,可以查看方法的信息,必须放在第一个参数位置
* JoinPoint:业务方法,要加入切面功能的业务方法
* 作用是:可以在通知方法中获取方法执行时的信息,例如方法名称、实参等
* 如果你的切面功能中需要用到方法的信息,就加入JoinPoint
* 这个JoinPoint参数的值由框架赋予,必须是第一个位置的参数
5.2.5. 前置通知
使用前置通知和JoinPoint在方法执行前加入新功能。
- 该方法无返回值
- 可以有一个参数JoinPoint
1 |
|
5.2.6. 后置通知
使用后置通知查看目标方法返回值,但是不能修改。returning属性值和第切面方法第二个参数的名字一样,切面方法中的第二个参数即为返回值。
- 该方法无返回值
- 有一个Object类型的参数,即为目标方法的返回值
- 不能修改返回值
1 |
|
5.2.7. 环绕通知
该通知为最强的通知,可以在目标方法之前或之后添加功能,还可以修改目标方法的返回值,并且能够控制目标方法是否被调用,是否被执行!
- 该方法只有个固定的参数ProceedingJoinPoint ,该参数能够获取目标方法的参数或执行等等
- 必须要有一个返回值!(推荐Object)
1 |
|
5.2.8. 异常通知
异常通知方法的定义格式:
- public
- 没有返回值
- 方法名称自定义
- 方法有一个Exception,如果还有是JoinPoint
该方法会在目标方法抛出异常时执行,可以用来做异常的监控程序,检测目标方法执行时是否有异常
throwing属性值为Exception参数
1 |
|
其执行方式类似于以下代码
1 | try{ |
5.2.9. 最终通知
最终通知总是会执行,并在目标方法之后执行
1 |
|
其执行方式类似于以下代码
1 | try{ |
5.2.10. 定义和管理切入点
使用@Pointcut来定义和管理切入点
1 | /** |
当其他方法要使用该切入点时,只需使用该方法名就可以了
1 |
|
5.2.11. cglib代理方式
如果目标类有接口,就是使用jdk的动态代理,如果没有接口,就会使用cglib代理
如何在目标类有接口时也使用cglib呢?需要在声明自动代理生成器的时候指定 proxy-target-class=”true”
1 | <aop:aspectj-autoproxy proxy-target-class="true"/> |
6. spring整合mybatis
用的技术ioc,因为ioc能创建对象,可以把mybatis框架中的对象交给spring统一创建,开发人员从spring中获取对象。
6.1. 整合步骤
- 新建Maven并加入依赖
1 | <!--spring依赖--> |
- 创建实体类
1 | public class Student { |
- 创建dao接口和mapper文件
1 | public interface StudentDao { |
1 |
|
- 创建mybatis主配置文件,指定map文件所在包,不需要在此文件中指定数据库了,只需指定map文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<configuration>
<!--settings控制mybatis全局行为-->
<settings>
<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--sql映射文件的位置,classes文件夹内-->
<mappers>
<package name="com.hjznb.dao"/>
</mappers>
</configuration> - 创建Service接口和实现类,属性是dao
1 | public interface StudentService { |
1 | public class StudentServiceImpl implements StudentService { |
- 创建spring的配置文件:声明mybatis的对象交给spring创建:数据源、Dao对象、SqlSessionFactory、声明自定义的service
1 |
|
- 创建测试类,获取Service对象,通过service调用dao完成数据库的访问
1 |
|
7. spring事务处理
多种数据库的访问技术,有不同的事务处理机制,对象,方法。spring提供一种处理事务的同一模型,能使用统一步骤、方式完成多种不同数据库访问技术的事务处理。
7.1. 事务的类型
要使用spring的事务处理,需要给你的业务方法说明需要事务的类型,需要指明:
- 事务的隔离级别
- 事务的超时时间
- 事务的传播行为
7.1.1. 隔离性
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
spring事务的隔离级别有:
- DEFAULT (默认):这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。
- READ_UNCOMMITTED(读未提交数据):允许事务读取未被其他事务提交的变更数据,会出现脏读、不可重复读和幻读问题。
- READ_COMMITTED(读已提交数据):只允许事务读取已经被其他事务提交的变更数据,可避免脏读,仍会出现不可重复读和幻读问题。
- REPEATABLE_READ(可重复读):确保事务可以多次从一个字段中读取相同的值,在此事务持续期间,禁止其他事务对此字段的更新,可以避免脏读和不可重复读,仍会出现幻读问题。
- SERIALIZABLE(序列化):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,可避免所有并发问题,但性能非常低。
7.1.2. 传播行为
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
spring定义了7中传播行为,需要在propagation属性中指明
重点掌握1、2和4
7.1.3. 事务的超时时间
表示一个方法的最长执行时间,超过时间就回滚,单位是秒,默认为-1,没有时限。
7.2. spring框架中提供的事务处理方案
7.2.1. 使用注解@Transactional
步骤:
- 声明事务管理器对象
1 | <!--声明事务管理器--> |
- 开启事务注解驱动,告诉spring框架,我们要用注解的方式管理事务,注意:约束文件处是spring-tx.xsd!!
1 | <!-- |
- 在方法的上面加入注解@Transactional
使用格式:放在方法上面,在属性中指明传播行为,隔离级别,回滚原因,是否为只读。
1 |
|
当不指明属性值的时候,spring默认使用上面的值,但是遇到异常时就回滚。
1 | //默认值为上面的值,遇到运行时异常都会回滚 |
spring是使用aop机制,给注解所在类创建代理对象,加入注解功能,在业务方法执行之前先开启事务,在事务方法之后提交或回滚事务,使用aop的环绕通知。
7.2.2. 使用aspectj框架配置事务
注解适用于小型项目,aspectj适用于大型项目,在spring配置文件中声明类、方法需要的事务。这种方式业务方法和事务完全分离。
使用步骤:
- 加入aspectj依赖
- 声明事务管理器
1
2
3
4
5<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接的数据库-->
<property name="dataSource" ref="myDataSource"/>
</bean> - 声明方法的事务属性(隔离级别、传播行为、超时)
1 | <!-- |
- 指定aop,指定哪些类要创建代理,因为此时只是指定了方法名和事务,并没有说给哪个类
1 | <!--配置aop--> |
8. 解决类目录中没有spring配置文件
在pom中加入插件依赖
1 | <resources> |