1、AOP
1.1、AOP
(Aspect Oriented Programming
),面向切片编程
Spring
中使用AOP
技术封装了动态代理功能
1、在pom.xml
中添加依赖库
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
2、实现MethodBeforeAdvice
接口,用来编写额外功能(会在目标方法执行之前执行)
public class LogAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("LogAdvice --before --------------------" + method);
}
}
3、在applicationContext.xml
中配置aop
<!-- 附加代码-->
<bean id="logAdvice" class="com.sj.aop.LogAdvice" />
<aop:config>
<!-- 切入点,给哪些类的哪些方法增加附加代码-->
<!-- execution(* *(..))代表所有类的所有方法都会被切入-->
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<!-- 附加代码-->
<aop:advisor advice-ref="logAdvice" pointcut-ref="pc" />
</aop:config>
4、或者使用org.aopalliance.intercept.MethodInterceptor
接口来编写额外功能
public class LogInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("1----------------------------");
Object result = methodInvocation.proceed();
System.out.println("2----------------------------");
return result;
}
}
<bean id="logInterceptor" class="com.sj.aop.LogInterceptor" />
<aop:config>
<!-- 切入点,给哪些类的哪些方法增加附加代码-->
<!-- execution(* *(..))代表所有类的所有方法都会被切入-->
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<!-- 附加代码-->
<aop:advisor advice-ref="logInterceptor" pointcut-ref="pc" />
</aop:config>
1.2、AOP
-动态代理底层实现
1、Spring
动态代理底层实现
如果目标类有实现接口,使用JDK
实现
如果目标类没有实现接口,使用CGLib
实现
在BeanPostProcessor
的postProcessAfterInitialization
方法中创建代理对象
参照AbstractAutowireCapableBeanFactory
的applyBeanPostProcessorsAfterInitialization
方法(源码如下)
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
2、可以通过proxy-target-class
属性修改底层实现方案
true
:强制使用CGLib
false
:按照默认做法
<aop:config proxy-target-class="true">
1.3、AOP
-切入点表达式
常见的切入点指示符(pointcut designators
) 有
execution
、args
、within
、@annotation
、还可以使用&&
(and
)、||
(or
)、!
组合切入点表达式
1、execution
任意公共方法:execution
(public * *(..)
)
名字以set
开头的任意方法:execution
(* set*(..)
)
UserService
接口定义的任意方法:execution
(* com.sj.service.UserService.*(..)
)
service
包中定义的任意方法:execution
(* com.sj.service.*.*(..)
)
service
包中定义的任意方法(包括子包):execution
(* com.sj.service..*.*(..)
)
包含2个String
参数的任意方法:execution
(* *(String, String)
)
包含1个Serializable
参数的任意方法:args
(java.io.Serializable
)
service
包中的任意方法:within
(com.sj.service.*
)
service
包中的任意方法(包括子包):within
(com.sj.service..*
)
带有自定义注解(HeHe
)的方法:@annotation
(com.sj.aop.HeHe
)
2、默认情况下,目标方法相互调用时,总共只会被切入一次。解决方案:拿到代理对象去调用目标方法
@Override
public boolean login(String username, String password) {
System.out.println("09--login");
ctx.getBean("userService", UserService.class).register(username, password);
return false;
}
3、配置多个pointcut
、advisor
<aop:config>
<aop:pointcut id="pc" expression="within(com.sj.service.impl.PersonServiceImpl)"/>
<aop:advisor advice-ref="logInterceptor" pointcut-ref="pc" />
<aop:advisor advice-ref="logAdvice" pointcut-ref="pc" />
</aop:config>
2、事务管理
2.1、声明式事务
使用Spring
的声明式事务,可以及其容易地进行事务管理
只需要在XML
中进行声明配置、一般在Service
层进行事务管理
<!--事务管理器-->
<bean id="txMgr" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--附加代码-->
<tx:advice id="txAdvice" transaction-manager="txMgr">
<tx:attributes>
<tx:method name="list*"/>
<tx:method name="save*"/>
<tx:method name="update*"/>
<tx:method name="test*"/>
</tx:attributes>
</tx:advice>
<!--切面-->
<aop:config>
<aop:pointcut id="pc" expression="within(com.mj.service..*)"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc" />
</aop:config>
2.2、propagation
可以通过propagation
属性设置事务的传播行为
用于指定当事务嵌套时,如何管理事务,如下事务嵌套
tx1-begin
tx2-begin
tx2-end
tx3-begin
tx3-end
tx1-end
当Service
调用Service
时,会出现事务嵌套
<tx:method name="list*" propagation="SUPPORTS" />
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="test*" propagation="REQUIRED"/>
2.3、read-only
如果一个事务只执行读操作,那么数据库可能会采取某些优化措施
read-only
设置为true
:告诉数据库这是个只读事务
只适用于REQUIRED
或者是REQUIRED_NEW
<tx:method name="list*" propagation="REQUIRED" read-only="true"/>
2.4、time-out
单位是秒,默认是-1(默认情况处理),超时会抛出异常
<tx:method name="list*" timeout="4"/>
2.5、rollback-for
、no-rollback-for
默认情况下,RuntimeException
、Error
会导致事务回滚,而Exception
不会
rollback-for
:设置哪些异常会导致事务回滚(在RuntimeException
、Error
基础上增加一些异常)
no-rollback-for
rollback-for
:设置哪些异常不会导致事务回滚(在Exception
基础上增加一些异常)
<tx:method name="save*"
rollback-for="java.io.IOException,InterruptedException"
no-rollback-for="RuntimeException, Error"/>
2.6、isolation
事务的隔离级别
如果多个事务同时操作一份数据,可能引发以下问题
1、脏读:一个事务读取到了另一个事务没有提交的数据
2、不可重复读:一个事务范围内两个相同的查询却返回了不同数据
3、幻读:一个事务发现了之前本来确认不存在的数据
可以设置隔离级别来解决上述问题(由上到下,性能依次降低)
READ UNCOMMITTED
:什么也解决不了
READ COMMITED
:可以防止脏读
REPEATABLE READ
:可以防止脏读,不可重复读(MySQL
的默认级别)
SERLALIZABLE
:可以防止脏读,不可重复读,幻读(对整张表进行加锁)
- 查询隔离级别的
SQL
语句
SELECT @@TX_ISOLATION
- 设置隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL
隔离级别
网友评论