1、ioc的技术实现、
- di是ioc的技术实现
- di(dependency injection)依赖注入,只需要在程序中提供要使用的对象名称就可以,至于对象如何在容器中创建赋值,查找都由容器内部实现。
- spring是使用的di实现了ioc的功能,spring底层创建对象,使用的是反射机制
- spring就是一个容器,管理对象,给对象的属性赋值,底层是反射创建对象。
- spring将创建好的对象放入到map中,spring框架有一个map存放对象
- spring会将xml文件中的所有bean标签创建成对象,存放到map中
- spring不仅可以创建自定义对象,也可惜创建系统带的对象的实例
- spring-context和spring-webmvc是spring中的两个模块
- spring-context是ioc功能的,负责创建对象
- spring-webmvc做web开发使用,是servlet的升级
- spring-webmvc中也会用到spring-context中创建对象的功能的
2、 spring容器中的方法
String names[] = ac.getBeanDefinitionNames()
获取容器中所有对象的名称int nums = ac.getBeanDefinitionCount()
获取容器中对象的总数量
3、给对象赋值
-
3.1、 di的实现有两种方法
- 在spring的配置文件中,使用标签和属性完成,叫做基于xml的di注入
- 使用spring中的注解,完成属性赋值,叫做基于注解的di注入
-
3.2、di的语法分类
- set注入(设置注入):spring调用类的set方法,在set方法可以实现属性的赋值80%左右的都是使用的set注入
- set简单类型的注入(此时,被设置的类中不能有有参数构造方法,不然会报错)
-
<bean id="stu" class="com.xxx.xxx"> <property name="name" value="song"/> <property name="age" value="20"/> //因为这里是xml文件,所以必须符合xml文件,即使是int类型也要加引号 </bean>
- set引用类型注入
-
<bean id="myschool" class="com.xxx.School"> <property name="name" value="清华"/> <property name="address" value="北京"/> </bean> <bean id="stu" class="com.xxx.Student"> <property name="school" ref="myschool"/> </bean>
- 构造方法注入,spring调用类的有参数构造方法,床架嫩对象,在构造方法中完成赋值。
-
<bean id="stu" class="com.xxx.Student"> <constructor-arg name="name" value="song" /> <constructor-arg name="school" ref="myschool"> </bean> <bean id="stu" class="com.xxx.Student"> <constructor-arg index="0" value="song" /> <constructor-arg index="1" ref="myschool"> </bean>
- set注入(设置注入):spring调用类的set方法,在set方法可以实现属性的赋值80%左右的都是使用的set注入
-
引用类型的自动注入(byName(按名称注入)和byType(按类型注入))
-
byName:按名称注入,java类中 引用该类型的属性名和spring容器中(配置文件)bean的id名称一样,且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型
-
byType:按类型注入:java类中引用类型的数据类型和spring容器中bean的class属性是同源关系,这样的bean能够直接赋值给引用类型
-
//byName <bean id="school" class="com.xxx.School"> <property name="name" value="清华"/> <property name="address" value="北京"/> </bean> <bean id="stu" class="com.xxx.Student" autowire="byName"> <property name="name" value="song"/> //<property name="school" ref="myschool"/> </bean> //byType <bean id="school" class="com.xxx.School"> <property name="name" value="清华"/> <property name="address" value="北京"/> </bean> <bean id="stu" class="com.xxx.Student" autowire="byType"> <property name="name" value="song"/> //<property name="school" ref="myschool"/> </bean>
4、如何解决配置一个配置文件中bean声明过多
- 创建多个spring.xml文件,用一个total.xml文件将其他配置文件包含,在加载时,只加载这一个,就能加载所有
多个xml文件之间通过
< import resource=“classpath:com.xxx.xxx”/>
< import resource=“classpath:bao/spring-*” />//注意要使用通配符的时候,这些xml必须放到一个文件夹中,并且总文件不能和其他xml文件的前缀相同,不然会重复加载一直循环
5、基于注解的di实现
6、aop
6.1、实现方法:
- jdk动态代理,使用jdk中的proxy,method,invocationHanderl创建对象。jdk动态代理要求目标类必须实现接口。
- cglib动态代理:第三方的工具库,创建代理对象,原理是继承,通过继承目标类,创建子类。子类就是代理对象。要求目标类不能是final的,方法也不能是final的
6.2、动态代理的作用
- 在目标类源代码不改变的情况下,增加功能
- 减少代码的重复
- 解耦合,让你的业务功能和日志,事务非业务功能分离
6.2、什么是aop
- 基于动态代理的,可以使用jdk,cglib两种代理方式。
- aop就是动态代理的滚繁华,把动态代理的实现步骤,方式都定义好,让开发人员用一种统一的方式使用动态代理
6.3、aop(aspect orient programming)面向切面编程
- aspect:切面,给你的目标类增加功能,就是切面。切面的特点:一般都是非业务方法,独立使用的。
- orient:面向,对着
- programming:编程
- oop(object orient programming)面向对象编程
6.4、术语理解
- aspect:切面,表示增强的功能,就是一堆代码,完成某一个功能,非业务功能,常见的切面功能有日志,事务,统计信息,参数检查,权限验证
- joinpoint:连接点,连接业务方法和切面的位置,就某个类中的业务方法
- pointcut:切入点,指多个连接点方法的集合。多个方法
- 目标对象:给哪个类的哪个方法增加功能,这个类就是目标对象
- advice:通知,通知表示切面功能执行的时间
6.5、一个切面三个关键的要素:
- 切面的功能代码,切面干什么
- 切面的执行位置,使用pointcut表示切面执行的位置
- 切面的执行时间,使用advice表示时间,在目标方法之前,还是目标方法之后
6.6、学习spectj框架的使用
6.6.1、切面的执行时间,这个执行时间在规范中叫做advice(通知,增强)在aspectj中使用注解表示。
- @Before
- @AfterReturning
- @Around
- @AfterThrowing
- @After
6.6.2、切入点表达式
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
通配符介绍
符号 | 意义 |
---|---|
* | 0至多个字符 |
… | 用在方法参数中,表示任意多个参数,用在包名后,表示当前包及其子包路径 |
+ | 用在类名后,表示当前类及其子类,用在接口后,表示当前接口及其实现类 |
表达式 | 解释 |
---|---|
execution(public * *(…)) | 指定切入点为任意公共方法 |
excution(* set*(…)) | 任意一个以set方法开始的方法 |
execution(* com.xxx.service..(…)) | 定义在service包下的任意类的任意方法 |
execution(* com.xxx.service….(…)) | 定义在service中的子包里的任意类的方法。 |
execution(* …service..*(…)) | 所有包下的所有子类接口中所有的方法 |
7、aspectj的使用
- 在类的上面加@Aspect
-
@Aspect public class MyAspect{ @Before(value="execution(Joinpoint jp)") //Joinpoint jp 就是获取参数,通过Object args[] = jp.getArgs()可以获取所有参数 public void myBefore(){ System.out.println("song") } //@AfterReturning:后置通知, //value 切入点表达式,returning自定义变量,表示目标方法的返回值,自定义变量名必须和普通方 法的形参名相同 @AfterReturning(value=("execution(* *..SomeServiceImpl.*(..))") returning= "res") public void myAfterReturning(Object res){ } //@Around环绕通知:在前和后都能使用,等同于jdk动态代理 @Around(value="execution(* *..SomeServiceImpl.*(..))") public void myAround(ProceedingJoinpoint pjp){ System.out.println("在目标方法前执行") Object result = pjp.proceed();//目标方法执行,并能获取目标方法的结果 System.out.println("在目标方法后执行") } //@AfterThrowing异常通知,在目标方法报错时,执行的方法 @AfterThrowing(value="execution(* *..SomeServiceImpl.*(..))") public void myAfterThrowing(Expection ex){ System.out.println("我出错了") } //@After最终通知,总是会执行 @After(value="execution(* *..SomeServiceImpl.*(..))") public void myAfter(){ System.out.println("我执行了") } //@Pointcut:定义和管理切入点,如果你的项目中有多个切入点表达式是是重复的,可以复用的,可以使用Pointcut @Pointcut(value="execution(* *..SomeServiceImpl.*(..))") public void myPointcut(){ } 这里的myPointcut就表示这个包的别名,以后谁用直接用myPointcu't } // 然后再spring-config配置自动代理生成器,使用aspectj框架内部功能,创建目标对象的代理对象 //创建代理对象实在内存中实现的,修改目标对象的内存中的结构创建为代理对象所以目标对象是就是被修改后的代理对象 <aop:aspectj-autoproxy/>
8、jdk动态代理和cglib动态代理
- jdk动态代理要求目标类实现接口
- cglib动态代理要求目标类继承
通过一下代码可以查看用的什么代理方式,其实有接口也可以使用cglib动态代理
只需要在spring-config配置文件中将
<aop:aspectj-autoproxy proxy-target-class="true"/>修改成这样就可以了
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
System.out.println(ac.getClass().getName())
8、如何在spring配置文件中引入mybatis数据库properties呢
<context:property-placeholder location="classpath:jdbc.properties"/>
9、spring事务相关问题
9.1、什么是事务
- 事务是指一组sql语句的集合,集合中有多条sql语句,我们希望这些sql语句都能成功,或者都失败,这些sql语句的执行是一致的,作为一个整体执行
9.2、在什么时候使用事务
- 当我的操作涉及到多个表,或者多个sql语句,需要保证这些语句都能成功,后者都能失败,保证操作是符合要求的,在java代码中控制事务,放到service的业务上
9.3、通常使用jdbc访问数据库,还是mybatis访问数据库怎么处理事务
- jdbc访问数据库,处理事务:Connection conn;conn.commit();conn.rollback()
- mybatis访问数据库,处理事务:SqlSession.commit();SqlSession.rollback();
9.4、3问题中事务的处理方式,有什么不足
- 不同的数据库,处理事务的方法不同,需要了解不同数据库访问技术使用事务的原理
- 掌握多种数据库事务处理逻辑,什么时候提交事务,什么时候回顾事务
- 处理事务的多种方法
- 多种数据库访问数据库技术不同
9.5、怎么解决不足
- spring提供了一种处理事务的统一模型,能够使用一统一的步骤,方式完成多种数据库访问技术的事务处理,使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理
- 使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理机制
9.6、处理事务,需要做什么,怎么做
-
spring处理事务的模型,使用的步骤都是固定的,把事务使用的信息提供给spring就可以了。
-
事务内部提交,回滚事务,使用的事务管理器管理对象,代替你完成commit,rollback事务管理器是一个接口和他的众多实现类
-
接口:PlatFormTransactionManager,定义了事务重要方法commit,rollback
-
实现类:spring把每一种数据库访问技术对象的事务处理类都创建好了
-
怎么使用:你需要告诉spring你用的是那种数据库的访问技术,怎么告诉spring呢,声明数据库访问技术对于事务管理器实现类,在spring的配置文件中使用bean声明就可以了
-
你的业务方法需要什么样的事务,说明需要的事务的类型
-
说明方法需要的事务:
-
定义五个事务的隔离级别常量(主要控制在高并发下对数据安全性的控制)
- 这些常量户以ISOLATION开头,即形如ISOLATION_XXX
- DEFAULT:采用DB默认的事务隔离级别。MySql的默认为REPEATABLE_READ;Oracle默认为READ_COMMITTED。
- READ_UNCOMMITTED:读未提交。为解决而任何并发问题。
- READ_COMMITTTED:读已提交。解决脏读,存在不可重复读与幻读。
- REPEATABLE_READ:可重复读,不可重复读,存在幻读。
- SERIALIZABLE:串行化,不存在并发问题。
-
事务的超时时间:表示一个方法最长的执行时间,如果方法执行时超过了时间,事务回滚。单位是秒,整数值,默认是-1
-
事务的传播行为(重要):控制业务方法是不是有事务的,是什么样的事务。7个传播行为,表示你的业务方法调用时,事务在方法之间是如何使用的。
- PROPAGATION_REQUIRED
- 指定方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新的事务。这种传播行为是最常见的选择,也是spring默认的事务传播行为。例如:如果传播行为加载doOther()方法上。若doSome()方法在调用doOther()方法时就是在事务内运行的,则doOther()方法的执行也加入到该事务内执行。若doSome()方法在调用doOther()方法时没有在事务内执行,则doOther()方法会创建一个事务,并在其中执行。
- PROPANGATION_REQUIRES_NEW
- 总是新建一个事务,若当前存在事务,就讲当前事务挂起,知道新事物执行完毕
- PROPAGATION_SUPPORTS
- 指定的方法支持当前事务,但若当前的没有事务,也可以以事务方式执行
- 掌握以上三个
- PROPAGATION_MANDATORY
- PROPAGATION_NESTED
- PROPAGATION_NEVER
- PROPAGATION_NOT_SUPPORTED
- PROPAGATION_REQUIRED
10、spring提交事务,回滚事务的时机
- 当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务commit
- 当你的业务方法抛出运行时异常,spring执行回滚,调用事务管理器的rollback,运行时异常定义:RuntimeException 和他的子类都是运行时异常例如:NullPointException,NumberFormatException异常
- 当你的业务方法抛出非运行时异常,只要是受查异常,提交事务,受查异常:在你写代码中,必须处理的异常。
11、sping事务总结
- 管理事务的是 事务管理和他的实现类
- spring的事务是一个统一的模型
- 指定要使用的事务管理器实现类,使用bean
- 指定哪些类,哪些方法需要加入事务的功能
- 指定方法需要的隔离级别,传播行为,超时
- 你需要告诉spring,你的项目中类的信息,方法的名称,方法的事务传播行为。
12、spring事务使用
- 适合中小项目使用的,注解方案,spring框架自己用aop实现业务方法增加事务的功能,使用@Transactionl注解增加事务。
- @Transactional注解是spring框架自己注解,放在public方法的上面,可以给注解的属性赋值,表示具体的隔离级别
- 使用@Transactional的步骤:
- 1,需要声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">指定数据源,</bean>
- 开启事务注解驱动,告诉spring框架,我要使用注解的方式管理事务,spring使用aop机制,创建@Transactional所在的类对象,给方法加入事务的功能。spring给业务方法加入事务,在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知
<tx:annotation-driven(tx) transaction-manager="transactionManager"/>
- 声明完之后,找到要添加事务的方法,在方法的上面
@Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false, rollbackFor = {//表示发生指定的异常一定回滚 NullPointException.class, NotEnoughException.class } ) public void buy(){ }
13、aspectj大型项目的事务配置
<bean id="transactionManager" class="DataSourceTransactionManager">指定数据源,</bean>
-
<tx:advice id="myadvice" transaction-manager="transactionManager"> //配置事务属性 <tx:attrributes> //给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性 //name:方法名称,完成的方法名,不带包和类,方法可以使用通配符,表示任意字符 //propagation:传播行为,枚举值 //isolation:隔离级别 //rollback-for:你指定的异常类,全限定名称发生异常一定回滚。 <tx:method name="buy" propagation=" REQUIRED" isolation=" DEFAULT" rollback-for="XXXX "/> //使用通配符,制定很多方法 <tx:method name="add*" propagation="REQUIRES_NEW"/>//所有以add开头的方法都适用 <tx:method name="*" propagation="SUPPORTS" read-only="true"/> </tx:attributes> </tx:advice> //有了以上这些还不够,因为开发的时候,会有不同的包,不同包下可能会有相同名称的方法,这样就会造成冲突,所以要配置相应的aop <aop:config> //配置切入点表达式:指定那些包中类,要使用事务, //id:切入点表达式的名称,唯一值 //expression:切入点表达式,指定那些类要使用事务,aspectj会创建代理对象 <aop:pointcut id = " servicept" expression=" exectution(* *..service..*.*(..))"/> <aop:advisor advice-ref="myAdvice" pointcut-ref="servicept"/> </aop:config>