一、Spring的第一个核心功能ioc
- IoC (Inversion of Control):控制反转,是一个理论,概念,思想。
- 控制:创建对象,对象属性的赋值,对象之间的关系管理。
- 反转:把开发人员创建对象的权限转移给代码之外的容器,由容器代替开发人员管理对象,创建对象,给属性赋值。
- 为什么要用Ioc:目的是减少对代码的改动,实现不同的功能,解耦合。
二、 DI是ioc的技术实现
- DI(Dependency Injection):依赖注入,只需要在程序中提供要使用的对象名就可以,至于对象是怎么创建的,赋值的,找到的,都由容器内部实现。
- Spring是使用了di实现了ioc的功能,spring底层创建对象,使用的是反射机制。
三、创建Spring的配置文件(基于XML的DI)
- 一般在main目录下的resources文件夹里创建一个Spring config的xml文件,文件名一般叫applicationContext.xml。
- spring的配置文件
- beans:是根标签,spring把java对象称为bean。
- spring-beans.xsd是约束文件,和mybatis中的dtd是一样的。
- 声明bean就是告诉spring要创建某个类的对象
- id:对象的自定义名称,唯一值,spring通过这个id找到对象
- class:类的全限定名称(必须是类)
- spring就完成MyService myService = new ServiceImpl();
spring把创建好的对象放到map里,spring框架有一个map存放对象。
springMap.put(id的值,对象);
例如 springMap.put(“myService”,new ServiceImpl()); - 一个bean对应一个对象。
<bean id="MyService" class="com.roxic.service.impl.ServiceImpl"></bean>
- spring还可以创建一个非自定义对象(如SpringBuilder,Date)
<bean id="myDate" class="java.util.Date"></bean>
- spring对象注入的两种方式(在bean标签里面)
(1)set注入:
<!--简单类型的set注入-->
<property name="属性名称" value="属性值">
<!--引用类型的set注入-->
<property name="属性名称" ref="bean的id">
(2)构造注入:
constructor-arg标签属性:
name:表示构造方法里的形参名
index:表示构造方法的参数位置,从左到右是0,1,2…的顺序
value:构造方法的形参是简单类型,用value
ref:构造方法的形参如果是引用类型,用ref
<constructor-arg name="ename" value"史密斯"/>
<constructor-arg name="job" value"售货员"/>
(3)引用类型的自动注入:spring根据某些规则给引用类型赋值
① byName(按名称注入):
java类中引用类型的属性名和spring中bean标签的id一样,且数据类型一致。
<bean id="xxx" class="yyy" autowire="byName">
简单类型属性赋值
</bean>
② byType(按类型注入):
java类中引用类型的数据类型和spring中bean标签的class一样,是同源的。(class值相同/class值是父子类关系/class值是接口和实现类)
<bean id="xxx" class="yyy" autowire="byType">
简单类型属性赋值
</bean>
- 注意:在byType中,在xml配置文件中声明的bean只能有一个class值符合条件的,不然会出错(NoUniqueBeanDefinitionException)
四、使用多个spring配置文件(基于XML的DI)
- 当一个项目中有多个模块,或者有成百上千个类,某些类可以概括为一个功能,这个时候如果将所有类的bean都写在一个配置文件里会使单个配置文件大小非常大,而且会引起多人编写代码的冲突。
- total.xml表示主配置文件,包含其他配置文件,一般来说不创建对象。
语法:<import resource="其他配置文件的路径" />
关键字classpath:表示类路径(class所在的目录),在spring配置文件中指定其他文件的位置,需要用到classpath,告诉spring去哪里读取类文件。
<import resource="classpath:spring-xxxx.xml" />
<import resource="classpath:spring-xxxx.xml" />
<!--
* 通配符,表示任意字符,且必须要有上级目录才能使用 * 通配符。
主配置文件的名称不能包含在通配符的范围内。否则会死循环。
-->
<import resource="classpath:/xxx/spring-*.xml" />
五、基于注解的DI(常用的)
- 通过注解完成java对象创建,属性赋值。
1. @Component:创建对象的,等同于bean
属性:value就是对象的名字,就是bean的id
value的值是唯一的。
创建一个spring配置文件,声明组件扫描器component-scan,组件就是java对象。
base-package:指定注解在你项目中的包名。
conponent-scan工作方式:spring会扫描遍历指定的包,把包中和子包中的所有类,找到类中的注解,按照注解的内容创建对象,给属性赋值。
2. @Respotory(持久层)
- 放在dao的实现类上的,表示创建dao对象,能访问数据库。(使用语法和Component相同,还有额外的功能)
3. @Service(业务层)
- 放在service的实现类上面,创建service对象,service对象是做业务处理的,可以有事务等功能。(使用语法和Component相同,还有额外的功能)
4. @Controller(控制器)
- 放在控制器(处理器)类的上面,创建控制器对象,控制器对象能够接受用户提交的参数,显示请求的处理结果。(使用语法和Component相同,还有额外的功能)
5. @Value
- 简单类型赋值
特点:value是String类型的,表示简单类型的属性值。
位置:在属性定义的上面,不用set方法。或者在set方法上面。
@Value(value="xxx")
6. @Autowired
- 引用类型赋值
使用的是自动注入原理,支持byName,byType。
默认使用的是byType自动注入,byName要配合@Qualifier使用。
使用位置:属性定义的上面,或set方法上面。
属性rquired默认为true,若设置为true,如果找不到对象,就会报错。若设置为false,如果找不到对象,不会报错,会给该引用类型附个null。
7. @Resource
- 引用类型赋值(来自于JDK,spring提供了对这个注解的功能支持。)
使用的是自动注入原理,支持byName,byType。
默认使用的是byName自动注入,先使用byName自动注入,如果根据Name没找到对象,再自动使用byType。如果要设置只用byName,加个属性name,name是@Component的value属性值。
使用位置:属性定义的上面,或set方法上面。
六、注解与XML的对比
-
XML
优点:分离彻底,不需要改动源代码,只需要在XML文件中修改。对于经常发生改变的内容,可以用XML文件。
缺点:写起来效率低,和代码完全分开,需要一边看源代码,对应起来看,不够直观清晰。 -
注解
优点:写起来很快,很方便,省心,不用记很多语法。可读性比较好。
缺点:注解放在源代码里,会看上去比较乱,而且如果要改动,需要重新编译,不需要经常改变可以使用注解。
七、AOP(Aspect Orient Programming)
1. 动态代理
(1)实现方法:
①jdk动态代理,使用jdk中的proxy,method,invocationHandler创建代理对象。jdk动态代理要求目标类必须实现接口。
②cglib动态代理,第三方的工具库,创建代理对象,原理是继承。通过继承目标类,创建子类,子类就是代理对象。所以目标类不能是final,方法也不能是final。
(2)动态代理的作用:
①在目标类源代码不改动的前提下,增加功能。
②减少代码的重复,避免冗余。
③专注业务逻辑的代码。没有事务内容干扰。
④解耦合,让业务和非业务功能分离。
2. AOP:面向切面编程,基于动态代理,可以用jdk,也可以用cglib。
AOP就是动态代理的一种规范化,把动态代理的实现步骤,方法都定义好了,让开发人员用一种统一方式来使用动态代理。
(1)Aspect:切面,给目标类增加的功能,就是切面。一般是非业务功能。
(2)Orient :面向。
(3)Programming:编程
- 怎么理解面向切面编程?
(1)分析项目功能,找出切面。
(2)合理安排切面的执行时间。
(3)合理安排切面的执行位置。(哪个类,类里的哪个方法)
3. AOP中的术语
(1)Aspect:切面,表示增强的功能,完成某个任务。非业务功能,常见的切面有日志,事务,统计信息,参数检查,权限验证。
(2)JoinPoint:连接点,连接业务方法和切面的位置(哪个类中的哪个方法)
(3)PointCut:切入点,指多个连接点方法组合在一起的集合。(很多方法)
(4)目标对象:在某个类中使用切面,这个类就是目标对象。
(5)Advice:通知,切面功能执行的时间。(执行在目标方法前还是后)
4. AOP的实现
AOP的技术实现框架:
(1)spring:spring在内部实现了AOP规范,能做AOP工作。spring主要在事务处理时用AOP,但是实际开发中用的比较少,因为spring的aop比较笨重。(用起来很麻烦)
(2)aspectJ: 一个开源的专门做aop的框架。spring框架中集成了aspectJ,通过spring就可以用aspectJ。
- aspectJ实现AOP的两种方式:
①使用XML配置文件,配置全局事务。
②使用注解,有5个注解。
八、aspectJ框架的使用
1. 切面的执行时机(Advice【通知,增强】)
- 在aspectJ中用注释表示的,有五个。也可以用XML配置文件中的标签。
- 在Advice后的切面方法中有JoinPoint参数,可以获得该切面方法切入的业务方法的方法名,形参等信息。(JoinPoint必须放在形参的第一个位置)
(1)@Before
- 在目标方法之前执行
(2)@AfterReturning
- 在目标方法之后执行,并且能通过returning属性值拿到目标方法的返回值
@AfterReturning(value = "execution(* someEmploee(..))", returning = "object")
public void myAfterReturning(Object object){
}
(3)@Around
- 功能最强的通知。
- 在目标方法的前后都能功能增强。
- 控制目标方法是否被调用执行。
- 修改原来的目标方法的执行结果,可以影响最终结果。
- 等同于JDK动态代理的InvocationHandler接口。
参数:ProceedingJoinPoint等同于Method,为了执行目标方法。
注意:ProceedingJoinPoint继承JoinPoint,可以使用JoinPoint中的方法,获得方法名,形参值等。
返回值:目标方法的返回值,可以被修改。
(4)@AfterThrowing
- 在目标方法抛出异常时执行,有throwing属性值。
(5)@After
- 在目标方法执行之后(即使抛异常),一定会执行。一般用来做资源的清除工作。
2. aspectJ的切入点表达式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
- modifiers-pattern?:访问权限类型,可有可无。
- ret-type-pattern:返回值类型。
- declaring-type-pattern?:方法的包名,可有可无。
- name-pattern:方法的名字。
- (param-pattern):方法的形参。
- throws-pattern?:方法抛出的异常,可有可无。
(1)表达式可以概括为四个部分:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
各个部分用空格分开,其中可以用以下符号:
符号 | 意义 |
---|---|
* | 任意个字符 |
.. | 用在形参中,表示任意个形参。 用在包名后,表示当前包以及其子包路径。 |
+ | 用在类名后,表示当前类及其子类。 用在接口后,表示当前接口及其实现类 |
例子:
①execution(public * *(..))
指定切入点为:任意的公共方法。
②execution(* set*(..))
指定切入点为:任何一个以set开头的方法。
③execution(* com.roxic.dao.*.*(..))
指定切入点为:在com.roxic.dao包下的所有类中的所有方法。
④execution(* com.roxic.dao..*.*(..))
指定切入点为:在com.roxic.dao包下或子包的所有类中的所有方法。
⑤execution(* ..dao.*.*(..))
指定切入点为:在所有包里的service子包里所有类的所有方法。
⑥execution(* *.dao.*.*(..))
指定切入点为:一级包下面的service子包下所有类的所有方法。
3. 使用aspectJ实现AOP的基本步骤
(1)创建切面类【普通类】
①在类上面加上 @Aspect
②在类中定义切面要执行的功能代码。
③在方法上加入aspectJ的通知注解,比如@Before
④指定切入点表达式execution()
(2)创建spring的配置文件
①声明目标对象
②声明切面对象
③声明aspectJ框架里的自动代理生成器标签,用来完成代理对象的自动创建功能。
<aop:aspectj-autoproxy />
这个标签会把spring容器中所有的目标对象一次性生成代理对象。
(3)从spring容器中获取目标对象(实际上是代理对象)
通过代理对象执行方法,实现AOP的功能增加。
九、spring的事务
1. 什么是事务?
在mysql中提出了事务,事务是指一组sql语句的集合,集合中有许多sql语句,可能是插入,更新,删除,我们希望这些多个sql语句都能执行成功,或者都执行失败,这些sql语句的执行是一致的,作为一个整体。
2. 什么时候使用事务?
在数据库操作时,涉及到多个表,或者多条sql语句的插入,更新删除操作。需要保证这些语句一致成功或一致失败。
3. jdbc和mybatis处理事务
jdbc:Connection conn; conn.commit(); conn.rollback();
mybatis:SqlSession.commit(); SqlSession.rollback();
4. jdbc和mybat处理事务的弊端,不足
(1)不同的数据库访问技术,处理事务的对象不同。
(2)需要掌握多种数据库中的事务处理逻辑(什么时候提交,什么时候回滚)
(3)需要知道处理事务的多种方法。
5. 怎么解决不足
spring提供了一种处理事务的统一模型。能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。
6. 通过spring处理事务该怎么做
- spring处理事务的模型,使用的步骤是固定的。把事务使用的信息提供给spring就行了。
(1)事务内部提交,回滚事务,使用事务管理器对象,代替开发人员完成commit,rollback。事务管理器是一个接口,它有很多实现类。
(2)说明方法需要的事务信息:
①事务的隔离级别:
READ_UNCOMMITTED:读未提交
READ_COMMITTED:读已提交
REPEATABLE_READ:可重复度
SERIALIZABLE:串行化
DEFAULT:数据库默认的隔离级别
②事务的超时时间
表示一个方法的最长执行时间,如果方法执行超过了时间,事务就回滚,单位是秒,默认是-1
③事务的传播行为
控制业务方法是不是有事务的,是什么事务。
(3)提交事务,回滚事务的时机
①当业务方法执行成功,没有异常抛出,方法执行完毕后,spring在方法执行后提交事务,调用事务管理器的commit。
②当业务方法抛出运行时异常或者ERROR,spring执行回滚,调用事务管理器的rollback。
③当业务方法抛出非运行时异常,主要是受查异常(编译时异常),提交事务。
7. spring框架中提供事务的处理方案
(1)使用Spring框架的AOP注解管理事务,适合中小项目使用
-
spring框架中自己用AOP实现给业务方法增加事务的功能。@Transactional注解增加事务,放在public方法的上面,表示当前方法具有事务,可以给注解的属性赋值,设置隔离级别,传播行为,异常信息。
-
使用@Transactional的步骤:
①声明事务管理器对象
<bean id="xxx" class="DataSourceTransactionManager">
<!--指定数据源-->
<property name="dataSource" ref="myDataSource"
</bean>
②在目标业务方法上面加上@Transactional
③开启事务注解驱动,告诉spring,要使用注解的方式管理事务。spring使用自己的AOP机制,创建@Transactional所在的类的代理对象,给代理对象的对应方法加入事务功能,在指定加入事务的业务方法执行之前,先开启事务,在业务方法之后提交或回滚,用的是AOP的环绕通知。
<!--事务注解驱动-->
<!--注意!annotation-driven一定是选tx包下的annotation-driven,这是专门用来做事务的驱动,不要选错了!-->
<tx:annotation-driven transaction-manager="xxx" />
(2)使用AspectJ框架的AOP配置管理事务,适合大型项目使用
-
大型项目中有非常多的类和方法,需要大量事务配置,这种方案,业务方法和事务的配置是完全分离的。
-
使用AspectJ框架的AOP配置管理事务的步骤:
①声明事务管理器对象
<bean id="xxx" class="DataSourceTransactionManager">
<!--指定数据源-->
<property name="dataSource" ref="myDataSource"/>
</bean>
②声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时等属性】)
③配置AOP,指定哪些类需要事务。
<tx:advice id="myAdvice" transaction-manager="xxx">
<!--tx:attributes:配置事务属性-->
<tx:attributes>
<!--tx:method :给具体方法配置事务属性,可以有多个,给不同的方法设置事务-->
<!--name:方法名称,完整方法名,不带包和类,可以用通配符*,表示任意字符-->
<tx:method name="" propagation="" isolation="" rollback-for=""/>
</tx:attributes>
</tx:advice>
<!--指定哪些包中的类需要使用事务-->
<aop:config>
<!--配置切入点表达式,项目中所有包中有service包及其子包下的所有类的所有方法-->
<aop:pointcut id="servicePt" expression="execution(* *.. service..*.*(..))" />
<!--将配置好的pointcut和advice关联-->
<aop:advisor advice-ref="myAdvice" pointcut-re="servicePt" />
</aop:config>