前置项目用来引出AOP的作用
-
实现转账功能,aaa转账给bbb
-
package cn.tedu.service.impl; import cn.tedu.dao.IAccountDao; import cn.tedu.entity.Account; import cn.tedu.service.IAccountService; import cn.tedu.utils.TransactionManager; import java.util.List; /** * 账户业务层实现类 */ public class AccountServiceImpl1 implements IAccountService { private IAccountDao accountDao; private TransactionManager transactionManager; public TransactionManager getTransactionManager() { return transactionManager; } public void setTransactionManager(TransactionManager transactionManager) { this.transactionManager = transactionManager; } public IAccountDao getAccountDao() { return accountDao; } public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } @Override public List<Account> findAllAccount() { List<Account> allAccount = accountDao.findAllAccount(); return allAccount; } @Override public Account findAccountById(Integer id) { Account accountById = accountDao.findAccountById(id); return accountById; } @Override public void saveAccount(Account account) { accountDao.saveAccount(account); } @Override public void updateAccount(Account account) { accountDao.updateAccount(account); } @Override public void deleteAccount(Integer id) { accountDao.deleteAccount(id); } @Override//实现转账功能 public void transfer(String sourceName, String targetName, float money) { Account byAccountName = accountDao.findByAccountName(sourceName); byAccountName.setMoney(byAccountName.getMoney() - money); accountDao.updateAccount(byAccountName); int i = 1 / 0;//如果这里发生异常,就会导致,前减了,另一个人却没有加 Account byAccountName1 = accountDao.findByAccountName(targetName); byAccountName1.setMoney(byAccountName1.getMoney()+ money); accountDao.updateAccount(byAccountName1); } }
为了解决这个问题,使用了事务类,具体实不用去关心,主要是引出aop思想
-
加上事务,写上事务管理类
-
package cn.tedu.utils; import javax.sql.DataSource; import java.sql.Connection; //获取连接的类 public class ConnectionUtils { ThreadLocal<Connection> t1=new ThreadLocal<>(); private DataSource source; public DataSource getSource() { return source; } public void setSource(DataSource source) { this.source = source; } /** * 获取当前线程的连接 */ public Connection getConnection(){ try { Connection conn=t1.get(); if (conn==null){ conn=source.getConnection(); t1.set(conn); } return conn; }catch (Exception e){ throw new RuntimeException(e); } } /** * 把连接和线程解绑 */ public void remove(){ t1.remove(); } }
2. 事务类 1. ```java package cn.tedu.utils; import java.sql.SQLException; /** * 里面包含了,开启事务,关闭事务,回滚事务 */ public class TransactionManager { private ConnectionUtils connectionUtils; public ConnectionUtils getConnectionUtils() { return connectionUtils; } public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } public void beginTransaction(){ try { connectionUtils.getConnection().setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } } /** * 提交事务 */ public void commit(){ try { connectionUtils.getConnection().commit(); } catch (SQLException e) { e.printStackTrace(); } } /** * 回滚事务 */ public void rollback(){ try { connectionUtils.getConnection().rollback(); } catch (SQLException e) { e.printStackTrace(); } } /** * 关闭事务 */ public void release(){ try { connectionUtils.getConnection().close(); connectionUtils.remove(); } catch (SQLException e) { e.printStackTrace(); } } }
-
为了解决上面那个问题,我们使用了事务类,其实就是jdbc的知识,自己封装的而已,改造后
-
package cn.tedu.service.impl; import cn.tedu.dao.IAccountDao; import cn.tedu.entity.Account; import cn.tedu.service.IAccountService; import cn.tedu.utils.TransactionManager; import java.util.List; /** * 账户业务层实现类 */ public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao; private TransactionManager transactionManager; public TransactionManager getTransactionManager() { return transactionManager; } public void setTransactionManager(TransactionManager transactionManager) { this.transactionManager = transactionManager; } public IAccountDao getAccountDao() { return accountDao; } public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } @Override public List<Account> findAllAccount() { try { //开启事务 transactionManager.beginTransaction(); //执行操作 List<Account> allAccount = accountDao.findAllAccount(); //提交事务 transactionManager.commit(); //返回结果 return allAccount; }catch (Exception e){ //回滚事务 transactionManager.rollback(); }finally { //释放连接 transactionManager.release(); } return null; } @Override public Account findAccountById(Integer id) { try { //开启事务 transactionManager.beginTransaction(); //执行操作 Account accountById = accountDao.findAccountById(id); //提交事务 transactionManager.commit(); //返回结果 return accountById; }catch (Exception e){ //回滚事务 transactionManager.rollback(); }finally { //释放连接 transactionManager.release(); } return null; } @Override public void saveAccount(Account account) { try { //开启事务 transactionManager.beginTransaction(); //执行操作 accountDao.saveAccount(account); //提交事务 transactionManager.commit(); //返回结果 }catch (Exception e){ //回滚事务 transactionManager.rollback(); }finally { //释放连接 transactionManager.release(); } } @Override public void updateAccount(Account account) { try { //开启事务 transactionManager.beginTransaction(); //执行操作 accountDao.updateAccount(account); //提交事务 transactionManager.commit(); //返回结果 }catch (Exception e){ //回滚事务 transactionManager.rollback(); }finally { //释放连接 transactionManager.release(); } } @Override public void deleteAccount(Integer id) { try { //开启事务 transactionManager.beginTransaction(); //执行操作 accountDao.deleteAccount(id); //提交事务 transactionManager.commit(); //返回结果 }catch (Exception e){ //回滚事务 transactionManager.rollback(); }finally { //释放连接 transactionManager.release(); } } @Override//实现转账功能 public void transfer(String sourceName, String targetName, float money) { try { //开启事务 transactionManager.beginTransaction(); //执行操作 Account byAccountName = accountDao.findByAccountName(sourceName); byAccountName.setMoney(byAccountName.getMoney()-money); int i=1/0; Account byAccountName1 = accountDao.findByAccountName(targetName); byAccountName1.setMoney(byAccountName1.getMoney()+money); accountDao.updateAccount(byAccountName); accountDao.updateAccount(byAccountName1); //提交事务 transactionManager.commit(); //返回结果 }catch (Exception e){ //回滚事务 transactionManager.rollback(); }finally { //释放连接 transactionManager.release(); } } } //这样我们便不会发送上面那种情况,但是代码过于冗余
先看代理的知识
-
代理
-
动态代理:
-
特点:字节码随用随创建,随用随加载
-
作用:不修改源码的基础上对方法增强
-
分类:
- 基于接口的动态代理
- 基于子类的动态代理
-
基于接口的动态代理:
- 涉及的类:Proxy,提供者JDK官方
-
如何创建代理对象:
- 使用proxy类中的newProxyInstance方法
-
创建代理对象的要求:
- 被代理类最少实现一个接口,如果没有则不能使用
-
newProxyInstance方法的参数:
-
classloder:类加载器:–它是用于加载代理对象字节码的,和被代理对象使用相同的类加载器,固定写法
-
class[]:字节码数组: --它是用于让代理对象和被代理对象有相同的方法,固定写法
-
InvocationHandler:用于提供增强代码:—它是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的,此接口的实现类都是谁用谁写
-
例子:
-
public class Client { public static void main(String[] args) { Producer producer=new Producer(); IProducer iProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() { /** * 作用:执行被代理对象的任何借口方法都会经过该方法 * @param proxy 代理对象的引用 * @param method 当前执行的方法 * @param args 当前执行方法所需要的参数 * @return 方法执行的返回值 * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //提供增强的代码 float money= (float) args[0]; Object returnValue=null; if ("afterService".equals(method.getName())){ money*=0.8f; returnValue =method.invoke(producer,money); } return returnValue; } }); iProducer.afterService(1000); } }
-
-
-
-
cglib代理,这是没有实现类的情况下
-
特点:字节码随用随创建,随用随加载
-
作用:不修改源码的基础上对方法增强
-
分类:
- 基于接口的动态代理
- 基于子类的动态代理
-
基于子类的动态代理:
- 涉及的类:Enhancer,提供者JDK官方
-
如何创建代理对象:
- 使用Enhancer类中的create方法
-
创建代理对象的要求:
- 被代理类不能是最终类
-
create方法的参数:
- class:字节码:–它是用于指定被代理对象的字节码
- callback:用于提供增强代码:–它是让我们如何代理,我们一般都是写该接口的子接口实现类:MethodInterceptor
-
例子:
-
package cglib; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { Producer producer=new Producer(); Producer producer1=(Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() { /** * 执行被代理对象的任何方法都会经过该方法 * @param proxy * @param method * @param objects * 以上三个参数和基于接口的动态代理中invoke方法一样的 * @param methodProxy 当前执行方法的代理对象 * @return * @throws Throwable */ @Override public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //提供增强的代码 float money= (float) objects[0]; Object returnValue=null; if ("afterService".equals(method.getName())){ money*=0.8f; returnValue =method.invoke(producer,money); } return returnValue; } }); producer1.afterService(500); } }
-
-
-
-
修改后:我们只需要一个就可以了
@Test
public void proxy() {
IAccountService iAccountService = new AccountServiceImpl1();
IAccountService iAccountService1 = (IAccountService) Proxy.newProxyInstance(iAccountService.getClass().getClassLoader(), iAccountService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnValue = null;
try {
transactionManager.beginTransaction();
returnValue = method.invoke(iAccountService, args);
transactionManager.commit();
} catch (Exception e) {
transactionManager.rollback();
System.out.println("e");
throw new RuntimeException(e);
} finally {
transactionManager.release();
}
return returnValue;
}
});
iAccountService1.transfer("aaa","bbb",30);
}
AOP
-
什么是AOP
-
AOP实现原理
- aop底层将采用代理机制进行实现.
- 接口+实现类:spring采用jdk的动态代理Proxy
- 实现类 :spring采用cglib字节码增强
-
作用
- 在程序运行期间,不修改源码对已有方法进行增强
- 优势:
- 减少重复代码
- 提高开发效率
- 维护方便
AOP术语
- target:目标类,需要被代理的类
- Joinpoint(连接点)所谓连接点是指那些可能被拦截的方法 例如:所有方法 (卫生间的坑),(业务层的所有方法都可以看成是)
- PointCut 切入点:已经被增强的连接点. 列如:addUser() (已经被使用的坑),(或者说哪些被拦截的方法)
- advice (通知) /增强 增强的代码
- 通知的类型,前置通知,后置通知,异常通知,最终通知,环绕通知
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-64JufLWF-1602062746657)(.\SpringAOP.assets\image-20200917214710950.png)]
- Weaving (织入)是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程
- proxy 代理类
- Aspect(切面) :是切入点和通知的结合
学习spring中的aop要明确的事
- 开发阶段(我们做的):
- 编写核心业务代码,把公用代码抽取出来,制作成通知,在配置文件中声明切入点和通知的关系,即切面
- 运行阶段(spring框架来完成)
- spring框架监控切入点的方法执行,一旦监控到切入点方法被运行,使用代理,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行
- spring会根据target有没有接口来判断使用哪个代理
测试AOP
-
创建测试业务类
-
package cn.tedu; public interface AccountService { /** * 模拟保存用户 */ void saveAccount(); /** * 模拟更新 * @param i */ void updateAccount(int i); /** * 模拟删除 * @return */ int deleteAccount(); } package cn.utils; /** * 用于记录日志的工具类 */ public class Logger { /** * 用于打印日志,计划让其在切入点方法执行之前执行(切入点就是业务层方法) */ public void pringLog(){ System.out.println("Loggrt 类的方法开始进行记录日志了"); } }
-
创建通知类,就是要增强的代码
-
package cn.utils; /** * 用于记录日志的工具类 */ public class Logger { /** * 用于打印日志,计划让其在切入点方法执行之前执行(切入点就是业务层方法) */ public void pringLog(){ System.out.println("Loggrt 类的方法开始进行记录日志了"); } }
-
-
项目要导入的jar包
-
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId><!--解析切入点表达式的--> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency>
</dependencies>
-
-
配置文件
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!--配置spring的ioc把service对象配置进来--> <bean id="accountService" class="cn.tedu.AccountServiceImpl"></bean> <!--spring基于xml的aop配置步骤 1,把通知bean也交给spring来管理 1. 使用aop:config标签表名开始aop的配置 2. 使用aop:aspect标签表明配置切面 1. id属性:是给前面提供一个唯一标识 2. ref属性:是指通知类bean的id 3. 在aop:aspect标签的内部使用对象标签来配置通知的类型,我们现在实例时让pringLog方法在切入点方法执行之前:所以是前置通知 1. aop:before:标识前置通知 1. method属性:用于指定logger类中哪个方法是前置通知 2. pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强 2. 切入带你表达式的写法: 1. 关键字: execution(表达式) 1. 表达式: 访问修饰符 返回值 包名.包名.包名.....类名.方法名(参数列表) 2. 标准表达式写法: public void com.cn.tedu.AccountServiceImpl.saveAccount() --> <!--配置logger类--> <bean id="logger" class="cn.utils.Logger"/> <!--配置aop--> <aop:config> <!--配置切面--> <aop:aspect id="" ref="logger"> <!--配置通知的类型,并且建立通知方法和切入点方法的关联--> <aop:before method="pringLog" pointcut="execution(public void cn.tedu.AccountServiceImpl.saveAccount())"/> </aop:aspect> </aop:config> </beans>
-
-
-
配置步骤和上面xml文件一样的,这里方便好看
-
把通知bean也交给spring来管理
-
使用aop:config标签表名开始aop的配置
-
使用aop:aspect标签表明配置切面
- id属性:是给前面提供一个唯一标识
- ref属性:是指通知类bean的id
-
在aop:aspect标签的内部使用对象标签来配置通知的类型,我们现在实例时让pringLog方法在切入点方法执行之前:所以是前置通知
-
aop:before:标识前置通知
- method属性:用于指定logger类中哪个方法是前置通知
- pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强
-
切入带你表达式的写法:
-
关键字: execution(表达式)
- 表达式: — 访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)
-
标准表达式写法: public void com.cn.tedu.AccountServiceImpl.saveAccount()
-
全通配写法:
-
* *..*.*(..)
-
-
访问修饰符可以省略
-
void com.cn.tedu.AccountServiceImpl.saveAccount()
-
-
返回值可以使用通配符 表示任意返回值
-
* com.cn.tedu.AccountServiceImpl.saveAccount()
-
-
包名可以使用通配符,标识任意包,但是有几级包,就需要写几个*.
-
* *.*.*.AccountServiceImpl.saveAccount()
-
-
包名可以使用…表示当前包及其子包
-
* *..AccountServiceImpl.saveAccount()
-
-
类名和方法名都可以使用*
-
* *..*.*()
-
-
参数列表: 可以直接写数据类型
- 基本类型 直接写名称 int
- 引用类型写包名.类名的方式 java.lang.String
- 可以使用通配符表示任意参数,但必须是有参数
- 可以使用…表示有误参数均可,有参数可以是任意数据类型
-
实际开发中切入点表达式的通常写法:切到业务层实现类下的所有方法
-
cn.tedu.service.imp.*.*(..)
-
-
-
-
aop五种通知
-
通知类型:
- 前置通知:在切入点方法执行之前执行
- 后置通知:在切入点方法正常执行之后执行(它和异常通知只能执行一个)
- 异常通知:在切入点方法执行产生异常之后执行(它和后置通知只能执行一个)
- 最终通知:无论切入点方法是否正常执行它都会在其后面执行
- 环绕通知
-
测试
-
aop配置类:和上面不变,就是通知类型变多
-
package cn.utils; /** * 用于记录日志的工具类 */ public class Logger { /** * 用于打印日志,计划让其在切入点方法执行之前执行(切入点就是业务层方法) */ //前置通知 public void pringLog(){ System.out.println("Loggrt 前置通知"); } //后置通知 public void afterReturning(){ System.out.println("后置通知"); } //异常通知 public void afterThrowingPointLog(){ System.out.println("异常通知"); } //最终通知 public void afterPringLog(){ System.out.println("最终通知"); } }
-
-
配置文件
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!--配置spring的ioc把service对象配置进来--> <bean id="accountService" class="cn.tedu.AccountServiceImpl"></bean> <bean id="logger" class="cn.utils.Logger"/> <!--配置aop--> <aop:config> <!--配置切面--> <aop:aspect id="" ref="logger"> <!--配置通知的类型,并且建立通知方法和切入点方法的关联--> <aop:before method="pringLog" pointcut="execution(* *..AccountServiceImpl.*())"/> <!--后置通知--> <aop:after-returning method="afterReturning" pointcut="execution(* *..AccountServiceImpl.*())"/> <!--异常通知`通知--> <aop:after-throwing method="afterThrowingPointLog" pointcut="execution(* *..AccountServiceImpl.*())"/> <!--最终通知通知--> <aop:after method="afterPringLog" pointcut="execution(* *..AccountServiceImpl.*())"/> </aop:aspect> </aop:config> </beans>
-
上面那种过于繁琐
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!--配置spring的ioc把service对象配置进来--> <bean id="accountService" class="cn.tedu.AccountServiceImpl"></bean> <bean id="logger" class="cn.utils.Logger"/> <!--配置aop--> <aop:config> <!--配置切面--> <aop:aspect id="" ref="logger"> <!--配置通知的类型,并且建立通知方法和切入点方法的关联--> <aop:before method="pringLog" pointcut-ref="pul"/> <!--后置通知--> <aop:after-returning method="afterReturning" pointcut-ref="pul"/> <!--异常通知`通知--> <aop:after-throwing method="afterThrowingPointLog" pointcut-ref="pul"/> <!--最终通知通知--> <aop:after method="afterPringLog" pointcut-ref="pul"/> <!--配置切入点表达式 id用于指定表达式的唯一标识,experssion属性用于指定表达式的内容--> <aop:pointcut id="pul" expression="execution(* *..AccountServiceImpl.*())"/> </aop:aspect> </aop:config> </beans>
-
-
-
环绕通知
-
配置xml
-
<aop:around method="aroud" pointcut-ref="pul"></aop:around>
-
-
配置环绕通知类
-
public Object aroud(ProceedingJoinPoint pro){ Object rtvalue=null; try { Object[] args=pro.getArgs();//得到方法执行所需参数 System.out.println("Logger类中的 方法开始记录日志了: 前置"); rtvalue= pro.proceed(args);//明确调用业务方法(切入点方法) System.out.println("Logger类中的 方法开始记录日志了: 后置"); } catch (Throwable throwable) { System.out.println("Logger类中的 方法开始记录日志了: 异常"); throw new RuntimeException(throwable); }finally { System.out.println("Logger类中的 方法开始记录日志了: 最终"); } return rtvalue; }
-
-
-
注解aop
-
配置文件
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!--配置spring创建容器时要扫描的包--> <context:component-scan base-package="cn"/> <!--开启aop注解支持--> <aop:aspectj-autoproxy/> </beans>
-
aop通知类
-
package cn.utils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /** * 用于记录日志的工具类 */ @Component @Aspect public class Logger { /** * 用于打印日志,计划让其在切入点方法执行之前执行(切入点就是业务层方法) */ @Pointcut("execution(* *..AccountServiceImpl.*())") public void pt1(){} //前置通知 @Before("pt1()") public void pringLog(){ System.out.println("Loggrt 前置通知"); } //后置通知 @AfterReturning("pt1()") public void afterReturning(){ System.out.println("后置通知"); } //异常通知 @AfterThrowing("pt1()") public void afterThrowingPointLog(){ System.out.println("异常通知"); } //最终通知 @After("pt1()") public void afterPringLog(){ System.out.println("最终通知"); } /** * 环绕通知 * spring框架为我们提供了一个接口:proceedingJoinPoing,该接口有一个方法proceed(),此方法就相当于明确的调用了切入点方法 * 该接口可以作为环绕通知的方法参数,在程序执行是,spring框架会为我们提供该接口的实现类供我们使用 * */ @Around("pt1()") public Object aroud(ProceedingJoinPoint pro){ Object rtvalue=null; try { Object[] args=pro.getArgs();//得到方法执行所需参数 System.out.println("Logger类中的 方法开始记录日志了: 前置"); rtvalue= pro.proceed(args);//明确调用业务方法(切入点方法) System.out.println("Logger类中的 方法开始记录日志了: 后置"); } catch (Throwable throwable) { System.out.println("Logger类中的 方法开始记录日志了: 异常"); throw new RuntimeException(throwable); }finally { System.out.println("Logger类中的 方法开始记录日志了: 最终"); } return rtvalue; } }
-
存注解要加上这个
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3bzVeDJ6-1602062746660)(C:\Users\taget\Desktop\spring\SpringAOP.assets\image-20200921225659768.png)]
-
-
-
-
-
JDBCTemplate
JDBC已经能够满足大部分用户最基本的需求,但是在使用JDBC时,必须自己来管理数据库资源如:获取PreparedStatement,设置SQL语句参数,关闭连接等步骤。
JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分。JdbcTemplate处理了资源的建立和释放。他帮助我们避免一些常见的错误,比如忘了总要关闭连接。他运行核心的JDBC工作流,如Statement的建立和执行,而我们只需要提供SQL语句和提取结果
-
作用:
- 它就是用于和数据库交互的,实现对表的crud操作
-
常用的操作:
-
导入jar包
-
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> </dependencies>
-
-
编写配置类
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <!--配置spring创建容器时要扫描的包--> <context:component-scan base-package="cn"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!--连接数据库必备信息--> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/eesy"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--连接数据库必备信息--> <property name="dataSource" ref="dataSource"/> </bean> <bean id="accountDao" class="cn.tedu.dao.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> </beans>
-
-
测试常用的增删改查
-
package cn.tedu.entity.jdbcTemplate; import cn.tedu.entity.Account; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; /** * jdbcTemplate的crud操作 */ public class JdbcTemplateDemo2 { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("bean.xml"); JdbcTemplate jdbcTemplate = context.getBean("jdbcTemplate", JdbcTemplate.class); //保存 jdbcTemplate.update("insert into account(name,money)values(?,?)","eee",333f); //更新 jdbcTemplate.update("update account set name=?,money=? where id=?","eee",333f); //删除 jdbcTemplate.update("delete from account where id=?",1); //查询所有 // List<Account> query = jdbcTemplate.query("seleclt * from account where money>?", new AccountRowMapper(), 1000f); List<Account> query = jdbcTemplate.query("seleclt * from account where money>?",new BeanPropertyRowMapper<Account>(Account.class), 1000f); //查询一个 List<Account> query1 = jdbcTemplate.query("seleclt * from account where id=?",new BeanPropertyRowMapper<Account>(Account.class), 1000f); System.out.println(query1.isEmpty()?"":query1.get(0)); //查询返回一行一列(使用聚合函数) Long count=jdbcTemplate.queryForObject("select count(*) from account where money>?" ,Long.class,100f); System.out.println(count); } } class AccountRowMapper implements RowMapper<Account>{ @Override public Account mapRow(ResultSet resultSet, int i) throws SQLException { Account account=new Account(); account.setId(resultSet.getInt("id")); account.setName(resultSet.getString("name")); account.setMoney(resultSet.getFloat("money")); return account; } }
-
-
事务
Spring 框架中,最重要的事务管理的 API 有三个:TransactionDefinition、PlatformTransactionManager 和 TransactionStatus自己去了解。 所谓事务管理,实质上就是按照给定的事务规则来执行提交或者回滚操作。
前置准备
package cn.tedu.dao;
import cn.tedu.entity.Account;
public interface IAccountDao {
/**
* 根据id查询用户
* @param id
* @return
*/
Account findByAccountId(Integer id);
/**
* g根据名称查询用户
* @param name
* @return
*/
Account findByAccountName(String name);
/**
* 更新账户
* @param account
*/
void updateAccount(Account account);
}
package cn.tedu.dao.impl;
import cn.tedu.dao.IAccountDao;
import cn.tedu.entity.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class AccountDaoImpl implements IAccountDao {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public Account findByAccountId(Integer id) {
List<Account> query = jdbcTemplate.query("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class), id);
return query.isEmpty()?null:query.get(0);
}
@Override
public Account findByAccountName(String name) {
List<Account> query = jdbcTemplate.query("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name);
return query.isEmpty()?null:query.get(0);
}
@Override
public void updateAccount(Account account) {
jdbcTemplate.update("update account set money=? where id=?",account.getMoney(),account.getId());
}
}
package cn.tedu.service;
import cn.tedu.entity.Account;
public interface IAccountService {
/**
* 根据id查询信息
* @param accountId
* @return
*/
Account findAccountById(Integer accountId);
/**
* 转账
* @param sourceName
* @param targetName
* @param money
*/
void transfer(String sourceName,String targetName,Float money);
}
package cn.tedu.service.impl;
import cn.tedu.dao.IAccountDao;
import cn.tedu.entity.Account;
import cn.tedu.service.IAccountService;
import org.springframework.stereotype.Service;
/**
* 账户业务层实现类
*/
public class AccountServiceImpl1 implements IAccountService {
private IAccountDao accountDao;
public IAccountDao getAccountDao() {
return accountDao;
}
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public Account findAccountById(Integer accountId) {
return null;
}
@Override
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("ssssss");
Account byAccountName = accountDao.findByAccountName(sourceName);
byAccountName.setMoney(byAccountName.getMoney() - money);
// int i = 1 / 0;
Account byAccountName1 = accountDao.findByAccountName(targetName);
byAccountName1.setMoney(byAccountName1.getMoney() + money);
accountDao.updateAccount(byAccountName);
accountDao.updateAccount(byAccountName1);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>day04-eesy-tx</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId><!--事务-->
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId><!--解析切入点表达式的-->
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
</project>
-
基于xml的配置事务
-
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:contexnt="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置spring创建容器时要扫描的包--> <context:component-scan base-package="cn"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!--连接数据库必备信息--> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/eesy"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--连接数据库必备信息--> <property name="dataSource" ref="dataSource"/> </bean> <bean id="accountDao" class="cn.tedu.dao.impl.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> <bean id="accountServiceImpl12" class="cn.tedu.service.impl.AccountServiceImpl1"> <property name="accountDao" ref="accountDao"/> </bean> <!--spring中基于xml的声明式事务控制配置步骤 1,配置事务管理器......这个和上面我们自己写的那个原理差求不多的其实(自我理解) 2,配置事务的通知 此时我们需要导入事务的约束,tx名称空间的约束,同时也需要aop的 使用tx:advice标签配置事务通知 属性: id:给事务通知起一个唯一标识 transaction-manager: 给事务通知提供一个事务管理器引用 3,配置aop中的通用切入点表达式 4,建立事务通知和切入点表达式的对应关系 5,配置事务的属性 --> <!--配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--配置事务的通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--spring配置事务的属性tx:method name="transfer" isolation="" 用于指定事务的隔离级别,默认值是default,表示使用数据库的默认级别 propagation="" 用于指定事务的传播行为,默认值是required,表示一定会有事务,增删改的选择,查询方法可以选择supports read-only="" 用于指定事务是否只读,只有查询方法才能设置true,默认值值是false,表示读写 timeout="" 用于指定事务的超时时间,默认值是-1,表示永不超时,如果指定了数值,以秒为单位 no-rollback-for=""用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚,没有默认值 rollback-for="" 用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚,没有默认值,表示任何异常都回滚--> <tx:method name="*" propagation="REQUIRED" read-only="false"/><!--这里表示所有方法--> <tx:method name="find*" propagation="SUPPORTS" read-only="true"/><!--这里表示find开头的查询所有方法,这个优先级高--> </tx:attributes> </tx:advice> <!--通过aop配置来配置事务--> <aop:config> <aop:pointcut id="tx" expression="execution(* cn.tedu.service.impl.AccountServiceImpl1*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="tx"/> </aop:config> </beans>
-
-
注解配置事务
-
配置xml
-
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:contexnt="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置spring创建容器时要扫描的包--> <context:component-scan base-package="cn"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!--连接数据库必备信息--> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/eesy"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--连接数据库必备信息--> <property name="dataSource" ref="dataSource"/> </bean> <!--spring中基于xml的声明式事务控制配置步骤 1,配置事务管理器......这个和上面我们自己写的那个原理差求不多的其实(自我理解) 2,开启sprig对注解事务的支持 3,需要事务支持的地方使用#Transaction事务注解 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--开启spring对注解事务的支持 如果用配置类就是这个注解 @EnableTransactionManagement--> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
-
-
service层
-
package cn.tedu.service.impl; import cn.tedu.dao.IAccountDao; import cn.tedu.entity.Account; import cn.tedu.service.IAccountService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** * 账户业务层实现类 */ @Service @Transactional //开启注解 public class AccountServiceImpl1 implements IAccountService { private IAccountDao accountDao; public IAccountDao getAccountDao() { return accountDao; } public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } @Override @Transactional(propagation = Propagation.SUPPORTS,readOnly = false) //配置读取方法的事务 public Account findAccountById(Integer accountId) { return null; } @Override public void transfer(String sourceName, String targetName, Float money) { System.out.println("ssssss"); Account byAccountName = accountDao.findByAccountName(sourceName); byAccountName.setMoney(byAccountName.getMoney() - money); // int i = 1 / 0; Account byAccountName1 = accountDao.findByAccountName(targetName); byAccountName1.setMoney(byAccountName1.getMoney() + money); accountDao.updateAccount(byAccountName); accountDao.updateAccount(byAccountName1); } }
还有一个编程事务,自己去了解
-
-