Spring AOP 编程

1. 静态代理设计模式(手动)

为什么需要代理设计模式

JavaEE分层开发中,最为重要的是Service层

Service层中包含了哪些代码?

Service层中 = 核⼼功能(⼏⼗⾏ 上百代 码) + 额外功能(附加功能)

  1. 核⼼功能

    • 业务运算
    • DAO调⽤
  2. 额外功能 不属于业务 可有可⽆ 代码量很⼩

    • 事务、⽇志、性能
额外功能书写在Service层中好不好?

Service层的调⽤者的⻆度(Controller):需要在Service层书写额外功能。

软件设计者:Service 层不需要额外功能

解决方式:引入一个代理(中介)类,完成额外功能,并调用目标类(原始类)的核心功能

代理设计模式

通过代理类,为原始类(⽬标)增加额外的功能

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

⽬标类 原始类

指的是 业务类 (核⼼功能 --业务运算 DAO调⽤)

⽬标⽅法,原始⽅法

⽬标类(原始类)中的⽅法 就是⽬标⽅法 (原始⽅法)

额外功能 (附加功能)

⽇志,事务,性能

代理开发的核⼼要素

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

/**
 * 代理类
 * 1. 与被代理类实现相同接口
 * 2. 必须原始对象
 * 3. 实现额外功能
 *
 * @author duniqb <duniqb@qq.com>
 * @version v1.0.0
 * @date 2020/7/1 10:52
 * @since 1.8
 */
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);
    }
}

静态代理存在的问题

静态类⽂件数量过多,不利于项⽬管理

UserServiceImpl

UserServiceProxy

额外功能维护性差

代理类中 额外功能修改复杂(麻烦)

2. Spring的动态代理开发

Spring动态代理的概念

概念:通过代理类为原始类(⽬标类)增加额外功能(与静态代理一样)

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

步骤

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="cn.duniqb.aop.proxy.UserServiceImpl"/>
2. 额外功能 MethodBeforeAdvice接⼝

额外的功能书写在接⼝的实现中,运⾏在原始⽅法执⾏之前运⾏额外功能。

public class Before implements MethodBeforeAdvice {
    /**
     * 作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----method before advice log------");
    }
}
<bean id="before" class="cn.duniqb.aop.proxy.dynamic.Before"/>
3. 定义切⼊点

切⼊点:额外功能加⼊的位置

⽬的:由程序员根据⾃⼰的需要,决定额外功能加⼊给那个原始⽅法

<aop:config proxy-target-class="true">
    <!-- 所有的方法,都作为切入点 -->
    <aop:pointcut id="all" expression="execution(* *(..))"/>
</aop:config>
4. 组装 (2 3整合)

表达的含义:所有的⽅法 都加⼊ before的额外功能

<aop:config proxy-target-class="true">
    <!-- 所有的方法,都作为切入点,加入额外功能 login register -->
    <aop:pointcut id="all" expression="execution(* *(..))"/>
    <!-- 组装:目的把切入点与额外功能进行整合 -->
    <aop:advisor advice-ref="before" pointcut-ref="all"/>
</aop:config>
5. 调⽤

⽬的:获得Spring⼯⼚创建的动态代理对象,并进⾏调⽤

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

注意:

  1. Spring的⼯⼚通过原始对象的 id值获得的是代理对象

  2. 获得代理对象后,可以通过声明接⼝类型,进⾏对象的存储

UserService userService = (UserService) ctx.getBean("user Service");
userService.login("") 
userService.register()

动态代理细节分析

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

Spring框架在运⾏时,通过动态字节码技术,在JVM创建的,运⾏在JVM内部,等程序结束后,会和JVM⼀起消失

什么叫动态字节码技术

通过第三方动态字节码框架,在JVM中创建对应类的字节码,进⽽创建对象,当虚拟机结束,动态字节码跟着消失。

结论

动态代理不需要定义类⽂件,都是JVM运⾏过程中动态创建的,所以不会造成静态代理,类⽂件数量过多,影响项⽬管理的问题。

动态代理编程简化代理的开发

在额外功能不改变的前提下,创建其他⽬标类(原始类)的代理对象时,只需要指定原始(⽬标)对象即可。

动态代理额外功能的维护性⼤⼤增强

3. Spring动态代理详解

额外功能的详解

MethodBeforeAdvice分析

MethodBeforeAdvice接⼝作⽤:额外功能运⾏在原始⽅法执⾏之前,进⾏额外功能操作。

public class Before1 implements MethodBeforeAdvice {
    /**
     * 需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
     *
     * @param method 额外功能所增加给的那个原始方法 login方法
     * @param args   额外功能所增加给的那个原始方法的参数。String name,String password,User
     * @param target 额外功能所增加给的那个原始对象  UserServiceImpl
     */
    @Override
    public void before(Method method, Object[] args, Object target) {
        System.out.println("-----new method before advice log------");
    }
}
MethodInterceptor(⽅法拦截器)

MethodInterceptor 接⼝:额外功能可以根据需要运⾏在原始⽅法执⾏ 前、后、前后。

public class Around implements MethodInterceptor {
    /**
     * 作用:额外功能书写在invoke
     * 额外功能  原始方法之前
     * 原始方法之后
     * 原始方法执行之前 之后
     *
     * @param invocation 额外功能所增加给的那个原始方法
     * @return 原始方法的返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("------log-----");
        return invocation.proceed();
    }
}

4. AOP 编程

概念

AOP (Aspect Oriented Programing)

⾯向切⾯编程 = Spring动态代理开发

以切⾯为基本单位的程序开发,通过切⾯间的彼此协同,相互调⽤,完成程序的构建

切⾯ = 切⼊点 + 额外功能

OOP (Object Oritened Programing)

⾯向对象编程 Java

以对象为基本单位的程序开发,通过对象间的彼此协同,相互调⽤,完成程序的构建

POP (Producer Oriented Programing)

⾯向过程(⽅法、函数)编程 C

以过程为基本单位的程序开发,通过过程间的彼此协同,相互调⽤,完成程序的构建

AOP的概念:

本质就是Spring得动态代理开发,通过代理类为原始类增加额外功能。

好处:利于原始类的维护

注意:AOP编程不可能取代OOP,OOP编程有意补充。

AOP编程的开发步骤

  1. 原始对象

  2. 额外功能 (MethodInterceptor)

  3. 切⼊点

  4. 组装切⾯ (额外功能+切⼊点)

切⾯的名词解释

切⾯ = 切⼊点 + 额外功能

⼏何学 ⾯ = 点 + 相同的性质

5. AOP的底层实现原理

核⼼问题

AOP如何创建动态代理类

动态字节码技术

Spring⼯⼚如何加⼯创建代理对象

通过原始对象的id值,获得的是代理对象

动态代理类的创建

JDK的动态代理
Proxy.newProxyInstance(classloader, interfaces, invocationHandler);

代理创建三要素:

  1. 原始对象

    classloader,借用一个类加载器,创建代理类的Class 对象,进而可以创建代理对象

  2. 额外功能

    invocationHandler

  3. interfaces

    代理对象和原始对象实现相同的接口

    userService.getClass().getInterfaces();
    
public static void main(String[] args) {
    UserService userService = new UserServiceImpl();
    InvocationHandler handler = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("------proxy  log --------");
            // 原始方法运行
            return method.invoke(userService, args);
        }
    };

    // 借用类加载器加载原始对象, 代理对象和原始对象实现相同的接口, 额外功能
    UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
    userServiceProxy.login("duniqb", "123456");
    userServiceProxy.register(new User());
}
CGlib的动态代理

CGlib创建动态代理的原理:⽗⼦继承关系创建代理对象,原始类作为⽗类,代理类作为⼦类,这样既可以保证l两者⽅法⼀致,同时在代理类中提供新的实现(额外功能+原始⽅法)

public class TestCglib {
    public static void main(String[] args) {
        // 创建原始对象
        UserService userService = new UserService();

        // 通过cglib方式创建动态代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(TestCglib.class.getClassLoader());
        enhancer.setSuperclass(userService.getClass());

        MethodInterceptor interceptor = new MethodInterceptor() {
            // 等同于 InvocationHandler --- invoke
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("---cglib log----");

                return method.invoke(userService, args);
            }
        };

        enhancer.setCallback(interceptor);

        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login("suns", "123345");
        userServiceProxy.register(new User());
    }
}

动态代理总结

  1. JDK 动态代理

    Proxy.newProxyInstance() 通过接⼝ 创建代理的实现类

  2. Cglib 动态代理

    Enhancer 通过继承⽗类创建的代理类

Spring⼯⼚如何加⼯原始对象

6. 基于注解的AOP编程

基于注解的AOP编程的开发步骤

原始对象

额外功能

切⼊点

组装切⾯

posted @ 2020-07-09 17:06  duniqb  阅读(156)  评论(0编辑  收藏  举报