1.Spring中主要的两个方面:IOC和AOP。。
一、IOC
对于整个框架的编写流程分为三步:
1)导包
2)写配置
3)测试
1.导包
对于最基本的就是导包,然后进行buildpath(并且要注意版本问题)
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
和日志包commons-logging-1.2.jar
连接数据库所用到的jar包:
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar(连接数据库的辅助包)
mysql-connector-java-8.0.18.jar(对应数据库的版本)
支持注解要用到的jar包:spring-aop-4.0.0.RELEASE.jar
Spring单元测试包:spring-test-4.0.0.RELEASE.jar
Spring面向切面编程的包(AOP中会用到):
spring-aspects-4.0.0.RELEASE.jar(基础版)
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
事务会用的包:
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
2、写配置
创建一个Spring Bean Configuration File,作为Spring的bean配置文件。在配置文件中可以将一些要实现的类中的内容写入配置文件中,直接在测试类中测试即可。
<bean id="person01" class="com.bean.Person">
<property name="name" value="易烊千玺"></property>
<property name="age" value="18"></property>
</bean>
其中,还可以使用一些引用的信息。
<bean id="person06" class="com.bean.Person">
<property name="maps" ref="Mymap">
```<util:map id="Mymap">
<entry key="key01" value="易烊千玺"></entry>
<entry key="key02" value="白敬亭"></entry>
</util:map>
上面的代码中实现为某个人的一个集合类型的属性赋值,引用了id为Mymap的信息。util名称空间创建集合类型的bean方便别人引用。
引用外部属性文件用来连接数据库:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/jike_db"></property>
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
</bean>
<context:property-placeholder location="classpath:dbconfig.properties"/>
<bean id="dataSource02" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc_username}"></property>
<property name="password" value="${password}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="driverClass" value="${driverClass}"></property>
</bean>
上面有两种引用方式,一种是直接将数据库的信息输入在上面,另外一种是通过依赖context名称空间将配置信息都写到文件中。classpath: 这是固定写法,表示引用类型路径下的一个资源。${}是用来时间变量插值的一种表达式。#{}是SPEL表达式。他们的作用是通过spring把值注入给某个属性。
配置中的一些信息还可以通过注解将其表示出来。例如,在MVC模式中,主要分为三层,这些类可以通过配置文件将其加入到IOC容器中,也可以通过注解@Controller(Servlet包下)@Service(业务逻辑层)@Repository(给数据库层dao层)@Component(不属于以上几层的组件添加)。但是如果要使用注解也需要一些步骤:
1)给要添加的组件上标注四个注解中的一个
2)告诉Spring,自动扫描注解的组件,这要依赖context名称空间。
<context:component-scan base-package="com.packet"></context:component-scan>
3)一定要导入aop包
3.测试
在测试类(不要命名Test,容易造成误解)中,首先是要写@Test注解,然后使用ApplicationContext获取IOC容器,通过实例化ClassPathXMLApplicationContest获取到当前配置文件路径下的IOC容器。
ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationconf.xml");
然后再写测试方法从容器中获取内容即可。
4、IOC中的注解
@controller:推荐给控制器层(Servlet包下)
@Service:业务逻辑,给业务逻辑层的组件
@repository:给数据库层(dao层)的组件添加这个注解
@Component:不属于以上几层的组件添加这个注解
将注解组件快速的加入到容器中需要:
1)给要添加的组件上标对应的注解
2)告诉Spring,自动扫描注解的组件,在配置文件中使用context名称空间
```
<context:component-scan base-package="com.packet"></context:component-scan>
@Autowired:原理就是先按照类型去容器中找到对应的组件,找到就赋值,没找到就抛异常,找到多个后按照变量名作为id继续装配(变量名是类名首字母小写),如果还报错就可以使用**@Qualifier**注解明确指定目标bean的id。
二、AOP
AOP(Aspect Oriented Programming):面对切面编程
OOP(Object Oriented Programming):面向对象编程
1.AOP
AOP是基于OOP基础之上新的编程思想;指在程序运行期间,将某段代码动态的切入到指定方法的指定位置进行运行的这种编程方式。Spring实现了AOP的功能,底层就是动态代理。
2.AOP专业术语:
3.AOP使用步骤:
1)导包
(当然不只是这些包,比如下面提到的要加入到IOC容器中就要导入相应的包)
spring-aspects-4.0.0.RELEASE.jar(基础版)
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
2)配置
1)将目标类和切面类(封装了通知方法(在目标方法前后执行的方法))加入到ioc容器中(导入相应的包),使用注解并使用context名称空间。
<context:component-scan base-package="com.packet"></context:component-scan>
2)告诉Spring到底哪个是切面类(@Aspect)
3)告诉Spring切面类里面的每一个方法都何时何地运行,使用以下通知注解:
@Before:前置通知
@After:后置通知
@AfterReturning:返回通知
@AfterThrowing:异常通知
@Around:环绕通知
具体到哪个目标方法就应该写切入点表达式(execution(访问权限符 返回值类型 方法签名)),切入点表达式表示的是在那个方法前或者后进行通知
例如:
@Before("execution(public int com.packet.impl.MyMathCalculateImpl.*(int, int))")
public static void logStart(JoinPoint joinPoint) {
Object[] args=joinPoint.getArgs();
System.out.println("【"+joinPoint.getSignature().getName()+"】方法开始执行,用的参数列表是"+Arrays.asList(args));
}
上面的代码中表示的是在MyMathCalculateImpl的所有方法执行之前都会有前置通知。其余通知添加方法相同。
@AfterReturning("execution(public int com.packet.impl.MyMathCalculateImpl.*(int, int))")
public static void logReturn(JoinPoint joinPoint,Object result) {
System.out.println("【"+joinPoint.getSignature().getName()+"】方法执行结束,结果是"+result);
}
但是有的方法中会有参数传入,必须要向Spring说明参数的意义,例如在上面的AfterReturning中输出结果中包含参数result,所以在其后添加一个说明,更新为
@AfterReturning(value="execution(public int com.packet.impl.MyMathCalculateImpl.*(int, int)",returning = "result")
其中,JointPoint封装了当前目标方法的详细信息。
抽取可重用的切入点表达式:在上述中,切入点表达式相同,为了提高代码的鲁棒性,可以抽取切入点表达式。
1)随便声明一个没有实现的返回void的空方法
2)给方法标注@Pointcut注解
@Pointcut("execution(public int com.packet.impl.MyMathCalculateImpl.*(int, int))")
public void repeat() {};
@Before("repeat()")
public static void logStart(JoinPoint joinPoint) {
Object[] args=joinPoint.getArgs();
System.out.println("【"+joinPoint.getSignature().getName()+"】方法开始执行,用的参数列表是"+Arrays.asList(args));
}
4)开启基于注解的AOP功能(aop名称空间)
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
上面是基于注解的AOP,下面介绍基于配置的AOP。
4.基于配置的AOP
<bean id="mymathcalculator" class="com.packet.impl.MyMathCalculateImpl"></bean>
<bean id="logUtils" class="com.packet.log.LogUtils"></bean>
<aop:config>
<aop:aspect ref="logUtils">
<aop:pointcut expression="execution(* com.packet.impl.MyMathCalculateImpl.*(int, int))" id="mypoint"/>
<aop:before method="logStart" pointcut-ref="mypoint"/>
<aop:after-returning method="logReturn" pointcut-ref="mypoint" returning="result"/>
<aop:after-throwing method="logException" pointcut-ref="mypoint" throwing="exception"/>
<aop:after method="logEnd" pointcut-ref="mypoint"/>
<aop:around method="myAround" pointcut-ref="mypoint"/>
</aop:aspect>
</aop:config>
首先是bean标签,将目标类和切面类加入到ioc中。
使用aop名称空间,<aop:config>
中配置指定切面,配置各个通知方法。
5.基于注解的AOP的注意事项
1.从ioc容器中拿到目标对象;如果想用类型,一定用他的接口类型。
2.切入点表达式的写法:
*:匹配一个或者多个字符
…:匹配任意多个参数,任意参数类型
有一点需要注意的是,在配置文件中写切入点表达式需要在前面加一个*
。
3.方法执行顺序:
正常执行:@Before @After @AfterReturning
异常执行:@Before @After @AfterThrowing
4.环绕通知:四合一通知
执行顺序:(环绕前置–普通前置)–目标方法执行–环绕正常返回/出现异常–环绕后置–普通后置–普通返回
5.多切面运行顺序
如果均无环绕,则谁先进来谁后出去
如果有环绕,判断环绕出现在哪个类中,如果A中有环绕:
环绕前置–A前置–B前置–方法执行–B后置–B返回–环绕返回–环绕后置–A后置–A返回
6.声明式事务
如果用JdbcTemplate需要导包
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
声明式事务:以前需要通过复杂编程来编写一个事务,替换为只需要告诉Spring哪个方法是事务方法即可。事务管理代码的固定模式作为一种横切关注点,可以通过AOP方法模块化,进而借助Spring AOP框架实现声明式事务管理。这个事务管理器可以在目标方法运行前后进行事务控制(事务切面);目前都使用DataSourceTransactionManager即可。
6.1 配置声明式事务:
1)配置出这个事务管理器(一定要导入面向切面的包)
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
2)开启基于注解的事务控制模式;依赖tx名称空间
<tx:annotation-driven transaction-manager="transactionManager"/>
3)给事务加注解。@Transactional
6.2 事务细节
异常分类:
运行时异常:可以不用处理,默认都回滚。
编译时异常:要么try-catch,要么throws,默认不回滚。
noRollbackFor:哪些异常事务可以不回滚(参数是 异常名.class)
rollbackFor:原本不回滚,
隔离级别:一个事务与其他事务隔离的程度
读未提交:Read uncommitted(会出现脏读、不可重复读、幻读)
读已提交:Read committed(会出现不可重复读、幻读)
可重复读:repeatable read:执行期间禁止其他事务对这个 字段进行更新。
串行化:Serializable:(一般不用)
数据库事务并发问题:
脏读:
Transaction01将某条记录的AGE值从20修改为30
Transaction02读取了Transaction01更新后的值
Transaction01回滚,AGE的值恢复到20
Transaction02读取到的30是一个无效的值
不可重复读:
Transaction01读取了AGE的值为20
Transaction02将AGE的值修改为30
Transaction01再次读取AGE的值,和第一次读取不一致
幻读:
Transaction01读取了Student表中的一些数据
Transaction02向Student表中插入了新的行
Transaction01读取Student表时多出了一些行
读未提交(Read uncommited):脏读、不可重复读、幻读
读已提交(Read commited):不可重复读、幻读
可重复读(repeatable read):Transaction01执行期间,禁止其他事务对这个字进行更新
串行化(Serializable):(基本是不会用到的)
查询mysql的隔离级别
select @@global.tx_isolation;(mysql8.0改为 transaction _ioslation) 注意着当中有一个很重要的点是**分号**
select@@session.tx_iso;ation; 查询当前会话的隔离级别
select @@tx_isolation;
修改mysql隔离级别:set (session/global) transaction iso;ation {read|uncommited|commited}
事务操作:
开启事务:start transaction
提交事务:commit
回滚事务:rollback
事务的传播行为:
propagation:如果有多个事务进行嵌套运行,子事务是否要和大事务共用一个事务
required:与之前的事务共用一条连接
required_new:新开一条连接
multx(){
//required
A(){
//Required_new
B(){}
//Required
C(){}
}
//Required_new
D(){
//Required
E(){
//Required_new
F(){
10/0;(E、G、A、C崩)
}
}
G(){}
}
10/0;(B成功,D整个分支下全部成功)
}
任何处崩,已经执行的required_new都会成功
如果是required,事务的属性都是继承于大事务的,而propagation=Propagation.Requires_new可以调整
默认:required
**基于xml配置的事务:**依赖tx和aop名称空间
1)Spring中提供事务管理器(事务切面),配置这个事务管理器
2)配置出事务方法
3)告诉Spring哪些方法是事务方法(事务切面按照切入点表达式去切入事务方法)
<aop:config>
<aop:pointcut expression="execution(* com.packet.*.*(..))" id="txPoint"/>
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"/>
</aop:config>
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
<tx:method name="checkout" propagation="REQUIRED"/>
<tx:method name="get *" read-only="true"/>
</tx:attributes>
</tx:advice>