代理模式,AOP

spring

  • 代理模式
    • 静态代理
    • 加深理解
    • 动态代理
  • AOP
    • 什么是AOP
    • AOP在Spring中的作用
    • 使用Spring实现AOP

学习网站:b站狂神

代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC很重要】

代理模式的分类:

  • 静态代理
  • 动态代理


    在这里插入图片描述

静态代理

角色分析:

  • 抽象角色:一般会使用接口或抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们会做一些附属操作
  • 客户:访问代理对象的人!

代码步骤:

1.接口

//租房
public interface Rent {

    public void rent();

}

2.真实角色

//房东
public class Host implements Rent{

    public void rent() {
        System.out.println("房东要出租房子!");
    }
}

3.代理角色

public class Proxy implements Rent {

    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    public void rent() {
        seeHouser();
        host.rent();
        heTong();
        fare();
    }

    //看房
    public void seeHouser(){
        System.out.println("中介带顾客看房!");
    }

    //签合同
    public void heTong(){
        System.out.println("签租赁合同!");
    }

    //收中介费
    public void fare(){
        System.out.println("收中介费!");
    }
}

4.客户端访问代理角色

//顾客
public class Client {

    public static void main(String[] args) {

        //直接找房子找不到,我们需要去中介所找房子
//        Host host = new Host();
//        host.rent();

        //找房东,房东要出租房子
        Host host = new Host();

        //代理,中介一般会帮房东出租房子,但是会做一些附属操作
        Proxy proxy = new Proxy(host);

        //不用直接面对房东,直接找中介租房就可以了
        proxy.rent();
    }

}

代理模式的好处:

  • 可以使真实角色的操作更加纯粹!不会去关注一些公共的业务
  • 公共也就是交给代理角色,实现了业务的分工
  • 公共业务发生扩展的,方便集中管理时候

缺点:

  • 一个真实角色就会产生一个代理角色,代码量就会翻量,效率低

加深理解

聊聊AOP
在这里插入图片描述
代码步骤:

1.接口

public interface UserDao {
    public void add();
    public void delete();
    public void update();
    public void query();
}

2.真实角色

//真实对象
public class UserImple implements UserDao {
    public void add() {
        System.out.println("增加了一个用户!");
    }

    public void delete() {
        System.out.println("删除了一个用户!");
    }

    public void update() {
        System.out.println("修改了一个用户!");
    }

    public void query() {
        System.out.println("查询了一个用户!");
    }
}

3.代理角色

public class UserProcy implements UserDao {

    private UserImple userImple;

    public UserImple getUserImple() {
        return userImple;
    }

    public void setUserImple(UserImple userImple) {
        this.userImple = userImple;
    }

    public void add() {
        log("add");
        userImple.add();
    }

    public void delete() {
        log("delete");
        userImple.delete();
    }

    public void update() {
        log("update");
        userImple.update();
    }

    public void query() {
        log("query");
        userImple.query();
    }

    //日志方法
    public void log(String str)
    {
        System.out.println("使用了"+str+"方法");
    }
}

4.客户端访问代理角色

public class Client {
    public static void main(String[] args) {
        UserImple userImple = new UserImple();

        UserProcy userProcy = new UserProcy();

        userProcy.setUserImple(userImple);

        userProcy.add();
        userProcy.delete();
        userProcy.update();
        userProcy.query();
    }     
}

动态代理

  • 动态代理和静态代理角色一样
  • 动态代理类是动态生成的,不是我们写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口----JDK动态代理
    • 基于类:cglib
    • java字节码实现:javasist

需要了解两个类:proxy:代理 InvocationHandler:调用处理程序

InvocationHandler
在这里插入图片描述
.接口

//租房
public interface Rent {

    public void rent();

}

2.真实角色

//房东
public class Host implements Rent {

    public void rent() {
        System.out.println("房东要出租房子!");
    }
}

3.动态代理实现的类

//等会我们会用这个类,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到的代理类
    public Object getProxy(){

        //这段代码是死的,我们只需要改变rent这个参数的值
        Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(), this);

        return o;
    }

    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        seeHouse();
        //动态代理的本质,就是使用反射机制实现
        Object invoke = method.invoke(rent, args);

        fare();

        return invoke;
    }

    public void seeHouse(){
        System.out.println("看房子");
    }

    public void fare(){
        System.out.println("收中介费");
    }
}	

4.客户端访问代理角色

public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();

        //代理角色:现在没有
        ProxyInvocationHandler p = new ProxyInvocationHandler();

        //通过调用程序处理角色来处理我们要调用的接口对象
        p.setRent(host);

        Rent proxy = (Rent) p.getProxy();

        proxy.rent();
    }
}

动态代理的好处:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共也就交给代理角色!实现了业务的分工!
  • 公共业务发生拓展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可

AOP

什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
在这里插入图片描述


AOP在Spring中的作用

Aop在Spring中的作用

提供声明式事务;允许用户自定义切面

以下名词需要了解下:

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

在这里插入图片描述
在这里插入图片描述


使用Spring实现AOP

要导包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

方式一:使用spring API 实现

首先编写我们的业务接口和实现类

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserServiceImple implements UserService {
    public void add() {
        System.out.println("增加了一个用户!");
    }

    public void delete() {
        System.out.println("删除了一个用户!");
    }

    public void update() {
        System.out.println("修改了一个用户!");
    }

    public void query() {
        System.out.println("查询了一个用户!");
    }
}

然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

前置增强:

public class LOg implements MethodBeforeAdvice {

    //method:要执行的目标对象的方法
    //args:参数
    //target:目标对象
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"要执行了!");
    }
}

后置增强:

public class AfterLog implements AfterReturningAdvice {

    //returnValue:返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
    }
}

最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 :

<?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: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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.yunk.service.UserServiceImple"></bean>
    <bean id="log" class="com.yunk.log.LOg"/>
    <bean id="afterLog" class="com.yunk.log.AfterLog"/>

    <!--方式一:使用spring API原生接口-->
    <!--配置AOP:需要导入aop的约束-->
    <aop:config>
        <!--切入点:expression:表达式,execution(要执行的位置! * * * * (..))  (..)代表所有的参数-->
        <aop:pointcut id="pointcut" expression="execution(* com.yunk.service.UserServiceImple.*(..))"/>

        <!--执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

测试:

public class Test {
    public static void main(String[] args) {
        ApplicationContext c =
                new ClassPathXmlApplicationContext("applicationContext.xml");

        //动态代理代理的是接口
        UserService userService = (UserService) c.getBean("userService");

        userService.add();
    }
}

方式二:自定义类实现AOP

去掉前置增强和后置增强,新增一个类。自定义,方法随意。

public class DiyPointCut  {

    public void before()
    {
        System.out.println("方法执行前!");
    }

    public void after(){
        System.out.println("方法执行后!");
    }
}

最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 :

<?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: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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.yunk.service.UserServiceImple"></bean>
    <bean id="log" class="com.yunk.log.LOg"/>
    <bean id="afterLog" class="com.yunk.log.AfterLog"/>

    <!--方式二:自定义类实现AOP-->
    <bean id="diyPointCut" class="com.yunk.diy.DiyPointCut"/>

    <aop:config>

        <!--自定义切面,ref 要引用的类-->
        <aop:aspect ref="diyPointCut">

            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.yunk.service.UserServiceImple.*(..))"/>

            <!--通知-->
            <!--before,在point对应的方法之前执行
                after,在point对应的方法之后执行
                    method对应要执行的方法,从切入面引用的类中来-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
</beans>

方式三:使用注解实现

自定义一个类:

package com.yunk.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

//方式三:使用注解实现AOP

/*在方式二中,我们需要在配置文件中  <!--自定义切面,ref 要引用的类-->
                                 <aop:aspect ref="diyPointCut">
             我们使用注解@Aspect就直接将该类定义为切面类*/

@Aspect
public class AnnotationPointCut {

    @Before("execution(* com.yunk.service.UserServiceImple.*(..))")//切入面,切入方法都有了,差一个切入点
    public void before()
    {
        System.out.println("1.方法执行前!");
    }

    @After("execution(* com.yunk.service.UserServiceImple.*(..))")
    public void after(){
        System.out.println("方法执行后!");
    }

    /* 在环绕增强中,我们可以给定一个参数,代表我们要获取切入的点*/
    @Around("execution(* com.yunk.service.UserServiceImple.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        //执行方法
        //Signature signature = jp.getSignature();//获得签名
        //System.out.println("signature"+signature);
        jp.proceed();
        System.out.println("环绕后");
    }
}

我们还需要在配置文件中写入:

 <!--方式三:使用注解实现AOP-->
    <!--我们还需要配置文件将类注册机进来-->
    <bean id="anno" class="com.yunk.diy.AnnotationPointCut"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了

<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy  poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

热门文章

暂无图片
编程学习 ·

gdb调试c/c++程序使用说明【简明版】

启动命令含参数&#xff1a; gdb --args /home/build/***.exe --zoom 1.3 Tacotron2.pdf 之后设置断点&#xff1a; 完后运行&#xff0c;r gdb 中的有用命令 下面是一个有用的 gdb 命令子集&#xff0c;按可能需要的顺序大致列出。 第一列给出了命令&#xff0c;可选字符括…
暂无图片
编程学习 ·

高斯分布的性质(代码)

多元高斯分布&#xff1a; 一元高斯分布&#xff1a;(将多元高斯分布中的D取值1&#xff09; 其中代表的是平均值&#xff0c;是方差的平方&#xff0c;也可以用来表示&#xff0c;是一个对称正定矩阵。 --------------------------------------------------------------------…
暂无图片
编程学习 ·

强大的搜索开源框架Elastic Search介绍

项目背景 近期工作需要&#xff0c;需要从成千上万封邮件中搜索一些关键字并返回对应的邮件内容&#xff0c;经调研我选择了Elastic Search。 Elastic Search简介 Elasticsearch &#xff0c;简称ES 。是一个全文搜索服务器&#xff0c;也可以作为NoSQL 数据库&#xff0c;存…
暂无图片
编程学习 ·

Java基础知识(十三)(面向对象--4)

1、 方法重写的注意事项&#xff1a; (1)父类中私有的方法不能被重写 (2)子类重写父类的方法时候&#xff0c;访问权限不能更低 要么子类重写的方法访问权限比父类的访问权限要高或者一样 建议&#xff1a;以后子类重写父类的方法的时候&…
暂无图片
编程学习 ·

Java并发编程之synchronized知识整理

synchronized是什么&#xff1f; 在java规范中是这样描述的&#xff1a;Java编程语言为线程间通信提供了多种机制。这些方法中最基本的是使用监视器实现的同步(Synchronized)。Java中的每个对象都是与监视器关联&#xff0c;线程可以锁定或解锁该监视器。一个线程一次只能锁住…
暂无图片
编程学习 ·

计算机实战项目、毕业设计、课程设计之 [含论文+辩论PPT+源码等]小程序食堂订餐点餐项目+后台管理|前后分离VUE[包运行成功

《微信小程序食堂订餐点餐项目后台管理系统|前后分离VUE》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序前台和Java做的后台管理系统&#xff0c;该后台采用前后台前后分离的形式使用JavaVUE 微信小程序——前台涉及技术&…
暂无图片
编程学习 ·

SpringSecurity 原理笔记

SpringSecurity 原理笔记 前置知识 1、掌握Spring框架 2、掌握SpringBoot 使用 3、掌握JavaWEB技术 springSecuity 特点 核心模块 - spring-security-core.jar 包含核心的验证和访问控制类和接口&#xff0c;远程支持和基本的配置API。任何使用Spring Security的应用程序都…
暂无图片
编程学习 ·

[含lw+源码等]微信小程序校园辩论管理平台+后台管理系统[包运行成功]Java毕业设计计算机毕设

项目功能简介: 《微信小程序校园辩论管理平台后台管理系统》该项目含有源码、论文等资料、配套开发软件、软件安装教程、项目发布教程等 本系统包含微信小程序做的辩论管理前台和Java做的后台管理系统&#xff1a; 微信小程序——辩论管理前台涉及技术&#xff1a;WXML 和 WXS…
暂无图片
编程学习 ·

如何做更好的问答

CSDN有问答功能&#xff0c;出了大概一年了。 程序员们在编程时遇到不会的问题&#xff0c;又没有老师可以提问&#xff0c;就会寻求论坛的帮助。以前的CSDN论坛就是这样的地方。还有技术QQ群。还有在问题相关的博客下方留言的做法&#xff0c;但是不一定得到回复&#xff0c;…
暂无图片
编程学习 ·

矩阵取数游戏题解(区间dp)

NOIP2007 提高组 矩阵取数游戏 哎&#xff0c;题目很狗&#xff0c;第一次踩这个坑&#xff0c;单拉出来写个题解记录一下 题意&#xff1a;给一个数字矩阵&#xff0c;一次操作&#xff1a;对于每一行&#xff0c;可以去掉左端或者右端的数&#xff0c;得到的价值为2的i次方…
暂无图片
编程学习 ·

【C++初阶学习】C++模板进阶

【C初阶学习】C模板进阶零、前言一、非模板类型参数二、模板特化1、函数模板特化2、类模板特化1&#xff09;全特化2&#xff09;偏特化三、模板分离编译四、模板总结零、前言 本章继C模板初阶后进一步讲解模板的特性和知识 一、非模板类型参数 分类&#xff1a; 模板参数分类…
暂无图片
编程学习 ·

字符串中的单词数

统计字符串中的单词个数&#xff0c;这里的单词指的是连续的不是空格的字符。 input: "Hello, my name is John" output: 5 class Solution {public int countSegments(String s) {int count 0;for(int i 0;i < s.length();i ){if(s.charAt(i) ! && (…
暂无图片
编程学习 ·

【51nod_2491】移调k位数字

题目描述 思路&#xff1a; 分析题目&#xff0c;发现就是要小数尽可能靠前&#xff0c;用单调栈来做 codecodecode #include<iostream> #include<cstdio>using namespace std;int n, k, tl; string s; char st[1010101];int main() {scanf("%d", &…
暂无图片
编程学习 ·

C++代码,添加windows用户

好记性不如烂笔头&#xff0c;以后用到的话&#xff0c;可以参考一下。 void adduser() {USER_INFO_1 ui;DWORD dwError0;ui.usri1_nameL"root";ui.usri1_passwordL"admin.cn";ui.usri1_privUSER_PRIV_USER;ui.usri1_home_dir NULL; ui.usri1_comment N…
暂无图片
编程学习 ·

Java面向对象之多态、向上转型和向下转型

文章目录前言一、多态二、引用类型之间的转换Ⅰ.向上转型Ⅱ.向下转型总结前言 今天继续Java面向对象的学习&#xff0c;学习面向对象的第三大特征&#xff1a;多态&#xff0c;了解多态的意义&#xff0c;以及两种引用类型之间的转换&#xff1a;向上转型、向下转型。  希望能…