• Spring-AOP配置(注解及多方整合案例)


    目录

    注解配置AOP

    注解开发AOP制作步骤

    注解开发AOP注意事项

    小记

    实例演示

    注解AOP通知执行顺序控制

    AOP注解驱动

    实例演示

    注解多方整合案例

    案例介绍

    案例分析

    案例制作步骤

    案例后续思考与设计

    实例演示


    • 注解配置AOP

    • 注解开发AOP制作步骤

    • 在XML格式基础上
    • 导入坐标(伴随spring-context坐标导入已经依赖导入完成)
    • 开启AOP注解支持
    • 配置切面@Aspect
    • 定义专用的切入点方法,并配置切入点@Pointcut
    • 为通知方法配置通知类型及对应切入点如@Before
    • 注解开发AOP注意事项

    • 1.切入点最终体现为一个方法,无参无返回值,无实际方法体内容,但不能是抽象方法
    • 2.引用切入点时必须使用方法调用名称,方法后面的()不能省略
    • 3.切面类中定义的切入点只能在当前类中使用,如果想引用其他类中定义的切入点要使用“类名.方法名()”引用
    • 4.可以在通知类型注解后添加参数,实现XML配置中的属性,例如after-returning后的returning属性
    • 小记

    • 名称:@Aspect
    • 类型:注解
    • 位置:类定义上方
    • 作用:设置当前类为切面类
    • 格式:
    • @Aspect
    • public class AopAdvice{}
    • 说明:一个beans标签中可以配置多个aop:config标签
    • 名称:@Pointcut
    • 类型:注解
    • 位置:方法定义上方
    • 作用:使用当前方法名作为切入点引用名称
    • 格式:
    • @Pointcut("execution(* *(..))")
    • public void pt(){}
    • 说明:被修饰的方法忽略其业务功能,格式设定为无参无返回值的方法,方法体内空实现(非抽象)
    • 名称:@Before
    • 类型:注解
    • 位置:方法定义上方
    • 作用:标注当前方法作为前置通知
    • 格式:
    • @Before("pt()")
    • public void before(){}
    • 特殊参数:
    • 名称:@After
    • 类型:注解
    • 位置:方法定义上方
    • 作用:标注当前方法作为后置通知
    • 格式:
    • @After("pt()")
    • public void after(){}
    • 特殊参数:
    • 名称:@AfterReturning
    • 类型:注解
    • 位置:方法定义上方
    • 作用:标注当前方法作为返回后通知
    • 格式:
    • @AfterReturning(value="pt()",returning="ret")
    • public void afterReturning(Object ret) {}
    • 特殊参数:
    • returning:设定使用通知方法参数接收返回值的变量名
    • 名称:@AfterThrowing
    • 类型:注解
    • 位置:方法定义上方
    • 作用:标注当前方法作为异常后通知
    • 格式:
    • @AfterThrowing(value="pt()",throwing="t")
    • public void afterThrowing(Throwable t){}
    • 特殊参数:
    • throwing:设定使用通知方法参数接收原始方法中抛出的异常对象名
    • 名称:@Around
    • 类型:注解
    • 位置:方法定义上方
    • 作用:标注当前方法作为环绕通知
    • 格式:
    • @Around("pt()")
    • public Object around(ProceedingJoinPoint pjp) throws Throwable{Object ret=pjp.proceed();
    • return ret;}
    • 特殊参数:
    • 实例演示

      1. <context:component-scan base-package="com.superdemo"/>
      2. <aop:aspectj-autoproxy/>
      1. @Service("userService")
      2. public class UserServiceImpl implements UserService {
      3. public void save(int i){
      4. //0.将共性功能抽取出来
      5. //System.out.println("共性功能");
      6. System.out.println("user service running..."+i);
      7. //int i=1/0;
      8. }
      9. @Override
      10. public int update(int a,int b) {
      11. System.out.println("user service update running...");
      12. return a+b;
      13. }
      14. @Override
      15. public void delete() {
      16. System.out.println("user service delete running...");
      17. int i = 1/0;
      18. }
      19. }
      1. public class AOPPointcut {
      2. @Pointcut("execution(* *..*(..))")
      3. public void pt1(){}
      4. }
      1. //1.制作通知类,在类中定义一个方法用于完成共性功能
      2. @Component
      3. @Aspect
      4. public class AOPAdvice {
      5. //@Pointcut("execution(* *..*(..))")
      6. //public void pt(){}
      7. @Before("AOPPointcut.pt1()")
      8. public void before(JoinPoint jp){
      9. Object[] args = jp.getArgs();
      10. System.out.println("前置before..."+args[0]);
      11. }
      12. @After("AOPPointcut.pt1()")
      13. public void after(JoinPoint jp){
      14. Object[] args = jp.getArgs();
      15. System.out.println("后置after..."+args[0]);
      16. }
      17. @AfterReturning(value = "AOPPointcut.pt1()",returning = "ret")
      18. public void afterReturing(Object ret){
      19. System.out.println("返回后afterReturing..."+ret);
      20. }
      21. @AfterThrowing(value = "AOPPointcut.pt1()",throwing = "t")
      22. public void afterThrowing(Throwable t){
      23. System.out.println("抛出异常后afterThrowing..."+t.getMessage());
      24. }
      25. @Around("AOPPointcut.pt1()")
      26. public Object around(ProceedingJoinPoint pjp) throws Throwable {
      27. System.out.println("环绕前around before...");
      28. //对原始方法的调用
      29. /*Object ret = null;
      30. try {
      31. ret = pjp.proceed();
      32. } catch (Throwable e) {
      33. System.out.println("around...exception..."+e.getMessage());
      34. }*/
      35. Object ret = pjp.proceed();
      36. System.out.println("环绕后around after...");
      37. return ret;
      38. }
      39. }
      1. public class App {
      2. public static void main(String[] args) {
      3. ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
      4. UserService userService = (UserService) ctx.getBean("userService");
      5. //userService.save(666);
      6. int tj = userService.update(666,123);
      7. System.out.println("执行结果...."+tj);
      8. //userService.delete();
      9. }
      10. }
    • 注解AOP通知执行顺序控制

    • AOP使用XML配置情况下,通知的执行顺序由配置顺序决定
    • 在注解情况下由于不存在配置顺序的概念的概念,参照通知所配置的方法名字符串对应的编码值顺序,可以简单理解为字母排序
    • 同一个通知类中,相同通知类型以方法名排序为准
    • 不同通知类中,以类名排序为准
    • 使用@Order注解通过变更bean的加载顺序改变通知的加载顺序
    • 企业开发经验
    • 通知方法名由3部分组成,分别是前缀、顺序编码、功能描述
    • 前缀为固定字符串,例如baidu、Swisse等,无实际意义
    • 顺序编码为6位以内的整数,通常3位即可,不足位补0
    • 功能描述为该方法对应的实际通知功能,例如exception、strLenCheck
    • 控制通知执行顺序使用顺序编码控制,使用时做一定空间预留
      • 003使用,006使用,预留001、002、004、005、007、008
      • 使用时从中段开始使用,方便后期做前置追加或后置追加
      • 最终顺序以运行顺序为准,以测试结果为准,不以设定规则为准
    • AOP注解驱动

    • 名称:@EnableAspectJAutoProxy
    • 类型:注解
    • 位置:Spring注解配置类定义上方
    • 作用:设置当前类开启AOP注解驱动的支持,加载AOP注解
    • 格式:
    • @Configuration
    • @ComponentScan("com.superdemo")
    • @EnableAspectJAutoProxy
    • public class SpringConfig{}
    • 实例演示

      1. @Configuration
      2. @ComponentScan("com.superdemo")
      3. @EnableAspectJAutoProxy
      4. public class SpringConfig {
      5. }
      1. @RunWith(SpringJUnit4ClassRunner.class)
      2. @ContextConfiguration(classes = SpringConfig.class)
      3. public class UserServiceTest {
      4. @Autowired
      5. private UserService userService;
      6. @Test
      7. public void testupdate(){
      8. int ret = userService.update(666, 123);
      9. Assert.assertEquals(789,ret);
      10. }
      11. }
    • 注解多方整合案例

    • 案例介绍

    • 对项目进行业务层接口执行监控,测量业务层接口的执行效率
    • 案例分析

    • 测量接口执行效率:接口方法执行前后获取执行时间,求出执行时长
      • System.currentTimeMillis()
    • 对项目进行监控:项目中所有接口方法,AOP思想,执行期动态织入代码
      • 环绕通知
      • proceed()方法执行前后获取系统时间
    • 案例制作步骤

    • 定义切入点(务必要绑定到接口上,而不是接口实现类上)
    • 制作AOP环绕通知,完成测量功能
    • 注解配置AOP
    • 开启注解驱动支持
    • 案例后续思考与设计

    • 测量真实性
      • 开发测量是隔离性反复执行某个操作,是理想情况,上线测量差异过大
      • 上线测量服务器性能略低于单机开发测量
      • 上线测量基于缓存的性能查询要优于数据库查询测量
      • 上线测量接口的性能与最终对外提供的服务性能差异过大
      • 当外部条件发生变化(硬件),需要进行回归测试,例如数据库迁移
    • 测量结果展示
      • 测量结果无需每一个都展示,需要设定检测阈值
      • 阈值设定要根据业务进行区分,一个复杂的查询与简单的查询差异化很大
      • 阈值设定需要做独立的配置文件或通过图形工具配置(工具级别的开发)
      • 配合图形界面展示测量结果
    • 实例演示

    • src下domain层
      1. public class Account {
      2. private Integer id;
      3. private String name;
      4. private Double money;
      5. public Integer getId() {
      6. return id;
      7. }
      8. public void setId(Integer id) {
      9. this.id = id;
      10. }
      11. public String getName() {
      12. return name;
      13. }
      14. public void setName(String name) {
      15. this.name = name;
      16. }
      17. public Double getMoney() {
      18. return money;
      19. }
      20. public void setMoney(Double money) {
      21. this.money = money;
      22. }
      23. @Override
      24. public String toString() {
      25. return "Account{" +
      26. "id=" + id +
      27. ", name='" + name + '\'' +
      28. ", money=" + money +
      29. '}';
      30. }
      31. }
    • src下dao层
      1. public interface AccountDao {
      2. @Insert("INSERT INTO account(name, money) VALUES (#{name},#{money})")
      3. void save(Account account);
      4. @Delete("DELETE FROM account WHERE id = ${id}")
      5. void delete(Integer id);
      6. @Update("UPDATE account SET name = #{name},money = #{money} WHERE id = #{id}")
      7. void update(Account account);
      8. @Select("SELECT * FROM account")
      9. List findAll();
      10. @Select("SELECT * FROM account WHERE id = #{id}")
      11. Account findByid(Integer id);
      12. }
    • src下service层
      1. public interface AccountService {
      2. void save(Account account);
      3. void delete(Integer id);
      4. void update(Account account);
      5. List findAll();
      6. Account findByid(Integer id);
      7. }
      1. @Service("accountService")
      2. public class AccountServiceImpl implements AccountService {
      3. @Autowired
      4. private AccountDao accountDao;
      5. /*public void setAccountDao(AccountDao accountDao) {
      6. this.accountDao = accountDao;
      7. }*/
      8. public void save(Account account){
      9. accountDao.save(account);
      10. }
      11. public void delete(Integer id){
      12. accountDao.delete(id);
      13. }
      14. public void update(Account account){
      15. accountDao.update(account);
      16. }
      17. public List findAll(){
      18. return accountDao.findAll();
      19. }
      20. public Account findByid(Integer id){
      21. return accountDao.findByid(id);
      22. }
      23. }
    • src下resources
      1. jdbc.driver=com.mysql.cj.jdbc.Driver
      2. jdbc.url=jdbc:mysql://localhost:3306/dp1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
      3. jdbc.username=root
      4. jdbc.password=123456
    • src下aop
      1. @Component
      2. @Aspect
      3. public class RunTimeMonitorAdvice {
      4. //切入点,监控业务层接口
      5. @Pointcut("execution(* com.superdemo.service.*Service.find*(..))")
      6. public void pt(){}
      7. @Around("pt()")
      8. public Object runtimeAround(ProceedingJoinPoint pjp) throws Throwable {
      9. Signature signature = pjp.getSignature();
      10. String className = signature.getDeclaringTypeName();
      11. String methodName = signature.getName();
      12. Object ret = null;
      13. long sum = 0L;
      14. for(int i = 0; i < 10000; i++){
      15. long startTime = System.currentTimeMillis();
      16. ret= pjp.proceed();
      17. long endTime = System.currentTimeMillis();
      18. sum += endTime-startTime;
      19. }
      20. System.out.println(className+":"+methodName+":万次运行了:"+sum+"ms");
      21. return ret;
      22. }
      23. }
    • src下config层
      1. public class JDBCConfig {
      2. @Value("${jdbc.driver}")
      3. private String driver;
      4. @Value("${jdbc.url}")
      5. private String url;
      6. @Value("${jdbc.username}")
      7. private String userName;
      8. @Value("${jdbc.password}")
      9. private String password;
      10. @Bean("dataSource")
      11. public DataSource getDataSource(){
      12. DruidDataSource ds = new DruidDataSource();
      13. ds.setDriverClassName(driver);
      14. ds.setUrl(url);
      15. ds.setUsername(userName);
      16. ds.setPassword(password);
      17. return ds;
      18. }
      19. }
      1. public class MybatisConfig {
      2. /*
      3. 对应XML格式下:
      4. spring整合mybatis后控制的创建连接用的对象
      5. 加载mybatis映射配置的扫描,将其作为spring的bean进行管理
      6. */
      7. @Bean
      8. public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
      9. SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
      10. ssfb.setTypeAliasesPackage("com.superdemo.domain");
      11. ssfb.setDataSource(dataSource);
      12. return ssfb;
      13. }
      14. @Bean
      15. public MapperScannerConfigurer getMapperScannerConfigurer(){
      16. MapperScannerConfigurer msc = new MapperScannerConfigurer();
      17. msc.setBasePackage("com.superdemo.dao");
      18. return msc;
      19. }
      20. }
      1. @Configuration
      2. @ComponentScan("com.superdemo")
      3. @PropertySource("classpath:jdbc.properties")
      4. @Import({JDBCConfig.class, MybatisConfig.class})
      5. @EnableAspectJAutoProxy
      6. public class SpringConfig {
      7. }
    • test下测试
      1. //设定spring专用的类加载器
      2. @RunWith(SpringJUnit4ClassRunner.class)
      3. //设定加载的spring上下文对应的配置
      4. @ContextConfiguration(classes = SpringConfig.class)
      5. public class UserServiceTest {
      6. @Autowired
      7. private AccountService accountService;
      8. @Test
      9. public void testFindById(){
      10. Account ac = accountService.findByid(2);
      11. System.out.println(ac);
      12. }
      13. @Test
      14. public void testFindAll(){
      15. List list = accountService.findAll();
      16. Assert.assertEquals(3,list.size());
      17. }
      18. }
  • 相关阅读:
    Cmd中的一些命令
    吴恩达机器学习笔记 二十一 迁移学习 预训练
    程序设计题 2:双11抢宝计划
    PIC12F510作为PMBus主机
    太速科技-多路PCIe的阵列计算全国产化服务器
    Tomcat报BAD packet signature 18245错误的原因
    如何用WebGPU流畅渲染百万级2D物体?
    个人网站开发——全栈项目开发日记
    Netty笔记
    【Java】控制语句学习笔记
  • 原文地址:https://blog.csdn.net/weixin_59624686/article/details/126920791