D42-Spring(AOP)

1.回顾AOP底层

jdk动态代理
cglib的动态代理

2.AOP的概述

AOP术语

3.AOP的配置

全xml的配置
半xml,半注解
纯注解的配配置

4.使用spring的AOP技术,做转账的事务增强。—声明式事务的底层

一、动态代理:在不改变源代码的情况下对方法增强

  1. jdk动态代理(基于接口)
    • 条件: 要有接口
    • 生成的动态代理对象和被代理对象是兄弟关系
    • 工具类-Proxy
public class Demo_JDK {

    public static void main(String[] args) {
        final AccountService accountService = new AccountServiceImpl();
        //String value = accountService.save("aaaaa");
        //System.out.println(value);

        // 在不修改方法源码的基础上,对方法进行增强 ---save()
        // 动态代理:JDK提供的动态代理   CGLIB提供的动态代理
        // JDK提供的动态代理

        // 参数1: 和目标对象一样的类加载器
        // 参数2:和目标对象一样的接口
        AccountService proxy =(AccountService) 
        	Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                new Class[]{AccountService.class},
                // 匿名内部类
                new InvocationHandler() {
                    // 增强业务  代理对象调用方法的时候就执行 调用一次执行一次
                    // 参数1 :代理对象的引用 (谨慎用)
                    // 参数2 : 目标方法(save)
                    // 参数3:  目标方法执行过程中需要的参数(aaaa)
                    public Object invoke(Object proxy, Method method, 
                    	Object[] args) throws Throwable {
                        if("save".equals(method.getName())){
                            try {
                                // 增强
                                System.out.println("之前增强。。。");
                                // 调用原方法
                                // 参数1:本身应该执行原方法的对象
                                // 参数2:原方法需要的参数
                                Object value = method.invoke(
                                	accountService, args); //原来方法
                                System.out.println(value);

                                System.out.println("之后增强。。。");

                            }catch (Exception e){

                                System.out.println("异常增强...");

                            }finally {
                                System.out.println("最终增强....");
                            }

                            // invoke是谁调用的就返回给谁内容
                            return "我知道是你代理对象调用的,我把数据返回给你";

                        }else
                        {
                            // 调用你原来的方法 我不给你增强
                            method.invoke(accountService, args);
                            return null;
                        }

                    }
                });


        // 代理对象调用方法
        String value=proxy.save("aaaaa"); //只要代理对象调用方法 invoke就会
        //执行 invoke执行的是对调用方法(save)的增强
        System.out.println(value);
        //proxy.delete();
    }
}

  1. cglib的动态代理(基于类)
  • 代理对象与对象的 关系-------父子关系
  • 条件:
    • 目标对象的类不需要接口但也不能被final修饰
    • 导入cglib的jar包—通过工具类-----Enhancer生成的动态代理对象是被代理对象的子类。
public class Demo_CGLIB {
    public static void main(final String[] args) {
        final AccountServiceImpl2 accountServiceImpl2 = new 
        							AccountServiceImpl2();
        //String value = accountServiceImpl2.save("bbbbb");
        //System.out.println(value);
        // 不改变方法源码的前提下,对 save 进行增强 --动态代理
        // 第三方提供的动态代理--cglib动态代理
        //cglib动态代理:目标对象不需要接口,也可以做增强
        // 工具类--Enhancer

        //参数1:目标对象的字节码文件
        //参数2:增强业务类  InvocationHandler===MethodInterceptor
        AccountServiceImpl2 proxy=(AccountServiceImpl2)Enhancer.create(
        							accountServiceImpl2.getClass(),
                // 匿名内部类
                new MethodInterceptor() {
                    // 参数1 :代理对象的引用 (谨慎用)
                    // 参数2 : 目标方法(save)
                    // 参数3:  目标方法执行过程中需要的参数(aaaa)
                    // 参数4:方法对象的代理对象
                    public Object intercept(Object o, Method method, 
                    		Object[] objects, MethodProxy methodProxy) 
                    									throws Throwable {

                        if("save".equals(method.getName())){

                            try {
                                // 增强
                                System.out.println("之前增强。。。");
                                // 调用原方法
                                // 参数1:本身应该执行原方法的对象
                                // 参数2:原方法需要的参数
                                Object value = method.invoke
                                		(accountServiceImpl2, objects); //原来方法
                                System.out.println(value);

                                System.out.println("之后增强。。。");

                            }catch (Exception e){

                                System.out.println("异常增强...");

                            }finally {
                                System.out.println("最终增强....");
                            }

                            // invoke是谁调用的就返回给谁内容
                            return "我知道是你代理对象调用的,我把数据返回给你";

                        }else
                        {
                            // 调用你原来的方法 我不给你增强
                            method.invoke(accountServiceImpl2, objects);
                            return null;
                        }

                    }
                });

        String value = proxy.save("bbb");
        System.out.println(value);
    }
}

  1. AOP的底层会自动选择(有接口,选jdk)

二、 AOP的概述

2.1 AOP:面向切面编程

  1. 作用: 在不修改源码的基础上,对方法进行增强
  • 底层:jdk动态代理,cglib动态代理(自动抉择)
  1. 关注点1:自己创建的方法---- AOP做好了事务的方法
  2. 关注点2:告诉spring需要在哪些方法上使用增强方法-用动态代理
  3. 一句话:自己写增强方法,然后在配置文件中告诉spring,自己在哪些方法上进行了增强
    • spring:自动使用动态代理技术实现方法的增强

2.2 AOP的相关术语

  1. Target(目标对象): 要增强的对象
  2. Proxy(代理对象):对目标对象的增强封装
  3. JoinPoint(连接点):目标对象的所有方法
  4. Advice(通知/增强):增强的那段代码方法
    4.1. 一个通知就是一个增强方法
  • 前置通知:在切入点(要被增强的方法)之前的增强方法
  • 后置通知:在切入点(要被增强的方法)之后的增强方法
  • 议程通知:在切入点(要被增强的方法)发生异常执行的增强方法
  • 最终通知:在切入点(要被增强的方法)执行完毕的增强方法
  • 环绕通知:代替上面四个
  1. (Aspect)切面 :切入点+通知 = 切面
    目标方法和增强方法合成在一起
  2. Weaving(织入): 将切入点集成到切面的这个过程
    底层是用到动态代理

三、AOP的配置

3.1 AOP的xml方式:

  • 导入AOP的坐标
  • 定义一个类,自己在类中编写增强方法----ioc管理这个切面类。
  • 通过配置文件告诉spring使用自己的增强方法在哪些方法上做前置,后置,异常,最终增强。

3.2 基于xml的配置

1. 声明AOP配置
< aop:config>…</aop:config>

  • 配置切入点:
  • id:切入点的唯一标识
    • expression:切入点表达式
    • 完整写法:execution(方法的修饰符 方法的返回值 类的全限定名.方法名(参数))
<aop:pointcut id="pt" expression="execution(* com.itheima..AccountServiceImpl.save(..))">
</aop:pointcut>
<!-- 支持通配符的写法:
     *   : 标识任意字符串
      ..  : 任意重复次数
      1. 方法的修饰符可以省略:void cn.itcast.service.impl.AccountServiceImpl.saveAccount()
      2. 返回值可以使用*号代替:标识任意返回值类型
               * cn.itcast.service.impl.AccountServiceImpl.saveAccount()
     3. 包名可以使用*号代替,代表任意包(一个包使用一个*)
     4. 使用..配置包名,标识此包以及此包下的所有子包
     5. 类名可以使用*号代替,标识任意类
     6. 方法名可以使用*号代替,表示任意方法
     7. 可以使用..配置参数,任意参数
-->

2. 配置切面

  • ref:切面类的唯一标识
<aop:aspect ref="logger">
  1. 配置通知类型:前置,后置,异常,最终,环绕通知
  • method:切面类中的方法
  • pointcut-ref:切入点唯一标识
<aop:before method="before" pointcut-ref="pt"></aop:before>
//此为前置通知,后置,异常,最终通知类似
  • 环绕通知
<aop:around method="around" pointcut-ref="pt"></aop:around>

4. 测试类

@RunWith(value = SpringJUnit4ClassRunner.class)  
//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:bean.xml") 
//声明spring的配置信息+
public class SpringJunit {

    @Autowired  //默认按照 类型(接口)从容器中查找对象并注入
                 //如果该容器中有多个该接口的对象
    private AccountService accountService;

    @Test
    public void t1(){
        accountService.save();
    }
}

3.3 基于注解结合XML的方式

  • 半注解: 自己的资源
  • 半XML: 第三方的资源
  1. 注解:
  • IOC的注解
  • AOP的注解
  1. 开启注解们的支持
  • IOC包扫描
  • 开启对AOP注解的支持
      • 找切面类
      • 在切面类的通知上配置切入点表达式

3. 注解:

  • @Aspect: 声明切面类
  • @PointCut: 定义工作的切入点。
      • 配置到空方法上,value:切入点表达式
      • 引用:方法名()
    • 配置通知类型:
      • @Before:前置通知
      • @AfterReturnint : 后置通知
      • @AfterThrowing :异常通知
      • @After :最终通知
      • @Around :环绕通知
  1. 步骤(在bean.xml中):
  • 开启ioc扫描器
<!--开启ioc扫描类-->
<context:component-scan base-package="com.itheima">
</context:component-scan>
  • 到Service层
@Service(value = "accountService")
public class AccountServiceImpl implements AccountService {
    //切入点
    public void save() {
        System.out.println("save...");
    }
 }
  • 开启AOP注解扫描器
<!--开启AOP扫描类-->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
    • 找切面类
    • 用注解的方式配置前置,后置异常等.
//切面类---里面都是增强方法(通知)
@Component(value = "myaspect")
@Aspect//声明当前类为切面类
public class MyAspect {
    /**
     * 使用注解的方式把表达式抽取出来
     *
     * 要求:
     *  1.需要有一个无参无返回值无内容的方法
     *  2.在该方法上添加注解@Pointcut
     *  3.使用:谁用谁调用方法名
     */
/*
* Pointcut:定义为公共的切入点,配置到空方法上,value:切入点表达式
* 引用:方法名
* */
    @Pointcut(value = "execution(* com.itheima..AccountServiceImpl.save(..))")
    public void aaa(){
    }
    //注解的方式前置通知
    //@Before(value = "execution(* com.itheima..AccountServiceImpl.save(..))")
   // @Before(value = "aaa()")
    public void before() {
        System.out.println("前置通知。。");
    }
     //环绕通知.
  //  @Around(value = "aaa()")
    @Around(value = "execution(* com.itheima..AccountServiceImpl.save(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) {

        try {
            //前置通知
            System.out.println("前置通知。。");
            //原方法执行
            proceedingJoinPoint.proceed();//invoke
            //后置通知
            System.out.println("后置通知...");
        } catch (Throwable throwable) {
            //异常通知
            System.out.println("异常通知....");
        } finally {
            //最终通知
            System.out.println("最终通知");
        }
    }
}
  1. 执行顺序:
  • 测试层
@RunWith(value = SpringJUnit4ClassRunner.class)  
//声明spring提供的单元测试环境
@ContextConfiguration(locations = "classpath:bean.xml") 
//声明spring的配置信息+
public class SpringJunit {
    @Autowired  //默认按照 类型(接口)从容器中查找对象并注入
                 //如果该容器中有多个该接口的对象
    private AccountService accountService;
    @Test
    public void t1(){
        accountService.save();
    }
}

  • 加载 配置文件,然后按照第4步执行。

3.4 纯注解的方式

  • @EnableAspectJAutoProxy : 开启对AOP注解的支持
  1. 不同点:
  • 在测试类中,加载配置类信息。
@RunWith(value = SpringJUnit4ClassRunner.class) 
 //声明spring提供的单元测试环境
@ContextConfiguration(classes = SpringConfig.class)
 //声明spring的配置信息+
public class SpringJunit {
    @Autowired  //默认按照 类型(接口)从容器中查找对象并注入
                 //如果该容器中有多个该接口的对象
    private AccountService accountService;
    @Test
    public void t1(){
        accountService.save();
    }
}
  • 用配置类代替配置文件bean.xml
    • 在配置类中开启ioc,aop扫描
@ComponentScan(basePackages = "com.itheima")//开启ioc扫描类
@EnableAspectJAutoProxy//开启aop扫描类
public class SpringConfig {
}

其他地方与半xml半注解方式一致。

猜你喜欢

转载自blog.csdn.net/qq_33852347/article/details/85197253
今日推荐