Aop的相关术语、切点表达式、AOP的通知类型以及Spring整合Mybatis的案例笔记。
一、AOP相关术语
1.目标类Target:需要被增强的类、代理的目标对象。
2.连接点Joinpoint:目标类 可能 被增强的每一个方法。
3.切入点Pointcut:特殊的链接点,已经被增强了。
4.通知/增强Advice:增强的方法,需要添加的那段增强功能的逻辑代码。
5.切面Aspect:切入点和通知的结合
6.织入:增强添加到目标类的具体连接点上的过程。
7.代理Proxy:一个类被AOP织入增强后,就产生一个结果代理类。
二、切入点表达式
1.作用:
将通知/增强
作用于具体切入点
2.基本格式:
指示符(表达式)
指示符分类:
execution:用于匹配方法执行的连接点;【重点】
within:用于匹配指定类型内的方法执行;
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;
@within:用于匹配所以持有指定注解类型内的方法;
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行;
@annotation:用于匹配当前执行方法持有指定注解的方法;
3.execution 表达式
execution([修饰符] 返回值类型 包名.类名.方法名(参数类型列表) [throws 异常])
返回值:
void 没有返回值
int 返回整数
* 表示返回任意值
包名:
com.czxy.user.dao 具体包名
com.czxy.*.dao 任意模块的dao
com.czxy.*sys.dao 固定后缀的包
com.czxy.user.dao.. 任意子包 (dao.impl包)
com.czxy.*.dao.. 任意模块,dao下任意子包 (dao.impl包)
类名
UserDao 具体类名
*Dao 固定后缀
User* 固定前缀
* 任意
方法:
findAll 具体方法名
find* 固定前缀
*All 固定后缀
* 任意
(参数类型列表):
() 无参
(int) 第一个整形参数
(int,int) 两个参数都是整形
(*) 任意一个参数
(..) 参数任意
#综合
execution(* com.czxy..*.*(..))
三、通知类型
spring通知共5个分类:前置通知、后置通知、环绕通知、抛出异常通知、最终通知
1.位置:
try{
// 前置通知、环绕通知
// 目标类的方法
// 后置通知、环绕通知
} catch() {
// 抛出异常通知
} finally {
// 最终通知
}
2.注解:
通知名称 | 注解 | 应用场景 |
前置通知 | @Before | 初始化、加载配置文件 |
后置通知 | @AfterReturning | 处理数据(Java对象转换成JSON字符串) |
环绕通知 | @Around | 事务管理(开启事务、提交/回滚事务) |
抛出异常通知 | @AfterThrowing | 错误日志管理 |
最终通知 | @After | 释放资源 |
3.测试:
3.1 前置通知:
切面类:
/**
* 前置通知
* 参数1:JoinPoint joinPoint,可选
*/
//@Before("execution(* com.czxy.demo16_aop_interface.service..*.*())")
public void MyBefore(){//开启事务
System.out.println("--------");
System.out.println("前置通知");
}
service实现类:
@Service
public class UserServiceImpl implements UserService {
@Override
public String insert() {
System.out.println("添加");
return "嗯嗯";
}
@Override
public int update() {
System.out.println("更新");
return 250;
}
}
测试类:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = Demo16Configuration.class)
public class TestDemo16 {
@Resource
private UserService userService;
@Test
public void run (){
userService.insert();
userService.update();
}
}
测试结果:
3.2后置通知:
切面类:
/**
* 后置通知
* 参数1:JoinPoint joinPoint,可选
* 参数2:Object obj,可选
* 要求1:类型必须Object
* 要求2:变量名,必须与returning = "返回值变量名"一致
*/
@AfterReturning(value = "execution(* com.czxy.demo16_aop_interface.service..*.*())" , returning = "obj")
public void myAfterReturning(JoinPoint joinPoint , Object obj){//结束事务
//通过JoinPoint可以获得连接点信息
// 1 通过连接点获得方法签名,并获得方法名
System.out.println(joinPoint.getSignature().getName());
System.out.println("后置通知:"+obj);
}
测试结果:
3.3环绕通知:
切面类:
/**
* 环绕通知
* 要求1:返回值类型 Object
* 要求2:参数类型 ProceedingJoinPoint ,【必须】 表示可运行的连接点
* 要求3:需要手动执行目标方法,并处理异常
* 要求4:执行目标方法后,需获得返回值,并返回
* @return
*/
@Around("execution(* com.czxy.demo16_aop_interface.service..*.*())")
public Object myAround(ProceedingJoinPoint projoinPoint) throws Throwable {
System.out.println("环绕前");
// 手动执行目标方法,获得返回值
Object obj = projoinPoint.proceed();
System.out.println(obj);
System.out.println("环绕后");
// 返回
return obj;
}
测试结果:
3.4抛出异常通知:
切面类:
/**
* 抛出异常通知
* 参数1:JoinPoint joinPoint,可选
* 参数2:Exception e,可选
* 要求1:类型必须Exception ,表示异常
* 要求2:变量名,必须与throwing = "返回值变量名"一致
* @param joinPoint
* @param e
*/
//简单使用
/*
@AfterThrowing("execution(* com.czxy.demo16_aop_interface.service..*.*())")
public void myAfterThrowing(){
System.out.println("抛出异常通知");
}
*/
@AfterThrowing(value = "execution(* com.czxy.demo16_aop_interface.service..*.*())" , throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint , Exception e){
//切入点的方法名
System.out.println(joinPoint.getSignature().getName());
System.out.println("抛出异常通知: " + e.getMessage());
}
service实现类:
@Service
public class UserServiceImpl implements UserService {
@Override
public String insert() {
System.out.println("添加");
//模拟异常
int i = 1/0;
return "嗯嗯";
}
@Override
public int update() {
System.out.println("更新");
return 250;
}
}
测试结果:
3.5最终通知:
切面类:
/**
* 最终通知
* 参数1:JoinPoint joinPoint,可选
*/
@After("execution(* com.czxy.demo16_aop_interface.service..*.*())")
public void myAfter(){
System.out.println("最终通知");
}
测试结果:
3.6抽取接入点表达式:
语法:
//声明切入点,建议私有方法
@Pointcut("抽取的切入点")
private void 方法名(){
}
//使用
@Before("方法名()") //方法名进行引用。
切面类:
@Component
@Aspect //支持AOP
public class MyAspect2 {
/**
* 抽取切入点表达式
*
* 建议方法私有
*/
@Pointcut("execution(* com.czxy.demo16_aop_interface.service..*.*())")
private void myPointCut(){}
@Before("myPointCut()")
public void myBefore(){//开启事务
System.out.println("--------");
System.out.println("前置通知");
}
//@AfterReturning(value = "myPointCut()" , returning = "obj")
public void myAfterReturning(JoinPoint joinPoint , Object obj){//结束事务
//通过JoinPoint可以获得连接点信息
// 1 通过连接点获得方法签名,并获得方法名
System.out.println(joinPoint.getSignature().getName());
System.out.println("后置通知:"+obj);
}
//@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint projoinPoint) throws Throwable {
System.out.println("环绕前");
// 手动执行目标方法,获得返回值
Object obj = projoinPoint.proceed();
System.out.println(obj);
System.out.println("环绕后");
// 返回
return obj;
}
//@AfterThrowing(value = "myPointCut()" , throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint , Exception e){
//切入点的方法名
System.out.println(joinPoint.getSignature().getName());
System.out.println("抛出异常通知: " + e.getMessage());
}
//@After("myPointCut()")
public void myAfter(){
System.out.println("最终通知");
}
}
四、Spring整合Mybatis案例
1.项目结构:
2.分析:
- 步骤:
- 编写UserMapper 接口,继承通用mapper
- 编写UserService接口,查询所有、根据id查询详情、添加、修改、删除
- 编写UserServiceImpl实现类,查询所有根据id查询详情、添加、修改、删除
- 编写Spring配置类
- 编写MyBatis配置类,取代 SqlConfigMap.xml文件
- 测试类
3.实现:
环境:User + sql
public class User {
@Id
private String uid;
private String username;
private String password;
private String name;
private String email;
private String telephone;
private Date birthday;
private String sex;
private Integer state;
private String code;
@Override
public String toString() {
return "User{" +
"uid='" + uid + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", name='" + name + '\'' +
", email='" + email + '\'' +
", telephone='" + telephone + '\'' +
", birthday=" + birthday +
", sex='" + sex + '\'' +
", state=" + state +
", code='" + code + '\'' +
'}';
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public User(String uid, String username, String password, String name, String email, String telephone, Date birthday, String sex, Integer state, String code) {
this.uid = uid;
this.username = username;
this.password = password;
this.name = name;
this.email = email;
this.telephone = telephone;
this.birthday = birthday;
this.sex = sex;
this.state = state;
this.code = code;
}
public User() {
}
}
UserMapper:
public interface UserMapper extends Mapper<User> {
}
service接口:
public interface UserService{
/**
* 查询所有
* @return
*/
List<User> selectAll();
/**
* 添加
* @param user
* @return
*/
Integer insert(User user);
/**
* 根据id查询所有
* @param uid
* @return
*/
User selectById(String uid);
/**
* 根据id删除
* @param uid
* @return
*/
Integer delete(String uid);
/**
* 修改 更新
* @param user
* @return
*/
Integer update(User user);
}
UserService实现类:
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
public List<User> selectAll() {
return userMapper.selectAll();
}
@Override
public Integer insert(User user) {
return userMapper.insert(user);
}
@Override
public User selectById(String uid) {
return userMapper.selectByPrimaryKey(uid);
}
@Override
public Integer delete(String uid) {
return userMapper.deleteByPrimaryKey(uid);
}
@Override
public Integer update(User user) {
return userMapper.updateByPrimaryKey(user);
}
}
Mybatis 配置类:
@Configuration
public class MybatisConfiguration {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
//1 创建工厂
// 1.通过工厂bean创建对象,最后需要调用 getObject()获得具体的对象
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
//2 设置数据-- SqlMapConfig.xml 配置信息
// 1.1 设置数据源
factoryBean.setDataSource(dataSource);
// 1.2 设置别名包扫描
factoryBean.setTypeAliasesPackage("com.czxy.sm.domain");
// 1.3 全局配置:驼峰映射
org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
config.setMapUnderscoreToCamelCase(true);
factoryBean.setConfiguration(config);
// 2 插件配置
// 2.1 分页插件
PageHelper pageHelper = new PageHelper();
Properties pageProps = new Properties();
pageProps.setProperty("dialect", "mysql");
pageProps.setProperty("rowBoundsWithCount", "true");
pageHelper.setProperties(pageProps);
factoryBean.setPlugins(new Interceptor[] { pageHelper });
// 返回SqlSessionFactory
return factoryBean.getObject();
}
/**
* 扫描Dao的包,查找各种XxxMapper接口,创建好UserMapper等对象存入到IOC的容器中
* @return
*/
@Bean
public MapperScannerConfigurer mapperScanner() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.czxy.sm.mapper");
return configurer;
}
}
Spring配置类:
@Configuration
@PropertySource("classpath:db.properties")
@ComponentScan(basePackages = {"com.czxy.sm"})
public class SpringConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 连接池配置
* @return
*/
@Bean
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
db.properties文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_20211028
jdbc.username=root
jdbc.password=1234
测试类:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class, MybatisConfiguration.class})
public class TestSM {
@Resource
private UserService userService;
@Test
public void testSelectAll() {
List<User> list = userService.selectAll();
list.forEach(System.out::println);
}
@Test
public void testInsert() {
User user = new User();
user.setUid("u009");
user.setUsername("jack");
user.setPassword("1234");
System.out.println(userService.insert(user));
}
@Test
public void testSelectById(){
System.out.println(userService.selectById("u009"));
}
@Test
public void testDelete(){
System.out.println(userService.delete("u009"));
}
@Test
public void testUpdate(){
User user = new User();
user.setUid("u009");
user.setUsername("jack");
user.setPassword("1234");
user.setName("杰克2");
System.out.println(userService.update(user));
}
}