Spring-AOP编程

Spring-AOP编程

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

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

1.1、问题
  • 在JavaEE分层开发中,哪个层次对于我们来说是最重要的。
Dao --------->Service---------->Controller
JavaEE分层开发中,Service是最重要的
  • Service层中包含了哪些代码?
Service层中 = 核⼼功能(⼏⼗⾏ 上百代码) + 额外功能(附加功能)
# 1. 核⼼功能
     业务运算
     DAO调⽤
# 2. 额外功能
     1. 不属于业务
     2. 可有可⽆
     3. 代码量很⼩
 
 事务、⽇志、性能...
  • 额外功能书写在Service层中好不好?
Service层的调⽤者的⻆度(Controller):需要在Service层书写额外功能。
						 软件设计者:Service层不需要额外功能

2、代理设计模式

1.1、概念
通过代理类,为原始类(⽬标)增加额外的功能
	好处:利于原始类(⽬标)的维护
1.2、名词解释
1. ⽬标类 原始类
 	 指的是 业务类 (核⼼功能 --> 业务运算 DAO调⽤)
2. ⽬标⽅法,原始⽅法
	 ⽬标类(原始类)中的⽅法 就是⽬标⽅法(原始⽅法)
3. 额外功能 (附加功能)
 	 ⽇志,事务,性能
1.3 代理开发的核⼼要素
代理类 = ⽬标类(原始类) + 额外功能 + 原始类(⽬标类)实现相同的接⼝
房东 ---> public interface UserService{
     m1
     m2
 }
 UserServiceImpl implements UserService{
	 m1 ---> 业务运算 DAO调⽤
	 m2
 }
 UserServiceProxy implements UserService
     m1
     m2
1.4 编码

静态代理:为每⼀个原始类,⼿⼯编写⼀个代理类 (.java .class)
在这里插入图片描述

1.5 静态代理存在的问题
1. 静态类⽂件数量过多,不利于项⽬管理
     UserServiceImpl UserServiceProxy
     OrderServiceImpl OrderServiceProxy
2. 额外功能维护性差
	 代理类中 额外功能修改复杂(麻烦)

第二章、Spring的动态代理

1、Spring动态代理的概念
概念:通过代理类为原始类(目标类)增加额外功能
好处:利于原始类(目标类的维护)
2、搭建开发环境
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.1.14.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.8</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.3</version>
</dependency>
3、Spring动态代理的开发步骤
  • 1.1、创建原始类
public class UserServiceImpl implements UserService {
    @Override
    public void register(User user) {
        System.out.println("UserServiceImpl.register 业务处理");
    }

    @Override
    public boolean login(User user) {
        System.out.println("UserServiceImpl.login");
        return false;
    }

}
  • 1.2、配置文件注册
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">



    <bean id="userService" class="com.xiaohe.proxy.dynamicProxy.jdk.UserServiceImpl"/>
    
    <bean id="before" class="com.xiaohe.proxy.dynamicProxy.before.Before"/>
</beans>
  • 1.3、额外功能

  • MethodBeforeAdvice接口

额外的功能书写在接⼝的实现中,运⾏在原始⽅法执⾏之前运⾏额外功能。
public class Before implements MethodBeforeAdvice {
    /**
     * 作用:需要把运行在原始方法执行之前运行的额外功能,书写在Before方法中
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("--------- method before advice ----------");
    }
}
  • 1.4、定义切入点
# 切入点: 额外功能加入的位置
目的: 由程序员根据自己需要,决定额外功能加入给哪个原始方法
<aop:config>
    <!--定义切⼊点 所有的方法都作为切入点 加入额外功能-->
    <aop:pointcut id="pc" expression="execution(* *(..))"/>
</aop:config>
  • 1.5、组装切面
<!--组装:将额外功能个切入点整合-->
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
  • 1.6、调用
⽬的:获得Spring⼯⼚创建的动态代理对象,并进⾏调⽤
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
注意:
     1. Spring的⼯⼚通过原始对象的id值获得的是代理对象
     2. 获得代理对象后,可以通过声明接⼝类型,进⾏对象的存储
 
UserService userService=(UserService)ctx.getBean("userService");
userService.login("")
userService.register()
4、动态代理细节分析
  • 1、 Spring创建的动态代理类在哪⾥?
Spring框架在运⾏时,通过动态字节码技术,在JVM创建的,运⾏在JVM内部,等程序结束后,会和JVM⼀起消失
# 什么叫动态字节码技术:
	通过第三个动态字节码框架,在JVM中创建对应类的字节码,进⽽创建对象,当虚拟机结束,动态字节码跟着消失。
结论:动态代理不需要定义类⽂件,都是JVM运⾏过程中动态创建的,所以不会造成静态
代理,类⽂件数量过多,影响项⽬管理的问题。

在这里插入图片描述

  • 2、 动态代理编程简化代理的开发
在额外功能不改变的前提下,创建其他⽬标类(原始类)的代理对象时,只需要指定原始(⽬标)对象即可。

第三章、Spring动态代理详解

  • Spring Aop开发的四大步骤
1、原始对象
2、额外功能
3、切入点
4、组装

1、额外功能详解

  • MethodBeforeAdvice
public class Before implements MethodBeforeAdvice {
    /**
     * 作用:需要把运行在原始方法执行之前运行的额外功能,书写在Before方法中
     * method:额外功能所增加的给那个原始方法
     * args:额外功能增加给的那个方法的参数
     * target:原始对象
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("--------- method before advice ----------");
    }
}

  • MethodInterceptor(方法拦截器) 额外功能运行在原始对象之前、之后、异常
MethodBeforeAdvice---------------》原始方法之前
MethodInterceptor-------------------》原始方法之前、之后都行
 public class Arround implements MethodInterceptor {

        /**
     * invoke 方法的作用:额外功能书写在invoke
     * 额外功能 之前
     *        之后
     *        前后
     *MethodInvocation:额外功能增加给的那个原始方法
     */

        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            System.out.println("---------------- log -----------------");
            Object ret = methodInvocation.proceed();
            return ret;
        }
 }

2、切入点详解

切入点决定额外功能加入位置(方法)

<!--定义切⼊点 所有的方法都作为切入点 加入额外功能-->
<aop:pointcut id="pc" expression="execution(* *(..))"/>
execution(* *(..))  ----------->匹配所有的方法
execution()切入点函数
* *(..) 切入点表达式
2.1、切入点表达式

方法切入点表达式

* *(..)  匹配所有的方法
* -------------->修饰符 返回值
* --------------->方法名
()---------------->参数表
.. ---------------->对于参数没有要求(参数有没有,有几个都行)
  • 定义login方法作为切入点
# 定义login作为切入点
* login(..)

# 定义register作为切入点
* register(..)
  • 定义Login方法有两个字符串类型的参数作为切入点
* login(String,String)

精准方法切入点限定

在这里插入图片描述

类切入点

* com.xiaohe.proxy.UserService.*(..)
2.2、切入点函数
切⼊点函数:⽤于执⾏切⼊点表达式
  • execution
最为重要的切⼊点函数,功能最全。
执⾏ ⽅法切⼊点表达式 类切⼊点表达式 包切⼊点表达式
弊端:execution执⾏切⼊点表达式 ,书写麻烦
	 execution(* com.xiaohe.proxy..*.*(..))
 
注意:其他的切⼊点函数 简化是execution书写复杂度,功能上完全⼀致
  • args
作⽤:主要⽤于函数(⽅法) 参数的匹配
切⼊点:⽅法参数必须得是2个字符串类型的参数
execution(* *(String,String))
args(String,String)
  • within
作⽤:主要⽤于进⾏类、包切⼊点表达式的匹配
切⼊点:UserServiceImpl这个类
execution(* *..UserServiceImpl.*(..))
within(*..UserServiceImpl)
execution(* com.xiaohe.proxy..*.*(..))
within(com.xiaohe.proxy..*)
  • @annotation
作⽤:为具有特殊注解的⽅法加⼊额外功能
<aop:pointcut id="" expression="@annotation(com.xiaohe.Log)"/>

第四章、AOP编程

1、AOP概念

AOP (Aspect Oriented Programing) ⾯向切⾯编程 = Spring动态代理开发
	以切⾯为基本单位的程序开发,通过切⾯间的彼此协同,相互调⽤,完成程序的构建
	切⾯ = 切⼊点 + 额外功能
OOP (Object Oritened Programing) ⾯向对象编程 Java
	以对象为基本单位的程序开发,通过对象间的彼此协同,相互调⽤,完成程序的构建
POP (Producer Oriented Programing) ⾯向过程(⽅法、函数)编程 C
	以过程为基本单位的程序开发,通过过程间的彼此协同,相互调⽤,完成程序的构建
AOP的概念:
     本质就是Spring得动态代理开发,通过代理类为原始类增加额外功能。
     好处:利于原始类的维护
注意:AOP编程不可能取代OOP,OOP编程有意补充。

2、AOP编程的开发步骤

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

3、切⾯的名词解释

切⾯ = 切⼊点 + 额外功能
⼏何学
 ⾯ = 点 + 相同的性质

第五章、AOP底层实现

1、核心问题

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

2、动态代理的创建

2.1、JDK动态代理
  • Proxy.newInstance()方法详解

在这里插入图片描述

  • 编码
public class TestJdk {
    public static void main(String[] args) {
        //原始对象
        final UserService userService = new UserServiceImpl();

        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestJdk.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("-------------- proxy log ------------------");
                //原始功能执行
                Object ret = method.invoke(userService, args);
                return ret;
            }
        });

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

在这里插入图片描述

  • 编码
public class TestCglib {
    public static void main(String[] args) {
        //1、原始类
        final UserService userService = new UserService();

        /**
         * 通过cglib方式创建对象
         */
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setClassLoader(TestCglib.class.getClassLoader());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("-------- log --------");
                Object ret = method.invoke(userService, args);
                return ret;
            }
        });
        UserService userServiceProxy = (UserService) enhancer.create();
        userServiceProxy.login("zhangsan","123");
    }
}
#  JDK动态代理 Proxy.newProxyInstance() 通过接⼝创建代理的实现类
#  Cglib动态代理 Enhancer 通过继承⽗类创建的代理类
  • Spring工厂对代理的加工
public class ProxyPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }


    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {

        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object ret = method.invoke(bean, args);
                return ret;
            }
        };

        return Proxy.newProxyInstance(ProxyPostProcessor.class.getClassLoader(),new Class[]{UserService.class},handler);
    }
}

第六章、基于注解的AOP编程

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

  • 1、原始对象
  • 2、额外功能
  • 3、切入点
  • 4、组装切面
@Aspect //声明为切面类
public class MyAspect {


    /**
     * 切入点
     */
    @Around("execution(* com.xiaohe.aspect.UserServiceImpl.*(..))")//切入点
    public Object arround(ProceedingJoinPoint joinPoint){//ProceedingJoinPoint joinPoint 额外功能
        Object proceed = null;
        try {
            System.out.println("--------------- log ------------");
            proceed = joinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return proceed;
    }
}
  • 配置文件配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd 				                   http://www.springframework.org/schema/context                  https://www.springframework.org/schema/context/spring-context.xsd    http://www.springframework.org/schema/aop 
                           https://www.springframework.org/schema/aop/spring-aop.xsd">



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

    <!--
      切面:
            1、额外功能
            2、切入点
            3、组装切面
    -->
    <bean id="aspect" class="com.xiaohe.aspect.MyAspect"/>

    <!--告诉Spring基于注解进行AOP编程-->
    <aop:aspectj-autoproxy/>

</beans>

2、切入点复用

//切⼊点复⽤:在切⾯类中定义⼀个函数 上⾯@Pointcut注解 通过这种⽅式,定义切⼊点表达式,后续更加有利于切⼊点复⽤。
@Aspect //声明为切面类
public class MyAspect {


    /**
     * 切入点
     */
    @Pointcut("execution(* com.xiaohe.aspect.UserServiceImpl.*(..))")
    public void pointCut(){}

    
    @Around(value = "pointCut()")//切入点
    public Object arround(ProceedingJoinPoint joinPoint){
        Object proceed = null;
        try {
            System.out.println("--------------- log ------------");
            proceed = joinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return proceed;
    }
}

3、动态代理的创建⽅式

AOP底层实现 2种代理创建⽅式
    1. JDK 通过实现接⼝ 做新的实现类⽅式 创建代理对象
    2. Cglib通过继承⽗类 做新的⼦类 创建代理对象
默认情况 AOP编程 底层应⽤JDK动态代理创建⽅式
如果切换Cglib

 1. 基于注解AOP开发
 <aop:aspectj-autoproxy proxy-target-class="true" />
 2. 传统的AOP开发
 <aop:config proxy-target-class="true">
 </aop>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值