spring-AOP编程

spring-AOP编程

第一章、静态代理设计模式

1.为什么需要代理模式设计

1.1问题

  • JavaEE层次中哪层最重要

    Dao--->Service--->Controller
    
    Service 最重要
    
  • Service 层中包含了哪些代码?

    • Service层 = 核心功能(几十上百行代码) +额外功能(附加功能)

      • 核心功能:业务运算、DAO调用(满足用户需求)
      • 额外功能:不属于业务、可有可无、代码量很小(如事务、日志(记录谁+时间+什么事+结果、性能、…)

      spring-AOP编程_第1张图片

    • 额外功能书写在Service层中好不好?

      Service 层的调用者(Controller)的角度:需要在Service层书写额外功能(事务,保证业务完整性)
      	               软件设计者:Service层不需要额外功能(不好维护,解耦合)
      //矛盾
      
      • 现实中的问题和解决办法:

        租房场景:

      spring-AOP编程_第2张图片

        租房场景改造:
      

spring-AOP编程_第3张图片

        改变代码量较小的额外功能只要添加新的代理类即可,代码量多的核心功能不用改变.

2.代理设计模式

2.1概念

  • 通过代理类,为原始类添加额外的功能
  • 好处:利于目标类维护

2.2名词解释

  1. 目标类 原始类

    指的是 业务类(核心功能—→ 业务运算 Dao调用)

  2. 目标方法,原始方法

    指的是 业务类中的方法(核心功能—→ 业务运算 Dao调用)

  3. 额外功能(附加方法)

    日志,事务,性能

2.3代理开发的核心要素

  • 代理类 = 目标类(原始类) + 额外功能 实现相同的接口

    spring-AOP编程_第4张图片

2.4编码

静态代理:为每一个原始类,手动创建一个代理类(.java .class)

//代理类实现与原始类相同的接口,保证方法一致
public class UserServiceProxy implements UserService {
//原始类
private UserServiceImpl userService = new UserServiceImpl();
@Override
public void register(User user) {
    //额外功能
    System.out.println("-----log-----");
    //调用原始类中的方法
    userService.register(user);
}

@Override
public boolean login(String name, String password) {
    //额外功能
    System.out.println("-----log-----");
    //调用原始类中的方法
    return userService.login(name, password);
}
//代理类要实现原始类相同的接口UserService
//与装饰者模式区别:当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模 式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

2.5静态代理

对于每个Service原始类都需要一个对应的代理类,代码冗余。

  • 静态代理存在的问题

    代理类文件数量过多,不利于项目的管理。
    UserServiceImpl --> UserServiceProxy
    OrderServiceImpl --> OrderServiceProxy

    额外功能维护性差
    代理类中,额外功能修改复杂(麻烦)。一百个就需要修改一百次。

第二章、Spring动态代理

1.Spring动态代理的概念

概念:通过代理类,为原始类(目标)增加额外的功能。

好处:利于原始类(目标)的维护。

2.搭建开发环境

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-aopartifactId>
    <version>5.1.14.RELEASEversion>
dependency>
<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjrtartifactId>
    <version>1.8.9version>
dependency>
<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.8.9version>
    <scope>runtimescope>
dependency>
	

3.开发步骤

  1. 创建原始对象

    public class UserServiceImpl implements UserService {
        @Override
        public void register(User user) {
            //核心功能
            System.out.println("UserServiceImpl.register (业务运算+DAO调用)");
        }
    
        @Override
        public boolean login(String name, String password) {
            //核心功能
            System.out.println("UserServiceImpl.login");
            return true;
        }
    }
    
    
    <bean id="userService" class="com.angenin.proxy.UserServiceImpl"/>
    
  2. 额外功能

    Spring提供了MethodBeforeAdvice接口,我们需要把额外功能书写在此接口的实现类的before方法中,这个实现类的before方法会在原始方法执行前先执行。

    public class Before implements MethodBeforeAdvice {
        //书写运行在原始方法之前的额外功能
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            //额外功能
            System.out.println("Before...before...");
        }
    }
    
    
    <bean id="before" class="com.angenin.dynamic.Before"/>
    
  3. 定义切入点

    • 切入点:额外功能加入的位置。

    • 目的:由程序员根据自己的需要,决定额外功能加入给哪个原始方法。

    • 简单的测试:所有方法都作为切入点,都加入额外功能。

      
      <aop:config>
          
          <aop:pointcut id="pc" expression="execution(* *(..))"/>
      aop:config>
      
      
  4. 组装(2,3)

    <aop:config>
        
        <aop:pointcut id="pc" expression="execution(* *(..))"/>
        
        
        <aop:advisor advice-ref="before" pointcut-ref="pc"/>
    aop:config>
    
    
  5. 调用

获得Spring工厂创建的动态代理对象,并进行调用。

ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
UserService userservice = (UserService)ctx.getBean("userService");
userService.register(new User);
userService.login("aa","bbb");

注意:

  1. 此时,Spring工厂通过原始对象的id值获得的就是代理对象
  2. 生成的对象用原始类的接口类型进行接收。

userService userservice = (UserService)ctx.getBean(“userService”);

spring-AOP编程_第5张图片

打断点debug后可以看到获得的userService对象为代理对象。

spring-AOP编程_第6张图片

4.细节分析

4.1 Spring创建的动态代理类在哪?

Spring框架在运行时,通过动态字节码技术,在JVM创建的,运行在JVM内部,等程序结束后,会和JVM一起消失。动态代理不需要定义类文件,都是JⅥM运行过程中动态创建的,所以不会造成静态代理,类文件数量过多,影晌项目管理的问题。就解决了静态代理的问题

4.2什么是动态字节码技术?

  • 原来:Java运行一个类,JVM就要运行这个类的字节码来生成对象,.java文件编译成.class文件.clss文件里面是字节码
  • 动态字节码技术:通过第三方动态字节码框架,在JVM中创建对应类的字节码,进而创建对象,当虛拟机结束,动态字节码跟着消失。不用.java和.class的步骤
    spring-AOP编程_第7张图片

4.3.动态代理编程简化代理开发

只要额外功能不变,创建其他原始类的对象时,则只需要创建原始类

4.4 额外功能维护性大大增强

想要修改额外功能,增加一个额外功能的类,将配置修改即可,不用改原来的代码.

第三章、Spring动态代理详解

3.1额外功能的详解

3.3.1MethodBeforeAdvice分析

public class Before implements MethodBeforeAdvice {
    /*
        作用:书写运行在原始方法之前的额外功能

        参数:
            Method:额外功能所增加给的那个原始方法(如:login方法、register方法)
            Object[]:额外功能所增加给的那个原始方法的参数(如login方法的String name, String password)
            Object:额外功能所增加给的那个原始对象(如UserServiceImpl)
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        //额外功能
        System.out.println("Before...before...");
    }
}

  • before方法的3个参数在实战中,该如何使用?
    before方法的参数,在实战中,会根据需要进行使用,不一定都会用到,也有可能都不用,实战开发中before的三个参数用得比较少。

3.3.2MethodInterceptor(方法拦截器)

  • MethodBeforeAdvice --> 运行在原始方法之前
  • MethodInterceptor接口:额外功能可以根据需要运行在原始方法执行前、后、前后以及原始方法执行出现异常时执行,MethodInterceptor接口功能比MethodBeforeAdvice接口更加强大,所以更推荐使用MethodInterceptor接口。
//这里的MethodInterceptor是aop包下的那个
public class Around implements MethodInterceptor {
/*
invoke方法的作用:额外功能书写在invoke
额外功能执行的时机   1. 原始方法之前
2. 原始方法之后
3. 原始方法之前 和 之后
4. 原始方法执行异常时
调用参数methodInvocation的proceed()方法,可以让原始方法执行,所以我们可以在执行前写额外功能,也可以在之后写,或者都写
参数:
methodInvocation:额外功能所增加给的那个原始方法
返回值:
Object:原始方法执行后的返回值
什么的额外功能需要在原始方法运行的前后都运行呢?事务等。

*/
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//额外功能1
System.out.println("额外功能在原始方法执行前执行-------");
//原始方法
Object ret = methodInvocation.proceed();
//额外功能2
[//System.out.println](https://system.out.println/)("额外功能在原始方法执行后执行-------");
//返回的是原始方法执行后的返回值
return ret;

<bean id="around" class="com.angenin.dynamic.Around"/>

[aop:config](aop:config)

<aop:pointcut id="pc" expression="execution(* *(..))"/>

<aop:advisor advice-ref="around" pointcut-ref="pc"/>
aop:config>

  • 额外功能运行在原始方法抛出异常时
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
//额外功能1
[//System.out.println](https://system.out.println/)("额外功能在原始方法执行前执行-------");
Object ret = null;
try {
//原始方法
ret = methodInvocation.proceed();
} catch (Throwable throwable) {
System.out.println("额外功能在原始方法抛出异常时执行-------");
throwable.printStackTrace();
}
//额外功能2
[//System.out.println](https://system.out.println/)("额外功能在原始方法执行后执行-------");
return ret;
}

  • MethodInterceptor影响原始方法的返回值

    原始方法的返回值,直接作为invoke方法的返回值返回,MethodInterceptor不会影响原始方法的返回值。
    不返回原始方法的返回值,就会影响到结果。

@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Object ret = methodInvocation.proceed();
//return 1;
return false;
}

3.2切入点详解

切入点决定额外功能加入的位置(这里的位置指的是方法,切入哪些方法)。

<aop:pointcut id="pc"expression="execution(* *(..))"/>
execution(* *(..)):匹配了所有方法
  1. execution(): 切入点函数
    • *(…): 切入点表达式

3.2.1方法切入点表达式

spring-AOP编程_第8张图片

  • *(…) --> 所有方法

  • -> 修饰符 返回值

*-> 方法名
() --> 参数表
… --> 对参数没有要求(参数有没有、有几个、什么类型都行)

  • 定义名为login的方法作为切入点:* login(…)
  • 定义名为login并且有两个字符串类型参数的方法作为切入点:* login(String,String)
    • 注意:
      1. 非java.lang包中的类型,必须写全限定类名
        如:* register(com.angenin.proxy.User)
      2. …可以和具体的参数类型连用
        如:* login(String,…)
        第一个参数为String类型,后面无要求的login方法
  • 精准方法切入点限定
    上面所讲解的方法切入点表达式不精准。
    如下图,不能精准到a包下的UserServiceImpl的第一个login方法。

spring-AOP编程_第9张图片
- 精准切入
spring-AOP编程_第10张图片

```java
//修饰符/返回值		包.类.方法(参数)
*			com.baizhiedu.a.UserServiceImpl.login(String,String)
//精准到a包下的UserServiceImpl的第一个login方法
```

3.2.2. 类切入点表达式

指定特定类作为切入点(额外功能加入的位置),自然这个类中的所有方法,都会加上对应的额外功能。
spring-AOP编程_第11张图片

  • 语法一
    a包下UserServiceImpl类中的所有方法都加入额外功能: *
* com.baizhiedu.a.UserServiceImpl.*(..)

  • 语法二
    忽略包
    1. 类只存在一级包 比如:com.UserServiceImpl

      * **.UserServiceImpl.**(..)
      
    2. 类存在多级包 com.baizhiedu.a.UserServiceImpl

      * **..UserServiceImpl.**(..)
      

3.2.3包切入点表达式(实战

指定包作为额外功能加入的位置,自然包中的所有类及其方法都会加入额外的功能。

  • 语法一
    切入点包中的所有类,下面这条表达式包括的类必须在proxy包中,即使在proxy的子包中也无法生效。
* com.baizhiedu.proxy.**.**(..)
//切入点包中的所有类,必须在proxy中,不能在proxy包的子包中
  • 语法二
    切入点当前包及其子包都生效(多加一个点)

    * com.baizhiedu.proxy..**.**(..)
    //切入点包中的所有类,必须在proxy中,不能在proxy包的子包中
    

包切入点在实战中使用得更多,把相加额外功能的类放到同个包下即可。

3.3 切入点函数

切入点函数:用于执行切入点表达式。

3.3.1execution

最为重要的切入点函数,功能最全。
可以执行方法切入点表达式、类切入点表达式、包切入点表达式。

  • 弊端:execution执行切入点表达式,书写麻烦。
  • 注意:其他的切入点函数只是简化了execution书写的复杂度,在功能上完全一致。

3.3.2args

作用:主要用于函数(方法)参数的匹配,关注点在于参数,忽略包、类和方法名。
如:切入点为方法参数必须得是2个字符串类型的参数。
execution(* *(String,String))
替换后
args(String,String)

3.3.3within

作用:主要用于进行类、包切入点表达式的匹配,关注点在于包名、类名,忽略方法名和参数。
如:切入点为UserServiceImpl类。
execution(* *..UserServiceImpl.*(..))
替换后
within(*..UserServiceImpl)

如:切入点为proxy包下及其子包的所有类

execution(* com.baizhiedu.proxy..*.*(..))
替换后
within(com.baizhiedu.proxy..*)

3.3.4@annotation

作用:为具有特殊注解的方法加入额外功能。

自定义一个Log注解。
@Target(ElementType.METHOD) //决定在什么位置起作用(这里是在方法上起作用,如果是类上就是TYPE)
@Retention(RetentionPolicy.RUNTIME) //决定在什么时候起作用(这里在运行时起作用)
public @interface Log {}UserServiceImpl类的register方法加上@Log注解。
<[aop:config](aop:config)>
<!--@annotation括号里为注解的全限定类名-->
<aop:pointcut id="pc" expression="@annotation(com.angenin.proxy.Log)"/>
<aop:advisor advice-ref="around" pointcut-ref="pc"/>
</aop:config>

3.3.5切入点函数的逻辑运算

指的是 整合多个切入点函数一起配合工作,进而完成更为复杂的需求。

  • and 与操作(同时成立

    案例:方法名为login,并且参数为2个字符串。

    execution(* login(String,String))
    替换后
    execution(* login(…)) and args(String,String)

    • 注意:与操作不能用于同种类型的切入点函数,因为and需要同时满足。
      如:login方法和register方法都作为切入点(这样写是错误的,可以换成or)
      execution(* login(…)) and execution(* register(…))
  • or 或操作

案例:login方法和register方法都作为切入点

execution(* login(…)) or execution(* register(…))

第四章、AOP开发

4.1AOP的概念

  • AOP(Aspect Oriented Programing)面向切面编程 = Spring动态代理开发
    以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建。
    切面 = 切入点 + 额外功能

  • OOP(Object Oritened Programing)面向对象编程 Java
    以对象为基本单位的程序开发,通过对象间的彼此协同,相互调用,完成程序的构建。

  • POP(Procedure Oriented Programing)面向过程(方法、函数)编程 C语言
    以过程为基本单位的程序开发,通过过程间的彼此协同,相互调用,完成程序的构建。

  • AOP的概念:

    本质就是Spring的动态代理开发,通过代理类为原始类增加额外功能。
    好处:利于原始类的维护。
    注意:AOP是基于OOP的,所以AOP不可能取代OOP,AOP是OOP为了解耦而做的补充。

4.2 AOP的开发步骤

  1. 原始对象
  2. 额外功能(MethodInterceptor)
  3. 切入点
  4. 组装切面(额外功能 + 切入点)

4.3 切面的名词解释

切面 = 切入点 + 额外功能

几何学: 面 = 点 + 相同的性质

spring-AOP编程_第12张图片

第五章、AOP的底层实现原理

1. 核心问题

  1. AOP如何创建动态代理类?(动态字节码技术)
  2. Spring工厂如何加工创建代理对象?为什么通过原始对象的id值,获得的是代理对象?

2. 动态代理类的创建

2.1JDK的动态代理

  1. Proxy.newProxyInstance方法参数详解

  1. 关于类加载器(ClassLoader):
    首先得了解类加载器的作用,及其对象生成的过程。


    spring-AOP编程_第13张图片

    因为代理类没有字节码文件,所以JVM没有给它生成它的类加载器,需要借用其他类的类加载器,所以Proxy.newProxyInstance的第一个参数为其他类的类加载器(原始类或者其他类都可以)。

  2. 代码实现

    public class TestJdkProxy {
    public static void main(String[] args) {
    //代理创建3要素:1.原始对象;2.额外功能;3.代理对象和原始对象实现相同的接口。
    // 1.创建原始对象
        UserServiceImpl userService = new UserServiceImpl();
    
        // 2.JDK创建动态代理
    
        //这里为了下面来起来整洁一点,单独拿开,平时用匿名内部类即可,内部类实现接口直接大括号
        InvocationHandler handler = new InvocationHandler() {
            /*
            InvocationHandler的invoke方法:
                作用:用于书写额外功能,额外功能运行在原始方法执行前、后、前后、抛出异常
                参数:
                    proxy:忽略即可,代表的是代理对象
                    method:原始方法(增加额外功能的那个原始方法)
                    args:原始方法的参数
    
                method.invoke方法:执行原始方法,返回原始方法执行后的结果,
                    第一个参数为原始对象
                    第二个参数为原始方法的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("----proxy  log-----");
                //传入原始对象和原始方法的参数
                //这里需要注意,内部类使用外部类的变量时,外部类的变量需要用final来修饰
                Object ret = method.invoke(userService, args);
                //返回原始方法执行后的结果
                return ret;
            }
        };
        /*
            第一个参数为借用的类加载器(任意类都可以)
            第二个参数为原始类实现的接口,通过获取原始对象的class再获取接口(userService.getClass().getInterfaces())
            第三个参数为InvocationHandler接口,在此接口的invoke方法中书写额外功能
         */
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                                userService.getClass().getInterfaces(), handler);
    
        //因为InvocationHandler接口是一个函数式接口,所以可以使用Lambda表达式,代码变得更加简洁(因为main方法的参数同样为args,所以需要改一下)
    
    //        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
    //                userService.getClass().getInterfaces(), (proxy, method, argss) -> {
    //                    System.out.println("----log-----");
    //                    return method.invoke(userService, argss);
    //                });
        userServiceProxy.login("angenin", "123456");
        userServiceProxy.register(new User());
    }
    }
    

2.2 CGlib的动态代理

CGlib创建动态代理的原理:父子继承创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证二者方法一致,同时在代理类中提供新的实现(额外功能 + 原始方法)。

spring-AOP编程_第14张图片

public class TestCglib {
public static void main(String[] args) {
//原始类只是普通的class而不是接口 
//1.创建原始对象
UserService userService = new UserService();

/*
   2. 通过Cglib方式创建动态代理对象

    JDK动态代理:Proxy.newProxyInstance(classloader,interface,invocationhandler)
    Cglib动态代理:
        设置类加载器: Enhancer.setClassloader()   --> 同样,借用任意一个类的类加载器
        设置父类:    Enhancer.setSuperClass()
        设置额外功能: Enhancer.setCallback()  --> 实现接口MethodInterceptor
        Enhancer.create() ---> 创建代理
 */
Enhancer enhancer = new Enhancer();
//设置类加载器
enhancer.setClassLoader(userService.getClass().getClassLoader());
//设置父类
enhancer.setSuperclass(userService.getClass());
//这里的MethodInterceptor接口是spring的cglib包下的那个
//可以用匿名内部类,这里只是为了看起来简洁
MethodInterceptor interceptor = new MethodInterceptor() {
    //这里的intercept方法 等同于 InvocationHandler的invoke方法
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("------cglib  log------");
        //这里调用method的invoke和前面的JDK动态代理一样
        Object ret = method.invoke(userService, args);
        return ret;
    }
};
//设置额外功能
enhancer.setCallback(interceptor);

//创建代理
UserService userServiceProxy  = (UserService) enhancer.create();
userServiceProxy.login("angenin", "123456");
userServiceProxy.register(new User());

}

2.3. 总结

  1. JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理的实现类
  2. Cglib动态代理 Enhancer 通过继承父类创建的代理类

代理都是为了面向切面服务的。

3. Spring工厂如何加工原始对象

3.1.思路分析

BeanPostProcessor在上一篇文章里讲到。

spring-AOP编程_第15张图片

3.2编码实现:

  • 实现 BeanPostProcessor 进行加工

    public class ProxyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //返回生成的理对象
        return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), (proxy, method, args) -> {
            System.out.println("----new log-----");
            //返回原始类的执行结果
            return method.invoke(bean, args);
        });
    }
    }
    
  • 配置文件中对 BeanPostProcessor 进行配置

    <bean id="userService" class="com.angenin.factory.UserServiceImpl"/>
    <bean id="proxyBeanPostProcessor" class="com.angenin.factory.ProxyBeanPostProcessor"/>
    
    
  • 测试

    public class Test {
    public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext4.xml");
    UserService userService = (UserService) ctx.getBean("userService");
    userService.login("angenin", "123456");
    userService.register(new User());
    }
    }
    
    

第六章、基于注解的AOP开发

1. 基于注解的AOP开发步骤

  1. 原始对象

  2. 额外功能

  3. 切入点

  4. 组装切面

    通过切面类@ASpect 定义 额外功能 @Around

                定义 切入点 @Around(“execution(* login(..))”)
    

1.1 原始对象

public class UserServiceImpl implements UserService {
@Override
public void register(User user) {
    System.out.println("UserServiceImpl.register (业务运算+DAO调用)");
}

@Override
public boolean login(String name, String password) {
    System.out.println("UserServiceImpl.login");
    return true;
}
}

1.2 切面类(额外功能 + 切入点 + 组装)

/*
1. 原版的额外功能
public class MyAround implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) {
return invocation.proceed();
}
}
2. 切入点

*/
@Aspect //加上@Aspect注解代表这个类是切面类
public class MyAspect {
//加上@Around注解后,此时的 around方法 相当于 MethodInterceptor接口的invoke方法
//方法名和任意起,这里的参数 ProceedingJoinPoint 对应 原先的参数 MethodInvocation
@Around("execution(* login(..))")   //对应aop:pointcut标签
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("----log-----");
Object ret = joinPoint.proceed();
return ret;
}
}

<bean id="userService" class="com.angenin.aspect.UserServiceImpl"/>

<bean id="around" class="com.angenin.aspect.MyAspect"/>

<[aop:aspectj-autoproxy/](aop:aspectj-autoproxy/)>

1.3 测试

public class TestAspectProxy {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext4.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login("angenin", "123456");
userService.register(new User());
}
}

2.细节

2.1 切入点复用

好处: 相同切入点加入不同额外功能,避免代码冗余,利于维护.

切入点复用:在切面类中定义一个空方法,在其上面加上@Pointcut注解,通过这种方式定义切入点表达式,后续更加有利于切入点的复用。

@Aspect
public class MyAspect {
//创建空方法,提取出切入点表达式
@Pointcut("execution(* login(..))")
public void myPointcut(){}
@Around(value = "myPointcut()")   //引用空方法的切入点函数
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("----log-----");
    Object ret = joinPoint.proceed();
    return ret;
}

@Around("myPointcut()")   //引用空方法的切入点表达式
public Object around2(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("----tx-----");
    Object ret = joinPoint.proceed();
    return ret;
}
}

2. 动态代理的创建方式

  • AOP底层实现的2种代理创建方式:

    JDK:通过实现接口,做新的实现类方式,创建代理对象。
    Cglib:通过继承父类,做新的子类,创建代理对象。

  • 默认情况下,AOP底层应用JDK动态代理创建的方式。

  • 切换为Cglib:

    1. 基于注解AOP开发
      只需要在aop:aspectj-autoproxy标签中加入proxy-target-class="true"即可。

      
      <aop:aspectj-autoproxy proxy-target-class="true"/>
      
      
    2. 传统的AOP开发
      在aop:config标签上加上proxy-target-class="true"即可,只是加的位置不同而已,属性都是同一个

      <aop:config proxy-target-class="true">
      ...
      aop:config>
      
      

第七章、AOP开发中的一个坑

1.坑:

在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过普通的方式调用,都调用的是原始方法,因为用的是this.XXX,this指代的是当前对象即原始类对)。如果想让内层的方法也调用代理对象的方法,需要实现AppicationContextAware接口,通过其setApplicationContext方法获得工厂对象,进而获得代理对象,调用方法,加入额外功能。

2.解决:

  • 原始类:
 public class UserServiceImpl implements UserService, ApplicationContextAware {
private ApplicationContext ctx;
//通过实现 ApplicationContextAware 接口,通过 setApplicationContext方法 获取项目中的IOC工厂对象
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
@Override
public void register(User user) {
    System.out.println("UserServiceImpl.register (业务运算+DAO调用)");

    // 模拟同一个业务类不同的业务方法间相互调用的情况
    // 此时调用的是原始对象的login,不是代理对象,所以只有核心功能,没有额外功能
    // 但是我们在这里调用的目的是要有额外功能+核心功能的
    // 由于IOC工厂是重量级资源,一个应用最好只创建一个,所以不能在这里再创建一个工厂
    // 实现 ApplicationContextAware 接口,可以获取到项目中的IOC工厂对象
    //this.login("angenin", "11");

    //通过工厂对象创建代理对象,再调用login方法,实现额外功能+核心功能
    UserService userService = (UserService) ctx.getBean("userService");
    userService.login("angenin", "11");
}

@Override
public boolean login(String name, String password) {
    System.out.println("UserServiceImpl.login");
    return true;
}

第八章、AOP阶段知识总结

spring-AOP编程_第16张图片

你可能感兴趣的:(spring,java,代理模式)