Spring框架:第七章:AOP切面编程
Spring的切入点表达式
@PointCut切入点表达式语法格式是:
execution(访问权限 返回值类型 方法全限定名(参数类型列表))
execution(public int com.atguigu.pojo.Calculator.add(Integer, Integer))
限定符:
_表示任意的意思:
1)匹配某全类名下,任意或多个方法。
execution(public int com.atguigu.pojo.Calculator._(Integer, Integer))
表示匹配任意的方法
2)在Spring中只有public权限能拦截到,访问权限可以省略(访问权限不能写*)。
execution(int com.atguigu.pojo.Calculator.*(Integer, Integer))
可以省略public访问权限
3)匹配任意类型的返回值,可以使用 * 表示
execution(public * com.atguigu.pojo.Calculator.*(Integer, Integer))
表示可以接受任意返回值类型
4)匹配任意子包。
execution(public int com.atguigu.*.Calculator.add(Integer, Integer))
匹配任意子包
5)任意类型参数
execution(public int com.atguigu.pojo.Calculator.add(Integer, *))
表示第二个参数是任意类型的参数
…:可以匹配多层路径,或任意多个任意类型参数
1)任意层级的包
execution(public int com…pojo.Calculator.add(Integer, Integer))
表示com和pojo之间可以有任意层级的包。
2)任意类型的参数
execution(public int com.atguigu.pojo.Calculator.add(…))
不关心参数的个数,和参数的类型。
模糊匹配:
// 表示任意返回值,任意方法全限定符,任意参数
execution(* _(…))
// 表示任意返回值,任意包名+任意方法名,任意参数
execution(_ .(…))
精确匹配:
execution(public int com.atguigu.aop.Calculator.add(int, int))
切入点表达式连接:&& 、||
// 表示需要同时满足两个表达式
@Before(“execution(public int com.atguigu.aop.Calculator.add(int, int))”
+ " && "
- “execution(public * com.atguigu.aop.Calculator.add(…))”)
// 表示两个条件只需要满足一个,就会被匹配到
@Before(“execution(public int com.atguigu.aop.Calculator.add(int, int))”
+ " || "
+ “execution(public * com.atguigu.aop.Calculator.a*(int))”)
9.9、Spring通知的执行顺序
Spring通知的执行顺序是:
正常情况:
前置通知====>>>>目标方法====>>>>后置通知=====>>>>返回值之后
异常情况:
前置通知====>>>>目标方法====>>>>后置通知=====>>>>抛异常通知
@Aspect
@Component
public class LogUtils {
/**
* @Before是前置通知
_/
@Before(value = “execution(public int com.atguigu.pojo.Calculator._(Integer, Integer))”)
// 前置通知===前置增强
public static void logBefore() {
System.out.println(“前置通知 : 方法名:xxxx. 参数是:args”);
}
/**
* @After 后置通知
_/
@After(value = “execution(public int com.atguigu.pojo.Calculator._(Integer, Integer))”)
public static void logAfter() {
System.out.println(“后置通知: 方法名:. 参数是:”);
}
/**
* 返回值通知
_/
@AfterReturning(value = “execution(public int com.atguigu.pojo.Calculator._(Integer, Integer))”)
public static void logAfterReturning() {
System.out.println(“返回通知: 方法名:. 返回值是:”);
}
/**
* 异常通知
_/
@AfterThrowing(value = “execution(public int com.atguigu.pojo.Calculator._(Integer, Integer))”)
public static void logAfterThrowing() {
System.out.println(“异常通知: 方法名:. 异常信息是:”);
}
}
获取连接点信息
JoinPoint 是连接点的信息。
只需要在通知方法的参数中,加入一个JoinPoint参数。就可以获取到拦截方法的信息。
注意:是org.aspectj.lang.JoinPoint这个类。
/**
- @Before是前置通知
*/
@Before(value = “execution(public int com.atguigu.pojo.Calculator.*(Integer, Integer))”)
// 前置通知===前置增强
public static void logBefore(JoinPoint jp) {
// jp.getSignature().getName() 获取方法名
// jp.getArgs() 获取目标方法传递的参数
System.out.println(“前置通知 : 方法名:” + jp.getSignature().getName() + “. 参数是:” + Arrays.asList(jp.getArgs()));
}
获取拦截方法的返回值和抛的异常信息
获取方法返回的值分为两个步骤:
1、在返回值通知的方法中,追加一个参数 Object result
2、然后在@AfterReturning注解中添加参数returning=“参数名”
/**
-
返回值通知
-
returning属性设置用哪个参数来接收返回值
*/
@AfterReturning(value = “execution(public int com.atguigu.pojo.Calculator.*(Integer, Integer))”,returning=“result”)
public static void logAfterReturning(JoinPoint jp, Object result) {
System.out.println(“返回通知: 方法名:” + jp.getSignature().getName() + “. 返回值是:” + result);
}
获取方法抛出的异常分为两个步骤:
1、在异常通知的方法中,追加一个参数Exception exception
2、然后在@AfterThrowing 注解中添加参数 throwing=“参数名”
/**
-
异常通知
-
throwing=“e” 表示使用参数Throwable e来接收抛出的异常
*/
@AfterThrowing(value = “execution(public int com.pojo.Calculator.*(Integer, Integer))”,throwing=“e”)
public static void logAfterThrowing(JoinPoint jp,Throwable e) {
System.out.println(“异常通知: 方法名:” + jp.getSignature().getName() + “. 异常信息是:” + e);
}
Spring的环绕通知
1、环绕通知使用@Around注解。
2、环绕通知如果和其他通知同时执行。环绕通知会优先于其他通知之前执行。
3、环绕通知一定要有返回值(环绕如果没有返回值。后面的其他通知就无法接收到目标方法执行的结果)。
4、在环绕通知中。如果拦截异常。一定要往外抛。否则其他的异常通知是无法捕获到异常的。
/**
-
@throws Throwable
-
@Around 注解表示环绕通知
-
1 环绕通知 执行顺序优先于 普通通知(默认情况)
-
2 环绕通知方法一定要有返回值。而且这个返回值一定是目标方法的返回值。
-
3 环绕通知方法中收到异常后,一定要往外抛
*/
@Around(value = “execution(public int com.pojo.Calculator.*(Integer, Integer))”)
public static Object around(ProceedingJoinPoint pjp) throws Throwable {
Object result = null;
try {
try {
System.out.println(“环绕的前置通知”);
// 调用目标方法
result = pjp.proceed();
} finally {
System.out.println(“环绕的后置通知”);
}
System.out.println(“环绕的返回通知:” + result);
} catch (Throwable e) {
System.out.println(“环绕的异常通知:” + e);
throw e;// 普通的异常通知就会收到异常
}
return result;// 普通的返回通知,就会收到返回值
}
切入点表达式的复用
* 切入点表达式的复用
* 第一步:定义一个方法
* 第二步: 在方法上,使用@Pointcut定义一个切入点表达式
* 第三步:在需要复用切入点表达式的地方换成方法调用
/**
-
切入点表达式的复用
-
第一步:定义一个方法
-
第二步: 在方法上,使用@Pointcut定义一个切入点表达式
-
第三步:在需要复用切入点表达式的地方换成方法调用
*/
@Pointcut(value=“execution(public int com.pojo.Calculator.*(Integer, Integer))”)
public static void pointcut1() {}
/**
- @Before是前置通知
*/
@Before(value = “pointcut1()”)
// 前置通知===前置增强
public static void logBefore(JoinPoint jp) {
System.out.println(“前置通知 : 方法名:” + jp.getSignature().getName() + “. 参数是:” + Arrays.asList(jp.getArgs()));
}
多个通知的执行顺序
当我们有多个切面,多个通知的时候:
1、通知的执行顺序默认是由切面类的字母先后顺序决定。
2、在切面类上使用@Order注解决定通知执行的顺序(值越小,越先执行)
如何基于xml配置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:context=“http://www.springframework.org/schema/context”
xmlns:aop=“http://www.springframework.org/schema/aop”
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">
<context:component-scan base-package=“com”></context:component-scan>
aop:config
<aop:aspect ref=“logUtils”>
<aop:pointcut expression=“execution(public int com.pojo.Calculator.*(Integer, Integer))”
id=“pointcut1”/>
<aop:before method=“logBefore”
pointcut=“execution(public int com.pojo.Calculator.*(Integer, Integer))”/>
<aop:after method=“logAfter” pointcut-ref=“pointcut1”/>
<aop:after-returning method=“logAfterReturning”
pointcut-ref=“pointcut1” returning=“result”
/>
<aop:after-throwing method=“logAfterThrowing”
pointcut-ref=“pointcut1” throwing=“e”
/>
</aop:aspect>
</aop:config>
Spring之数据访问
Spring数据访问工程环境搭建
创建一个java工程
导入jar包:
commons-logging-1.1.3.jar
druid-1.1.9.jar
mysql-connector-java-5.1.37-bin.jar
spring-aop-4.3.18.RELEASE.jar
spring-beans-4.3.18.RELEASE.jar
spring-context-4.3.18.RELEASE.jar
spring-core-4.3.18.RELEASE.jar
spring-expression-4.3.18.RELEASE.jar
spring-jdbc-4.3.18.RELEASE.jar
spring-orm-4.3.18.RELEASE.jar
spring-test-4.3.18.RELEASE.jar
spring-tx-4.3.18.RELEASE.jar
jdbc.properties属性配置文件:
url=jdbc:mysql://localhost:3306/book
user=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
applicationContext.xml配置文件:
<?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: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">
<context:property-placeholder location=“classpath:jdbc.properties”/>
Spring之JdbcTemplate使用
在Spring中提供了对jdbc的封装类叫JdbcTemplate。它可以很方便的帮我们执行sql语句,操作数据库。
先准备单表的数据库数据
drop database if exists jdbctemplate;
create database jdbctemplate;
use jdbctemplate;
CREATE TABLE employee
(
id
int(11) primary key AUTO_INCREMENT,
name
varchar(100) DEFAULT NULL,
salary
decimal(11,2) DEFAULT NULL
);
insert into employee
(id
,name
,salary
)
values (1,‘李三’,5000.23),(2,‘李四’,4234.77),(3,‘王五’,9034.51),
(4,‘赵六’,8054.33),(5,‘孔七’,6039.11),(6,‘曹八’,7714.11);
select * from employee;
实验2:将id=5的记录的salary字段更新为1300.00
@Test
public void test2() throws Exception {
String sql = “update employee set salary = ? where id = ?”;
jdbcTemplate.update(sql, new BigDecimal(1300),5);
}
实验3:批量插入
@Test
public void test3() throws Exception {
String sql = “insert into employee(name
,salary
) values(?,?)”; // 插入一条记录对应一个 一维数组
// jdbcTemplate.update(sql, “新来的” , new BigDecimal(10000000));
List<Object[]> args = new ArrayList<Object[]>();
args.add(new Object[] {“aaaa”,new BigDecimal(11111)});
args.add(new Object[] {“bbbb”,new BigDecimal(22222)});
jdbcTemplate.batchUpdate(sql, args);
}
实验4:查询id=5的数据库记录,封装为一个Java对象返回
public void test4() throws Exception {
String sql = “select id,name,salary from employee where id = ?”;
/**
-
RowMapper 接口,负责将查询到的每一行记录转换成为一个javaBean对象
-
BeanPropertyRowMapper需要把查询的结果转换成为Employee
-
queryForObject 用来查询一个对象
*/
Employee employee = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Employee.class), 5);
System.out.println( employee );
}
实验5:查询salary>4000的数据库记录,封装为List集合返回(List里都是Employee对象)
public void test5() throws Exception {
String sql = “select id,name,salary from employee where salary > ?”;
/**
-
RowMapper 接口,负责将查询到的每一行记录转换成为一个javaBean对象
-
BeanPropertyRowMapper需要把查询的结果每一行转换成为Employee
*/
List list = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Employee.class), new BigDecimal(4000));
System.out.println( list );
}
实验6:查询最大salary
@Test
public void test6() throws Exception {
String sql = “select max(salary) from employee”;
// queryForObject 查询只返回一行记录的语句
// query查询返回多行记录的sql
BigDecimal maxSalary = jdbcTemplate.queryForObject(sql, BigDecimal.class);
System.out.println( maxSalary );
}
实验7:使用带有具名参数的SQL语句插入一条员工记录,并以Map形式传入参数值
测试的代码:
public void test7() throws Exception {
/**
- :name 是占位符。名叫name的参数
*/
String sql = “insert into employee(name
,salary
) values( :name , :salary )”;
Map<String, Object> paramMap = new HashMap<>();
paramMap.put(“name”, “国哥很久没帅了!”);
paramMap.put(“salary”, new BigDecimal(10000));
namedParameterJdbcTemplate.update(sql, paramMap);
}
实验8:重复实验7,以SqlParameterSource形式传入参数值
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后总结
搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析
最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
验8:重复实验7,以SqlParameterSource形式传入参数值
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-nv7Gdbug-1712221249269)]
[外链图片转存中…(img-R9kDPB14-1712221249269)]
[外链图片转存中…(img-5sW9zLk1-1712221249270)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后总结
搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析
最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化
[外链图片转存中…(img-tIJsAiRx-1712221249270)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
更多推荐
所有评论(0)