设计模式第三天|设计模式结构型:适配器模式、装饰器模式、代理模式

news2024/11/15 11:06:33

文章目录

  • 设计模式的分类
  • 适配器模式
    • 概念
    • 俗话说
    • 角色
    • 具体应用(Spring MVC)
      • 图解
      • 具体步骤
  • 装饰器模式
    • 定义
    • 核心
    • 俗话说
    • 类名表现
    • 图解
    • 具体构造
    • 代码实现
    • 简化
    • 优点
    • 缺点
  • 代理模式(Spring AOP 面向切面)
    • 定义
    • 俗话说
    • 角色
    • 代理模式分类
      • 静态代理
        • 角色
        • 代码
        • 好处
        • 缺点
        • 实用
      • 动态代理
    • AOP
      • 什么是AOP
      • 具体信息
      • 通知类型
      • AOP底层原理
      • 实际用法
      • AOP操作
        • 准备环节
        • 操作环节(AspectJ注解)
        • 操作环节(AspectJ配置文件)

设计模式的分类

总的来说设计模式分为三大类

创建型模式(五种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
结构型模式(七种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
行为型模式(十一种):策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式。

这篇我们主要讲一下结构型模式。

由于我是java开发者,我会根据设计模式在java中的应用来说说

适配器模式

概念

将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的哪些类可以一起工作。

俗话说

就是安卓充电头(Adaptee原角色)转换成type-c充电头(Target目标角色)要使用转接头(Adapter适配器角色)一样

角色

  1. 目标角色(Target)

该角色定义把其他类转换为何种接口,也就是我们的期望接口。

  1. 源角色(Adaptee)

你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象。

  1. 适配器角色(Adapter)

适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:通过继承或是类关联的方式把源角色转换为目标角色。

具体应用(Spring MVC)

图解

在这里插入图片描述

具体步骤

  1. 浏览器发送请求到 控制器(DispatcherServlet)

  2. 控制器 根据请求地址, 到 HandlerMapping(处理器映射) 寻找对应的 Handler(处理器)

  3. HanldlerMapping 返回 找到的Handler

  4. DispatcherServlet 根据找到的Handler 找对应的HandlerAdaptor

  5. 执行对应的Handler方法

  6. Handler 将执行结果 和 要响应的视图名 封装成 ModelAndView 对象

  7. 控制器根据返回的 ViewName 找对应的ViewResolver (视图解析ViewResolver 将 Model 渲染到 View 中

  8. 将渲染结果 返回给控制器

  9. 最终将结果响应给客户端浏览器

适配器使用就是这里的第4,5步.

装饰器模式

定义

是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。

核心

装饰器模式的核心是功能扩展,使用装饰器模式可以透明且动态地扩展类的功能。

俗话说

也就跟开闭原则一样,只做扩展,不做改变,追只不过主体编号变成了要加上的功能

类名表现

  1. Wrapper(包装器模式)
    1. (包装类):基础数据类型转引用类型
基本数据类型包装类
intInteger
byteByte
shortShort
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

​ 2. MyBatis-plus中的Wrapper

​ 使用方法

QueryWrapper<User> queryWrapper = new QueryWrapper<>();   queryWrapper.isNull("name").age("age", 12).isNotNull("email");
int result = userMapper.delete(queryWrapper);
  1. Decorator(装饰模式)

如下所示

图解

在这里插入图片描述

具体构造

  1. Component(抽象构件):定义一个抽象接口以规范准备接收附加责任的对象。
  2. ConcreteComponent(具体构件):实现抽象构件,通过装饰角色为其添加一些职责。
  3. Decorator(抽象装饰类):继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. ConcreteDecorator(具体装饰类):实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

代码实现

大家应该都看过一部日本动画片《铁甲小宝》,里面的一号机器人—卡布达,在平时的时候,他是一个笨笨的但是特别可爱的机器人,当他们需要争夺和平星进行比赛的时候,他就会变身,成为战斗力爆表的卡布达,最后,他获得了他的卡布达巨人,让战斗力又提升了一个档次。这个例子中,变身卡布达以及卡布达巨人就是普通卡布达的装饰,增强卡布达的能力。

  1. 卡布达接口
//Component(抽象构件):定义一个抽象接口以规范准备接收附加责任的对象。
public interface KaBuDa {
 
    /**
     * 示人的形象
     */
    void display();
}
  1. 装饰器代码
//Decorator(抽象装饰类):继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
public class Decorator implements KaBuDa {
 
    private KaBuDa kaBuDa;
 
    public Decorator(KaBuDa kaBuDa) {
        this.kaBuDa = kaBuDa;
    }
 
 
    @Override
    public void display() {
        this.kaBuDa.display();
    }
}
  1. 普通卡布达CommonKaBuDa
//ConcreteComponent(具体构件):实现抽象构件,通过装饰角色为其添加一些职责。
public class CommonKaBuDa implements KaBuDa{
 
    @Override
    public void display() {
        System.out.println("我是普通卡布达,卡布卡布卡布");
    }
}
  1. 变身后的卡布达TransfigurationKaBuDa
//ConcreteDecorator(具体装饰类):实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
public class TransfigurationKaBuDa extends Decorator{
 
    public TransfigurationKaBuDa(KaBuDa kaBuDa) {
        super(kaBuDa);
    }
 
    @Override
    public void display() {
        super.display();
        System.out.println("启动超级变换形态---超级变换形态");
    }
}
  1. 卡布达巨人GiantKaBuDa
//ConcreteDecorator(具体装饰类):实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
public class GiantKaBuDa extends Decorator{
 
    public GiantKaBuDa(KaBuDa kaBuDa) {
        super(kaBuDa);
    }
 
    @Override
    public void display() {
        super.display();
        System.out.println("超级卡布达巨人");
    }
}
  1. 测试

可以看到,这个装饰是一点一点往上加的

public class MainClass {
    public static void main(String[] args) {
        System.out.println("-----------------1--------------------");
        KaBuDa commonKaBuDa = new CommonKaBuDa();
        commonKaBuDa.display();
        System.out.println("-----------------2--------------------");
        System.out.println("蜻蜓队长: 比赛开始");
        System.out.println("-----------------3--------------------");
        KaBuDa transfigurationKaBuDa = new TransfigurationKaBuDa(commonKaBuDa);
        transfigurationKaBuDa.display();
        System.out.println("-----------------4--------------------");
        System.out.println("呼唤卡布达巨人");
        System.out.println("-----------------5--------------------");
        KaBuDa giantKaBuDa = new GiantKaBuDa(transfigurationKaBuDa);
        giantKaBuDa.display();
    }
}
-----------------1--------------------
我是普通卡布达,卡布卡布卡布
-----------------2--------------------
蜻蜓队长: 比赛开始
-----------------3--------------------
我是普通卡布达,卡布卡布卡布
启动超级变换形态---超级变换形态
-----------------4--------------------
呼唤卡布达巨人
-----------------5--------------------
我是普通卡布达,卡布卡布卡布
启动超级变换形态---超级变换形态
超级卡布达巨人

结果看到,对卡布达不断进行进行装饰,最后达到拥有卡布达巨人的无敌状态。

简化

一般情况下,装饰器模式没有抽象的被装饰接口,只有一个具体的被装饰对象,这是就考虑去掉Component类(接口),把Decorator作为一个ConcreteComponent的子类。如下图所示:

在这里插入图片描述

优点

  1. 装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用

  2. 通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果

  3. 装饰器完全遵守开闭原则

缺点

  • 从代码层面来看,使用装饰器模式会出现更多的代码,更多的类,增加程序复杂性
  • 动态装饰时,多层装饰时会更复杂

代理模式(Spring AOP 面向切面)

定义

是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是我们生活中常见的中介。

俗话说

  1. 你要一个产品,这个产品(真实角色)是从某个模子(抽象角色)里刻出来的,然后你又需要这个产品有不一样的地方,需要一个次加工(代理角色).然后你最终拿到这个产品(代理角色)是从次加工那里拿到的
  2. 通知的用法:小宝宝想睡觉(service层中某个方法),但是睡前要脱衣服(方法前通知),睡醒还要穿衣服(方法执行后通知),于是脱衣服和穿衣服都可以写进(切面)
  3. 只不过这个切面不是属于框架中的,而是想要给框架中某一部分添加一些功能

角色

  1. 抽象角色(Subject):通过接口或抽象类声明真实角色实现的业务方法。
  2. 代理角色(Proxy):实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  3. 真实角色(RealSubject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

代理模式分类

静态代理

角色
  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象的人!
代码
  1. 抽象角色
//出租房子
public interface Rent {
    void rent();
}
  1. 真实角色
//房东
public class Host implements Rent {
    @Override
    public void rent() {
        //打印
        System.out.println("房东有房子出租!");
    }
}
  1. 代理角色
//中介
public class Proxy implements Rent {
    private Host host;

    public Proxy() {
    }

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

    @Override
    public void rent() {
        host.rent();
        seeHouse();
        hetong();
        fare();
    }

    public void seeHouse() {
        //打印
        System.out.println("See House 看房子");
    }

    public void hetong(){
        //打印
        System.out.println("签约租凭合同");
    }
    public void fare(){
        //打印
        System.out.println("收取中介费用!");
    }
}
  1. 客户端访问代理角色
public class Client {
    public static void main(String[] args) {
        //房东出租房子给中介代理
        Host host = new Host();
        //代理。帮助房东出租房子,会有一些附属的操作
        Proxy proxy = new Proxy(host);
        //
        proxy.rent();
    }
}
好处
  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共也就就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
缺点

一个真实角色就会产生一个代理角色;代码量会翻倍开发效率会变低

实用
  1. UserService
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
  1. UserServiceImpl——真实对象
//真实对象
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }

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

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

    @Override
    public void query() {
        System.out.println("查询了一个用户");
    }
}
  1. UserServiceProxy——代理
public class UserServiceProxy implements UserService{

    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

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

  1. 客户端
public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);
        proxy.add();
    }
}

动态代理

动态代理和静态代理角色一样

动态代理的代理类是动态生成的,不是我们直接写好的!

此处留白,作者还在学习

AOP

什么是AOP

  1. 面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而是的业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  2. 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
  3. 使用登录的例子说明AOP

在这里插入图片描述

具体信息

AOP的作用: 提供声明式事务,允许用户自定义切面.

  1. Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。(增强的集合))
  2. Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。(要增强的方法)
  3. Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。(是在方法执行的时机)
  4. Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。(具体要怎么增强)
  5. Target(目标对象):织入 Advice 的目标对象.。(具体要增强的类)
  6. Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程(增强的集合与目标对象的联系)

通知类型

  1. 前置通知 Before advice:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常
  2. 后置通知 After returning advice:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行
  3. 异常通知 After throwing advice:在连接点抛出异常后执行
  4. 最终通知 After (finally) advice:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容
  5. 环绕通知 Around advice:环绕通知围绕在连接点前后,能在方法调用前后自定义一些操作,还需要负责决定是继续处理 join point (调用 ProceedingJoinPoint 的 proceed 方法)还是中断执行

AOP底层原理

AOP底层使用动态代理,有两种情况

  1. 有接口的情况,使用JDK动态代理;创建接口实现类代理对象,增强类的方法
    在这里插入图片描述

  2. 没有接口情况,使用CGLIB动态代理;创建子类的代理对象,增强类的方法

在这里插入图片描述

实际用法

给某层创建一个切面,并且在这个切面中放入一些方法(增强)

在这里插入图片描述

AOP的JDK动态代理

  1. 使用JDK动态代理,使用Proxy类里面的方法创建代理对象

调用 newProxyInstance 方法,方法有三个参数:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

方法有三个参数:

第一参数,类加载器

第二参数,增强方法所在的类,这个类实现的接口,支持多个接口

第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分

  1. 编写JDK动态代理

    1. 创建接口,定义方法
    public interface UserDao {
        public int add(int a,int b);
        public String update(String id);
    
    }
    
    1. 创建接口实现类,实现方法
    public class UserDaoImpl implements UserDao{
        @Override
        public int add(int a,int b) {
            return a+b;
        }
    
        @Override
        public String update(String id) {
            return id;
        }
    
    }
    
    1. 使用Proxy类创建接口代理对象
    //创建代理对象代码
    public class UserDaoProxy implements InvocationHandler {
    
        //把创建的是谁的代理对象,把谁传递过来
        //有参构造传递
        private Object obj;
    
        public UserDaoProxy(Object obj) {
            this.obj = obj;
        }
    
        //增强的逻辑
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
             //方法之前
             System.out.println("方法之前执行..."+method.getName()+":传递的参数..."+ Arrays.toString(args));
    
             //被增强的方法执行
             Object res = method.invoke(obj, args);
    
             //方法之后
             System.out.println("方法之后执行..."+obj);
    
            return res;
        }
    }
    
    1. 客户端
    //使用 Proxy 类创建接口代理对象
    public class JDKProxy {
        public static void main(String[] args) {
    
            //创建接口实现类代理对象
            Class[] interfaces = {UserDao.class};
    
            UserDaoImpl userDao = new UserDaoImpl();
            UserDao dao  = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
    
            int res = dao.add(1, 2);
            System.out.println("result:"+res);
        }
    
    }
    

回过头来我们再看看AOP的具体信息

  1. 连接点

类里面哪些方法可以被增强,这些方法称为连接点

  1. 切入点

实际被真正增强的方法,称为切入点

  1. 通知(增强)
    1. 实际增强的逻辑部分称为通知(增强)
    2. 通知有多种类型,在上边有介绍
  2. 切面

把通知应用到切入点过程

AOP操作

准备环节
  1. Spring框架一般都是基于AspectJ实现AOP操作

AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作

  1. 基于AspectJ实现AOP操作

  2. 基于xml配置文件实现

  3. 基于注解方式实现(使用)

  4. 在项目工程中引入AOP相关依赖

		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.15.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.9</version>
        </dependency>

        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.9.9</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
  1. 切入点表达式

    1. 切入点表达式作用:知道对哪个类里面的哪个方法进行增强

    2. 语法结构

      • execution (【权限修饰符】【返回类型】【类全路径】【方法名称】(【参数列表】)
      • 举例1:对com.jin.dao.UserDao类里面的add进行增强

      execution(* com.jin.dao.UserDao.add(..))
      
      • 举例2:对com.jin.dao.UserDao类里面的所有的方法进行增强
      execution(* com.jin.dao.UserDao.*(..))
      
      • 举例3:对com.jin.dao类里面所有类,类里面的所有的方法进行增强
      execution(* com.jin.dao.*.*(..))
      
操作环节(AspectJ注解)
  1. 创建类,在类中定义方法
//被增强的类
public class User {
    public void add(){
        System.out.println("add ...");
    }
}
  1. 创建增强类(编写增强逻辑)

    1. 在增强类里面,创建方法,让不同方法代表不同通知类型
    //增强类
    public class UserProxy {
        //前置通知
        public void before(){
            System.out.println("before ...");
        }
    }
    
  2. 进行通知的配置

    1. 在spring配置文件中,开启注解扫描
    <?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: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
           http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd
    ">
        <!--开启注解扫描-->
    <context:component-scan base-package="com.jin.aopanno"></context:component-scan>
    </beans>
    
    
    1. 使用注解创建User和UserProxy对象
    //被增强的类
    @Component
    public class User {
       ...
    }
    
    //增强类
    @Component
    public class UserProxy {
       ...
    }
    
    1. 在增强类上面添加注解@Aspect
    //增强类
    @Component
    @Aspect   //生成代理对象
    public class UserProxy {
     	...   
    }
    
    1. 在spring配置文件中开启生成代理对象
    <!--开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  3. 配置不同类型的通知

    1. 在增加类的里面,通知方法上面添加通知类型注解,使用切入点表达式配置
    //增强类
    @Component
    @Aspect   //生成代理对象
    public class UserProxy {
        //前置通知
        //@Before注解表示作为前置通知
        @Before(value = "execution(* com.jin.aopanno.User.add(..))")
        public void before(){
            System.out.println("before ...");
        }
        @AfterReturning(value = "execution(* com.jin.aopanno.User.add(..))")
        public void afterReturning(){
            System.out.println("afterReturning ...");
        }
        @After(value = "execution(* com.jin.aopanno.User.add(..))")
        public void after(){
            System.out.println("after ...");
        }
    
    
        @AfterThrowing(value = "execution(* com.jin.aopanno.User.add(..))")
        public void afterThrowing(){
            System.out.println("AfterThrowing ...");
        }
        @Around(value = "execution(* com.jin.aopanno.User.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕之前 ...");
            proceedingJoinPoint.proceed();
            System.out.println("环绕之后 ...");
        }
    }
    
  4. 相同的切入点抽取

//增强类
@Component
@Aspect   //生成代理对象
public class UserProxy {

	//相同切入点抽取
    @Pointcut(value = "execution(* com.jin.aopanno.User.add(..))")
    public void pointdemo(){}
    
    //前置通知
    //@Before注解表示作为前置通知
    @Before(value = "pointdemo()")
    public void before(){
        System.out.println("before ...");
    }
}
  1. 有多个增强类多同一个方法进行增强,设置增强类优先级

    1. 在增强类上边添加注解@Order(数字类型值),数字类型值越小优先级越高
    @Component
    @Aspect   //生成代理对象
    @Order(1)  //优先级(数字值越小优先级越高)
    public class PersonProxy {
       ...
    }
    
  2. 完全使用注解开发

    1. 创建配置类,不需要创建xml配置文件
    @ComponentScan(basePackages = {"com.jin.pojo"})
    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true) //默认EnableAspectJAutoProxy为false
    public class SpringConfig {
    }
    
    1. 测试代码
    @Test
        public void MyTest01(){
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
            User user = context.getBean("user", User.class);
            user.add();
        }
    
操作环节(AspectJ配置文件)
  1. 创建两个类,增强类和被增强类,创建方法
//被增强类
public class Book {
    public void buy(){
        System.out.println("buy ....");
    }
}
//增强类
public class BookProxy {

    public void before(){
        System.out.println("before ...");
    }
}
  1. 在Spring配置文件中创建两个类对象
<!--创建对象-->
<bean id="book" class="com.jin.aop.Book"></bean>
<bean id="bookProxy" class="com.jin.aop.BookProxy"></bean>
  1. 在Spring配置文件中配置切入点
<!--配置aop增强-->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="p" expression="execution(* com.jin.aop.Book.buy(..))"/>
    <!--配置切面-->
    <aop:aspect ref="bookProxy">
        <!--增强作用在具体的方法上-->
        <aop:before method="before" pointcut-ref="p"/>
    </aop:aspect>
</aop:config>
  1. 测试
@Test
    public void MyTest(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Book book = context.getBean("book", Book.class);
        book.buy();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1946219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BFF:优化前后端协作设计模式

BFF&#xff1a;优化前后端协作设计模式 BFF是什么 BFF即 Backends For Frontends (服务于前端的后端)。是一种介于前端和后端之间一种重要的通信设计模式。它旨在解决前端与后端协作中的复杂性问题。 背景 行业背景&#xff1a;传统前端应用&#xff08;如Web应用、移动应…

《深入探秘Java中的枚举:掌握Enum的魔力》

目录 &#x1f4dd; 枚举枚举的定义枚举的使用1、表示一组固定常量2、实现接口3、枚举与策略模式4、EnumSet5、EnumMap &#x1f4ce; 参考文章 &#x1f600; 准备好了吗&#xff1f;让我们一起步入这座Java神奇的城堡&#xff0c;探寻枚举&#xff08;Enum&#xff09;这个强…

Ubuntu 修改源地址

注意事项&#xff1a;版本说明&#xff01;&#xff01;&#xff01; Ubuntu24.04的源地址配置文件发生改变。 不再使用以前的 sources.list 文件&#xff0c;该文件内容变成了一行注释&#xff1a; # Ubuntu sources have moved to /etc/apt/sources.list.d/ubuntu.sources…

STM32-FreeRTOS快速学习

定义 FreeRTOS 满足实施系统对任务响应时间的要求。 实时操作系统、轻量级&#xff08;内核小&#xff0c;只需要几KB的ROM和RAM&#xff09;、 提供了一些内核功能&#xff0c;如任务管理、时间管理、内存管理和通信机制等。 和裸机的区别 裸机&#xff1a;无操作系统&…

产品系统的UI暗色系和浅色系模式切换是符合人体视觉工程学的设计

视觉革命&#xff1a;UI设计中的暗夜与黎明 UI设计如同夜空中最亮的星辰&#xff0c;引领着用户穿梭于信息的海洋。而今&#xff0c;一场视觉革命正在悄然上演&#xff0c;它关乎于我们的眼睛&#xff0c;关乎于我们的体验——那就是产品系统的UI暗色系和浅色系模式的切换。如…

【机器学习】Jupyter Notebook如何使用之基本步骤和进阶操作

引言 Jupyter Notebook 是一个交互式计算环境&#xff0c;它允许创建包含代码、文本和可视化内容的文档 文章目录 引言一、基本步骤1.1 启动 Jupyter Notebook1.2 使用 Jupyter Notebook 仪表板1.3 在笔记本中工作1.4 常用快捷键1.5 导出和分享笔记本 二、进阶用法2.1 组织笔…

Excel超级处理器,工作簿文件.xls/.xlsx/.csv相互批量转换

如何将.xlsx文件转成.csv文件&#xff0c;.xls转换成.xlsx文件&#xff0c;以及.xls文件转成.csv文件或.csv转换成.xlsx文件&#xff0c;如果是单个文件转换&#xff0c;那么将当前文件另存为&#xff0c;保存类型&#xff0c;选择即可。如下图所示&#xff1a; 如果是多个文件…

【AutoDL】AutoDL+Xftp+Xshell+VSCode配合使用教程

身边没有显卡资源或不足以训练模型时&#xff0c;可以租赁服务器的显卡。 1、AutoDL Step :注册账号->选择显卡->选择环境->开机启动 1.1 首先打开AutoDL官网&#xff0c;注册账号 1.2 租赁自己想要的显卡资源 1.3 选择基础环境。 此处&#xff0c;我们让其自动配置…

[网络通信原理]——TCP/IP模型—网络层

网络层 网络层概述 网络层位于OSI模型的第三层&#xff0c;它定义网络设备的逻辑地址&#xff0c;也就是我们说的IP地址&#xff0c;能够在不同的网段之间选择最佳数据转发路径。在网络层中有许多协议&#xff0c;其中主要的协议是IP协议。 IP数据包格式 IP数据报是可变长度…

Linux服务器配置Python+PyTorch+CUDA深度学习环境

参考博主Linux服务器配置PythonPyTorchCUDA深度学习环境_linux cuda环境配置-CSDN博客 https://blog.csdn.net/NSJim/article/details/115386936?ops_request_misc&request_id&biz_id102&utm_termlinux%E8%99%9A%E6%8B%9F%E7%8E%AF%E5%A2%83%E6%8C%89pytorch%20c…

微信答题小程序产品研发-需求分析与原型设计

欲知应候何时节&#xff0c;六月初迎大暑风。 我前面说过&#xff0c;我决意仿一款答题小程序&#xff0c;所以我做了大量的调研。 题库软件产品开发不仅仅是写代码这一环&#xff0c;它包含从需求调研、分析与构思、设计到开发、测试再到部署上线一系列复杂过程。 需求分析…

子数组和为k子数组和最大

题目1&#xff1a;子数组和为k /*给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 k 的子数组的个数 。子数组是数组中元素的连续非空序列。示例 1&#xff1a;输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2 示例 2&#xff1a;输入&a…

微软蓝屏事件对企业数字化转型有什么影响?

引言&#xff1a;从北京时间2024年7月19日&#xff08;周五&#xff09;下午2点多开始&#xff0c;全球大量Windows用户出现电脑崩溃、蓝屏死机、无法重启等情况。事发后&#xff0c;网络安全公司CrowdStrike称&#xff0c;收到大量关于Windows电脑出现蓝屏报告&#xff0c;公司…

make2exe:自动集成测试

模板Makefile&#xff0c;生成多个C/C模块的集成测试程序。

算法学习day19

一、通过删除字母匹配到字符字典中的最大值 给你一个字符串 s 和一个字符串数组 dictionary &#xff0c;找出并返回 dictionary 中最长的字符串&#xff0c;该字符串可以通过删除 s 中的某些字符得到。 如果答案不止一个&#xff0c;返回长度最长且字母序最小的字符串。如果…

花几千上万学习Java,真没必要!(二十六)

1、成员内部类&#xff1a; package internalclass.com; //在Java中&#xff0c;成员内部类&#xff08;也称为非静态内部类&#xff09;是定义在另一个类&#xff08;外部类&#xff09;内部的类。 //成员内部类可以访问外部类的所有成员&#xff08;包括私有成员&#xff09…

【计算机网络】网络层——IPv4地址(个人笔记)

学习日期&#xff1a;2024.7.24 内容摘要&#xff1a;IPv4地址&#xff0c;分类编址&#xff0c;子网&#xff0c;无分类编址 IPv4地址概述 在TCP/IP体系中&#xff0c;IP地址是一个最基本的概念&#xff0c;IPv4地址就是给因特网上的每一台主机的每一个接口分配一个在全世界…

ASP.NET Web Api 使用 EF 6,DateTime 字段如何取数据库服务器当前时间

前言 在做数据库设计时&#xff0c;为了方便进行数据追踪&#xff0c;通常会有几个字段是每个表都有的&#xff0c;比如创建时间、创建人、更新时间、更新人、备注等&#xff0c;在存储这些时间时&#xff0c;要么存储 WEB 服务器的时间&#xff0c;要么存储数据库服务器的时间…

Java之数组应用-冒泡排序-二分查找

冒泡排序 冒泡(Bubble Sort)排序是一种简单排序算法&#xff0c;它通过依次比较交换两个相邻元素实现功能。每一次冒泡会让至少一个元素移动到它应该在的位置上&#xff0c;这样 n 次冒泡就完成了 n 个数据的排序工作。 这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”…

python实现图像缩放算法

图像缩放算法 1.最近邻插值图像缩放算法详解算法步骤Python 实现详细解释 优缺点2.双线性插值图像缩放算法详解算法步骤Python 实现详细解释 优缺点3.双三次插值图像缩放算法详解算法步骤Python 实现详细解释 优缺点 1.最近邻插值图像缩放算法详解 最近邻插值&#xff08;Near…