1.概述
JoinPoint(连接点):目标对象中,所有可以增强的方法,就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。
Pointcut(切入点):目标对象中,已经被增强的方法。调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法。
Advice(通知/增强) :增强方法的代码、想要的功能。
Target(目标对象):被代理对象,被通知的对象,被增强的类对象。
Weaving(织入):将通知应用到连接点形成切入点的过程
Proxy(代理):将通知织入到目标对象之后形成的代理对象
aspect(切面):切入点+通知————通知(Advice)说明了干什么的内容(即方法体代码)和什么时候干(什么时候通过方法名中的before,after,around等就能知道),二切入点说明了在哪干(指定到底是哪个方法),切点表达式等定义。
2. AOP详解
2.1 相关术语
- 目标类:需要被增强的类、代理的目标对象。
- 连接点:目标类可能被增强的每一个方法。
- 切入点:特殊的连接点,已经被增强了。
- 通知/增强:增强的方法
2.2 切入点表达式
-
作用:将
通知/增强
作用于具体切入点
-
基本格式:
指示符(表达式) 1.指示符分类: 【execution】:用于匹配方法执行的连接点; within:用于匹配指定类型内的方法执行; this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配; target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配; args:用于匹配当前执行的方法传入的参数为指定类型的执行方法; @within:用于匹配所以持有指定注解类型内的方法; @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解; @args:用于匹配当前执行的方法传入的参数持有指定注解的执行; @annotation:用于匹配当前执行方法持有指定注解的方法; 2.表示式格式: execution([修饰符] 返回值类型 包名.类名.方法名(参数类型列表) [throws 异常]) 返回值: * 表示返回任意值 包名: com.czxy.user.dao 具体包名 com.czxy.*.dao 任意模块的dao com.czxy.*sys.dao 固定后缀的包 com.czxy.user.dao.. 任意子包 (dao.impl包) 类名 UserDao 具体类名 *Dao 固定后缀 User* 固定前缀 * 任意 方法: findAll 具体方法名 find* 固定前缀 *All 固定后缀 * 任意 (参数类型列表): () 无参 (int) 第一个整形参数 (int,int) 两个参数都是整形 (*) 任意一个参数 (..) 参数任意
// 完整的实例代码 * * com.czxy.service..User*.select*(..) // 常见的写法 * com.czxy.service..*.*(..)
2.3 通知类型
-
spring通知共5个分类:前置通知、后置通知、环绕通知、抛出异常通知、最终通知
try{ // 前置通知、环绕通知 // 目标类的方法 // 后置通知、环绕通知 } catch() { // 抛出异常通知 } finally { // 最终通知 } //前置通知 @Before //后置通知 @AfterReturning //环绕通知 @Around //抛出异常通知 @AfterThrowing //最终通知 @After
2.3.1 前置通知
@Before("execution(* com.czxy.demo16_aop.dao..*.*(..))")
public void myBeforeAdvice() {
System.out.println("开启事务");
}
2.3.2 后置通知
// 返回值:类型 变量名
// 类型,必须是Object
// 变量名,需要通过returning设置,且提供对应的方法参数
@AfterReturning(value="切入点表达式", returning = "返回值变量名")
public void 方法名(Object 返回值变量名) {
}
@AfterReturning(value="execution(* com.czxy.demo16_aop.dao..*.*(..))", returning = "obj")
public void myAfterReturningAdvice(JoinPoint joinPoint, Object obj) {
System.out.println("目标类:" + joinPoint.getTarget());
System.out.println("方法名:" + joinPoint.getSignature().getName());
System.out.println("返回值:" + obj);
System.out.println("提交事务");
}
2.3.3 环绕通知
// 环绕通知:必须手动执行目标方法(连接点) ProceedingJoinPoint 可执行连接点
@Around("myPointcut()")
public Object myAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕前2");
// 执行目标
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕后2");
return proceed;
}
2.3.4 异常通知
// 异常通知,可以获得异常具体信息,异常类型(Throwable) 异常变量名
@AfterThrowing(value="execution(* com.czxy.demo16_aop.dao..*.*(..))",throwing = "e")
public void myAfterThrowingAdvice(Throwable e) {
System.out.println("异常通知:" + e.getMessage());
}
2.3.5 最终通知
@After("execution(* com.czxy.demo16_aop.dao..*.*(..))")
public void myAfterAdvice() {
System.out.println("释放资源");
}
2.3.6 抽取切入点
使用 @Pointcut 抽取切入点,通过 方法名 进行引用。
package com.czxy.demo16_aop.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAspect2 {
// 抽取公共切入点表达式
@Pointcut("execution(* com.czxy.demo16_aop.dao..*.*(..))")
private void myPointcut() {
}
// 前置通知
// @Before("myPointcut()")
public void myBeforeAdvice() {
System.out.println("开启事务2");
}
// 后置通知
// @AfterReturning(value="myPointcut()", returning = "obj")
public void myAfterReturningAdvice(JoinPoint joinPoint, Object obj) {
System.out.println("目标类2:" + joinPoint.getTarget());
System.out.println("方法名2:" + joinPoint.getSignature().getName());
System.out.println("返回值2:" + obj);
System.out.println("提交事务2");
}
// 环绕通知:必须手动执行目标方法(连接点) ProceedingJoinPoint 可执行连接点
@Around("myPointcut()")
public Object myAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕前2");
// 执行目标
Object proceed = proceedingJoinPoint.proceed();
System.out.println("环绕后2");
return proceed;
}
// 异常通知,可以获得异常具体信息,异常类型(Throwable) 异常变量名
@AfterThrowing(value="myPointcut()",throwing = "e")
public void myAfterThrowingAdvice(Throwable e) {
System.out.println("异常通知2:" + e.getMessage());
}
@After("myPointcut()")
public void myAfterAdvice() {
System.out.println("释放资源2");
}
}
3. Spring 整合 MyBatis
3.1 目标:
- 使用spring整合MyBatis
3.2 分析
- 搭建环境:mybatis、spring、整合jar
- 项目名:day10_sm
- 整合过程中,将采用配置类的方法。
- 步骤:
- 编写UserMapper 接口,继承通用mapper
- 编写UserService接口,查询所有
- 编写UserServiceImpl实现类,查询所有
- 编写Spring配置类
- 编写MyBatis配置类,取代 SqlConfigMap.xml文件
- 测试类
3.3 实现
3.3.1 编写UserMapper 接口,继承通用mapper
package com.czxy.sm.mapper;
import com.czxy.sm.domain.User;
import tk.mybatis.mapper.common.Mapper;
public interface UserMapper extends Mapper<User> {
}
3.3.2 编写UserService接口,查询所有
package com.czxy.sm.service;
import com.czxy.sm.domain.User;
import java.util.List;
public interface UserService {
/**
* 查询所有
* @return
*/
public List<User> selectAll();
}
3.3.3 编写UserServiceImpl实现类,查询所有
- service实现类需要添加到spring容器,需要使用 @Service注解
package com.czxy.sm.service.impl;
import com.czxy.sm.domain.User;
import com.czxy.sm.mapper.UserMapper;
import com.czxy.sm.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
public List<User> selectAll() {
return userMapper.selectAll();
}
}
3.3.4 编写Spring配置类
- 在spring配置类中,主要配置数据源 DataSource (连接池)
- 注意:配置类负责所有组件扫描
package com.czxy.sm.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
@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;
@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/ssm_db1 jdbc.username=root jdbc.password=1234
3.3.5 编写MyBatis配置类,取代 SqlConfigMap.xml文件
- 配置 SQLSessionFactory 用于取代 SqlMapConfig.xml 文件(别名、插件 等)
- Mapper扫描器需要单独配置
- MyBatis内置:org.mybatis.spring.mapper.MapperScannerConfigurer
- 通用Mapper:tk.mybatis.spring.mapper.MapperScannerConfigurer
package com.czxy.sm.config;
import com.github.pagehelper.PageHelper;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import tk.mybatis.spring.mapper.MapperScannerConfigurer;
import javax.sql.DataSource;
import java.util.Properties;
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;
}
}
2.3.6 测试类
package com.czxy.sm;
import com.czxy.sm.config.MyBatisConfiguration;
import com.czxy.sm.config.SpringConfiguration;
import com.czxy.sm.domain.User;
import com.czxy.sm.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class, MyBatisConfiguration.class})
public class TestSM {
@Resource
private UserService userService;
@Test
public void testDemo() {
List<User> list = userService.selectAll();
list.forEach(System.out::println);
}
}