AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程
动态代理技术,在运行期间,对目标对象的方法进行增强,代理对象同名方法内可以执行原有逻辑的同时嵌入执行其他增强逻辑或其他对象的方法
其实在之前学习BeanPostProcessor时,在BeanPostProcessor的after方法中使用动态代理对Bean进行了增 强,实际存储到单例池singleObjects中的不是当前目标对象本身,而是当前目标对象的代理对象Proxy,这样在调用目标对象方法时,实际调用的是代理对象Proxy的同名方法,起到了目标方法前后都进行增强的功能, 对该方式进行一下优化,将增强的方法提取出去到一个增强类中,且只对com.mem.service.impl
包下的任何类的任何方法进行增强
userService
public interface UserService {
void show1();
void show2();
}
public class UserServiceImpl implements UserService {
@Override
public void show1() {
System.out.println("show1 ...");
}
@Override
public void show2() {
System.out.println("show2 ...");
}
}
myAdvice
// 增强类,内部提供增强方法
public class MyAdvice {
public void beforeAdvice(){
System.out.println("前置增强....");
}
public void afterReturningAdvice(){
System.out.println("后置增强....");
}
}
public class MockAopBeanPostProcessor implements BeanPostProcessor , ApplicationContextAware {
private ApplicationContext applicationContext = null;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 目的:对UserServiceImpl中的show1,show2方法进行增强,增强方法存在与MyAdvice中
// 问题1:筛选service.impl包下所有类和所有方法多可以进行增强,解决方案:if-else
// 问题2:MyAdvice怎么获取? 解决方案:从spring容器中获取
if (bean.getClass().getPackage().getName().equals("com.mem.service.impl")){
//生成当前Bean的代理对象
Object proxyInstance = Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(Object proxy, Method method, Object[] args)->{
MyAdvice myAdvice = (MyAdvice) applicationContext.getBean("myAdvice");
//执行增强对象的前置方法
myAdvice.beforeAdvice();
//执行目标对象的目标方法
Object result = method.invoke(bean, args);
//执行增强对象的后置方法
myAdvice.afterReturningAdvice();
return result;
}
);
return proxyInstance;
}else{
return bean;
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
<bean id="myAdvice" class="com.mem.advice.MyAdvice"/>
<bean id="userService" class="com.mem.service.impl.UserServiceImpl"/>
<bean id="mockAopBeanPostProcessor" class="com.mem.processor.MockAopBeanPostProcessor"/>
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.show1();
}
}
// 输出
前置增强....
show1 ...
后置增强....
前面我们自己编写的AOP基础代码还是存在一些问题的,主要如下:
配置方式的设计、配置文件(注解)的解析工作,Spring已经帮我们封装好了
xml方式配置AOP的步骤:
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.6version>
dependency>
aspectj是实现AOP的一种实现方式
Spring-context坐标下已经包含spring-aop的包了,所以就不用额外导入了
public interface UserService {
void show1();
void show2();
}
public class UserServiceImpl implements UserService {
@Override
public void show1() {
System.out.println("show1 ...");
}
@Override
public void show2() {
System.out.println("show2 ...");
}
}
// 增强类,内部提供增强方法
public class MyAdvice {
public void beforeAdvice(){
System.out.println("前置增强....");
}
public void afterReturningAdvice(){
System.out.println("后置增强....");
}
}
<bean id="myAdvice" class="com.mem.advice.MyAdvice"/>
<bean id="userService" class="com.mem.service.impl.UserServiceImpl"/>
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
<aop:aspect id="" ref="myAdvice">
<aop:before method="beforeAdvice" pointcut-ref="myPointcut"/>
<aop:after-returning method="afterReturningAdvice" pointcut-ref="myPointcut"/>
aop:aspect>
aop:config>
xml配置AOP的方式还是比较简单的,下面看一下AOP详细配置的细节:
有两种
切点表达式是配置要对哪些连接点(哪些类的哪些方法)进行通知的增强,语法如下:
其中,
*
表示任意;.
表示该包下的类,使用双点..
表示该包及其子包下的类;..
表示任意参数。AOP的xml有两种配置方式,如下:
配置切面(详见之前的配置)
配置切面(如下)Spring定义了一个Advice接口,实现了该接口的类都可以作为通知类出现
Advice的子功能接口
前置通知和后置通知接口
通知类实现了前置通知和后置通知接口
public class MyAdvice2 implements MethodBeforeAdvice, AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("后置通知*****");
}
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置通知*****");
}
}
切面使用advisor标签配置
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
<aop:advisor advice-ref="myAdvice2" pointcut-ref="myPointcut"/>
aop:config>
环绕通知
通知类实现了方法拦截器接口
public class MyAdvice3 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕前*****");
// 执行目标方法
Object res = methodInvocation.getMethod().invoke(methodInvocation.getThis(), methodInvocation.getArguments());
System.out.println("环绕后*****");
return res;
}
}
切面使用advisor标签配置
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
<aop:advisor advice-ref="myAdvice3" pointcut-ref="myPointcut"/>
aop:config>
2)通知类的定义要求不同,advisor 需要的通知类需要实现Advice的子功能接口:
aspect 不需要通知类实现任何接口,在配置的时候指定哪些方法属于哪种通知类型即可,更加灵活方便:
3)可配置的切面数量不同:
4)使用场景不同:
由于实际开发中,自定义aop功能的配置大多使用aspect的配置方式,所以我们后面主要讲解aspect的配置, advisor是为了后面Spring声明式事务控制做铺垫,此处大家了解即可。
通过xml方式配置AOP时,我们引入了AOP的命名空间,根据讲解的,要去找spring-aop包下的META-INF,在去找spring.handlers文件
最终加载的是 AopNamespaceHandler,该Handler的init方法中注册了config标签对应的解析器(ConfigBeanDefinitionParser)
public class AopNamespaceHandler extends NamespaceHandlerSupport {
public AopNamespaceHandler() {
}
public void init() {
this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
以ConfigBeanDefinitionParser作为入口进行源码剖析,最终会注册一个AspectJAwareAdvisorAutoProxyCreator 进入到Spring容器中
public abstract class AopConfigUtils {
@Nullable
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
}
AspectJAwareAdvisorAutoProxyCreator 的上上级父类AbstractAutoProxyCreator中的 postProcessAfterInitialization方法
通过断点方式观察,当bean是匹配切点表达式时,this.wrapIfNecessary(bean, beanName, cacheKey)返回的是 一个**JDKDynamicAopProxy **
可以在深入一点,对wrapIfNecessary在剖析一下,看看是不是我们熟知的通过JDK的 Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h) 的方式创建的代理 对象呢?经过如下一系列源码跟踪
动态代理实现:
动态代理的实现的选择,在调用getProxy() 方法时,我们可选用的 AopProxy接口有两个实现类,如上图,这两种都是动态生成代理对象的方式,一种就是基于JDK的,一种是基于Cglib的
JDK的动态代理代码,之前已经写过了,下面看一下Cglib基于超类的动态代理
public class Target {
public void show(){
System.out.println("show.....");
}
}
public class MyAdvice4 {
public void beforeAdvice(){
System.out.println("前置增强....");
}
public void afterReturningAdvice(){
System.out.println("后置增强....");
}
}
/**
* CGLib基于父类的动态代理
*/
public class MyCglibProxyTest {
public static void main(String[] args) {
// 目标对象
Target target = new Target();
// 通知对象
MyAdvice4 myAdvice4 = new MyAdvice4();
// 编写CGLib的代码
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(Target.class); // 生成的代理对象就是Target的子类
// 设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
// intercept方法相当于JDK的Proxy的invoke方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
myAdvice4.beforeAdvice();
Object res = method.invoke(target, objects);
myAdvice4.afterReturningAdvice();
return res;
}
});
// 生成代理对象
Target proxy = (Target) enhancer.create();
proxy.show();
/**
* 前置增强....
* show.....
* 后置增强....
*/
}
}
Spring的AOP也提供了注解方式配置,使用相应的注解替代之前的xml配置,xml配置AOP时,我们主要配置了三部分:目标类被Spring容器管理、通知类被Spring管理、通知与切点的织入(切面),如下:
<bean id="myAdvice" class="com.mem.advice.MyAdvice"/>
<bean id="userService" class="com.mem.service.impl.UserServiceImpl"/>
<aop:config>
<aop:aspect id="" ref="myAdvice">
<aop:before method="beforeAdvice" pointcut="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
<aop:after-returning method="afterReturningAdvice" pointcut="execution(void com.mem.service.impl.UserServiceImpl.show*())"/>
aop:aspect>
aop:config>
通知
和 目标
@Service // 第一步
public interface UserService {
void show1();
}
@Component // 第二步
public class MyAdvice {
public void beforeAdvice(){
System.out.println("前置增强....");
}
public void afterReturningAdvice(){
System.out.println("后置增强....");
}
}
配置aop
配置aop,其实配置aop主要就是配置通知类中的哪个方法(通知类型)对应的切点表达式是什么
@Component
@Aspect // 第三步
public class MyAdvice {
//
@Before("execution(void com.mem.service.impl.UserServiceImpl.show*())") // 第四步
public void beforeAdvice(){
System.out.println("前置增强....");
}
//
@AfterReturning("execution(void com.mem.service.impl.UserServiceImpl.show*())") // 第四步
public void afterReturningAdvice(){
System.out.println("后置增强....");
}
}
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.mem"/>
@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.mem")
@EnableAspectJAutoProxy
public class ApplicationConfig {
}
public class ApplicationContextTest {
public static void main(String[] args) {
// 配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.show1();
// 配置类
// ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfig.class);
// UserService userService = (UserService) applicationContext.getBean("userService");
// userService.show1();
}
}
// 两种方式的打印结果
前置增强....
show1 ...
后置增强....
各种注解方式通知类型
切点表达式的抽取,使用一个空方法,将切点表达式标注在空方法上,其他通知方法引用即可
aspectj-autoproxy
标签之前在使用xml配置AOP时,是借助的Spring的外部命名空间的加载方式完成的,使用注解配置后,就抛弃了 标签,而该标签最终加载了名为AspectJAwareAdvisorAutoProxyCreator的BeanPostProcessor , 最终,在该BeanPostProcessor中完成了代理对象的生成。
同样,从aspectj-autoproxy标签的解析器(AspectJAutoProxyBeanDefinitionParser)入手
public class AopNamespaceHandler extends NamespaceHandlerSupport {
public AopNamespaceHandler() {
}
public void init() {
this.registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
this.registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
this.registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
this.registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
而AspectJAutoProxyBeanDefinitionParser代码内部,最终也是执行了和xml方式AOP一样的代码(注册一个AspectJAwareAdvisorAutoProxyCreator 进入到Spring容器)
registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
@EnableAspectJAutoProxy
注解查看@EnableAspectJAutoProxy源码,使用的也是@Import导入相关解析类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AspectJAutoProxyRegistrar.class})
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
使用@Import导入的AspectJAutoProxyRegistrar源码,一路追踪下去,最终还是注册了 AnnotationAwareAspectJAutoProxyCreator 这个类
事务是开发中必不可少的东西,使用JDBC开发时,我们使用connnection对事务进行控制,使用MyBatis时,我们使用SqlSession对事务进行控制,缺点显而易见,当我们切换数据库访问技术时,事务控制的方式总会变化, Spring 就将这些技术基础上,提供了统一的控制事务的接口。Spring的事务分为:编程式事务控制 和 声明式事务控制
Spring事务编程相关的类主要有如下三个
虽然编程式事务控制我们不学习,但是编程式事务控制对应的这些类我们需要了解一下,因为我们在通过配置的方式进行声明式事务控制时也会看到这些类的影子
搭建一个转账的环境,dao层一个转出钱的方法,一个转入钱的方法,service层一个转账业务方法,内部分别调 用dao层转出钱和转入钱的方法,准备工作如下:
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int NOT NULL AUTO_INCREMENT,
`account_name` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
`money` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
insert into `account`(`id`,`account_name`,`money`) values (1,'tom',5000),(2,'lucy',5000);
public interface AccountMapper {
// 加钱
@Update("update account set money=money+#{money} where account_name=#{accountName}")
void incrMoney(@Param("accountName") String accountName, @Param("money") Integer money);
// 减钱
@Update("update account set money=money-#{money} where account_name=#{accountName}")
void decrMoney(@Param("accountName") String accountName, @Param("money") Integer money);
}
public interface AccountService {
void transferMoney(String outAccount,String inAccount,Integer money);
}
@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public void transferMoney(String outAccount, String inAccount, Integer money) {
accountMapper.decrMoney(outAccount,money);
// int i = 1/0;
accountMapper.incrMoney(inAccount,money);
}
}
<context:component-scan base-package="com.mem"/>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}">property>
<property name="url" value="${jdbc.url}">property>
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.mem.mapper"/>
bean>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.91.135:3306/mybatis?useSSL=false
jdbc.username=root
jdbc.password=123456
public class AccountTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = (AccountService) applicationContext.getBean("accountService");
accountService.transferMoney("tom","lucy",500);
System.out.println("转账操作成功");
}
}
// 打印结果
转账操作成功
打开AccountServiceImpl类中的错误代码,报错:java.lang.ArithmeticException: / by zero
此时数据库,tom用户的余额减了500,而lucy用户的余额却没有增加500
下面通过spring的声明式事务进行控制
结合上面我们学习的AOP的技术,很容易就可以想到,可以使用AOP对Service的方法进行事务的增强。
我们分析要进行的操作:
详情:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.12version>
dependency>
<context:component-scan base-package="com.mem"/>
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.*.*(..))"/>
<aop:advisor advice-ref="" pointcut-ref="myPointcut"/>
aop:config>
疑问:Spring提供的通知类是谁?是spring-tx包下的advice标签配置提供的
配置详情:
<beans xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd"
>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
aop:config>
对上述配置进行详解一下
平台事务管理器:
首先,平台事务管理器PlatformTransactionManager是Spring提供的封装事务具体操作的规范接口,封装了事 务的提交和回滚方法
继承关系图
不同的持久层框架事务操作的方式有可能不同,所以不同的持久层框架有可能会有不同的平台事务管理器实现, 例如,
事务定义信息:
其次,事务定义信息配置,每个事务有很多特性,例如:隔离级别、只读状态、超时时间等,这些信息在开发时 可以通过connection进行指定,而此处要通过配置文件进行配置
此处需要区分的是切点表达式指定的方法与此处指定的方法的区别?
切点表达式,是过滤哪些方法可以进行事务增强;
事务属性信息的name,是指定哪个方法要进行哪些事务属性的配置
事务并发存在三大问题:脏读、不可重复读、幻读/虚读。可以通过设置事务的隔离级别来保证并发问题的出现,常用的是READ_COMMITTED 和 **REPEATABLE_READ **
如果是查询则设置为true,可以提高查询性能,如果是更新(增删改)操作则设置为false
如果超过该时间限制但事务还没有完成,则自动回滚事务 ,不在继续执行。默认值是-1,即没有超时时间限制
主要解决是A方法调用B方法时,事务的传播方式问题的,例如:使用单方的事务,还是A和B都使用自己的事务等。事务的传播行为有如下七种属性值可配置
标签使用的命名空间处理器是TxNamespaceHandler,内部注册的是解析器是 TxAdviceBeanDefinitionParser
public class TxNamespaceHandler extends NamespaceHandlerSupport {
static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
public TxNamespaceHandler() {
}
static String getTransactionManagerName(Element element) {
return element.hasAttribute("transaction-manager") ? element.getAttribute("transaction-manager") : "transactionManager";
}
public void init() {
this.registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
this.registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
}
TxAdviceBeanDefinitionParser中指定了要注册的BeanDefinition
TxAdviceBeanDefinitionParser二级父类AbstractBeanDefinitionParser的parse方法将TransactionInterceptor 以配置的名称注册到了Spring容器中
那么TransactionInterceptor是啥?
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
}
从上面的实现来看,他实现了**MethodInterceptor**
内部有个invoke方法(相当于环绕通知)
TransactionInterceptor中的invoke方法会被执行,跟踪invoke方法,最终会看到事务的开启和提交
根据targetClass的debug信息:可以证明目标对象是AccountServiceImpl
接着调用this.invokeWithinTransaction(..)
方法,在TransactionAspectSupport类中的invokeWithinTransaction方法内完成相应的处理(环绕前,目标方法,环绕后)
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
// 在createTransactionIfNecessary方法内部开启事务
TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
// 执行目标方法
retVal = invocation.proceedWithInvocation();
// commitTransactionAfterReturning方法 提交事务
this.commitTransactionAfterReturning(txInfo);
return retVal;
}
}
注解就是对xml的替代
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" isolation="REPEATABLE_READ" timeout="3" read-only="false" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
aop:config>
@Transactional
代替上面两个配置@Service("accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
@Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.SUPPORTS,timeout = 3)
public void transferMoney(String outAccount, String inAccount, Integer money) {
accountMapper.decrMoney(outAccount,money);
int i = 1/0;
accountMapper.incrMoney(inAccount,money);
}
}
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
改成全注解的方式需要替换成@Bean
和@EnableTransactionManagement
原来的xml(完整版):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.mem"/>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}">property>
<property name="url" value="${jdbc.url}">property>
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.mem.mapper"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" isolation="REPEATABLE_READ" timeout="3" read-only="false" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(void com.mem.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
aop:config>
beans>
替换成全注解方式,如下:
@Configuration
@ComponentScan("com.mem") //
@PropertySource("classpath:jdbc.properties") //
/**
*
*
*
*/
@MapperScan("com.mem.mapper")
@EnableTransactionManagement //
public class AccountConfig {
@Bean
public DataSource dataSource(
@Value("${jdbc.driver}") String driver,
@Value("${jdbc.url}") String url,
@Value("${jdbc.username}") String username,
@Value("${jdbc.password}") String password
){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}