代理模式及Spring AOP

代理模式及Spring AOP

代理模式:为其他对象提供一种代理以控制对这个对象的访问。

在这里插入图片描述

Subject:定义RealSubject和Proxy 的共用接口

RealSubject:定义Proxy所代理的实体

Proxy:保存一个指向真实实体的引用,并提供和真实实体相同的接口

Java实现:

    package com.lbl.pojo;
    
    public interface Subject {
        void talk();
        void run();
        void jump();
        void fly();
    }
    package com.lbl.pojo;
    
    public class RealSubject implements Subject {
        public void talk() {
            System.out.println("talk");
        }
    
        public void run() {
            System.out.println("run");
        }
    
        public void jump() {
            System.out.println("jump");
        }
    
        public void fly() {
            System.out.println("fly");
        }
    }
    package com.lbl.pojo;
    
    public class Proxy implements Subject{
        private Subject subject;
    
        public Proxy() {
            this.subject = new RealSubject();
        }
    
        public void talk() {
            subject.talk();
        }
    
        public void run() {
            subject.run();
        }
    
        public void jump() {
            subject.jump();
        }
    
        public void fly() {
            subject.fly();
        }
    }
    package com.lbl.pojo;
    
    public class Client {
        public static void main(String[] args) {
            Proxy proxy = new Proxy();
            proxy.run();
            proxy.talk();
            proxy.fly();
            proxy.jump();
        }
    }

代理模式的应用

  • 远程代理。为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实
  • 虚拟代理。根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。如网页中一张张加载的图片
  • 安全代理。用来控制真实对象的访问权限
  • 智能指引。当调用真实的对象时,代理处理另外的一些事。

代理模式的分类

  • 静态代理
  • 动态代理

静态代理的缺点是每一个真实实体都会产生一个代理角色,开发效率变低。而动态代理可以解决这个问题。

动态代理

  • 动态代理的代理类是动态生成的,不是事先写好的
  • 动态代理分为两类:基于接口的动态代理,基于类的动态代理
    • 基于接口:JDK动态代理
    • 基于类:cglib
    • Java字节码:javasist

基于接口要了解的两个类:Proxy,InvocationHandler

Proxy:提供了创建动态代理类和实例的静态方法

InvocationHandler:由代理实例调用处理程序实现的接口。

每个代理实例都有一个关联的调用处理程序,当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。

实例

我们有一个“战士(warrior)”接口,兽人战士(OrkWarrior)实现了“战士接口”,ProxyInvokeHandler类提供对兽人战士的代理访问,最后玩家(Player)通过代理操纵兽人战士。

    package com.lbl.demo;
    
    public interface Warrior {
        void attack();
        void defend();
        void skill();
    }
    package com.lbl.demo;
    
    public class OrkWarrior implements Warrior {
        public void attack() {
            System.out.println("为了部落!!");
        }
    
        public void defend() {
            System.out.println("兽人永不为奴");
        }
    
        public void skill() {
            System.out.println("大风车!");
        }
    }
    package com.lbl.demo;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyInvokeHandler implements InvocationHandler {
        private Warrior warrior;
    
        //设置需要代理的接口
        public void setWarrior(Warrior warrior) {
            this.warrior = warrior;
        }
        //获取代理
        public Object getProxy() {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),warrior.getClass().getInterfaces(),this);
        }
        //执行接口中的方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(warrior,args);
        }
    
    }
    package com.lbl.demo;
    
    public class Player {
        public static void main(String[] args) {
            Warrior warrior = new OrkWarrior();
            ProxyInvokeHandler pih = new ProxyInvokeHandler();
            pih.setWarrior(warrior);
            Warrior warrior1 = (Warrior) pih.getProxy();
            warrior1.attack();
            warrior1.defend();
            warrior1.skill();
        }
    }

Spring中AOP的实现

Spring API实现

导入maven依赖:

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>

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

实例

有一个UserService接口,一个UserServiceImp的实现类;有两个打印日志的事务:BeforeLog(方法执行前打印),AfterLog(方法返回前打印)。现要将BeforeLog切入到UserService的add方法前,将AfterLog切入到UserService的delete方法后。

    package com.lbl.service;
    
    public interface UserService {
        void add();
        void delete();
        void update();
        void select();
    }
    package com.lbl.service;
    
    public class UserServiceImp implements UserService {
        public void add() {
            System.out.println("add an user");
        }
    
        public void delete() {
            System.out.println("delete an user");
        }
    
        public void update() {
            System.out.println("update an user");
        }
    
        public void select() {
            System.out.println("select an user");
        }
    }
    package com.lbl.log;
    
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    public class BeforeLog implements MethodBeforeAdvice {
        //MethodBeforeAdvice接口用于切入方法执行前
        public void before(Method method, Object[] objects, Object o) throws Throwable {
            System.out.println(o.getClass().getName() + "类的" + method.getName() + "方法被执行了");
        }
    }
    package com.lbl.log;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    import java.lang.reflect.Method;
    
    public class AfterLog implements AfterReturningAdvice {
        //AfterReturningAdvice用于切入方法返回前
        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
            System.out.println(method.getName() + "被执行了,返回了" + o);
        }
    }

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:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
    
    
        <bean id="userService" class="com.lbl.service.UserServiceImp"/>
        <bean id="beforeLog" class="com.lbl.log.BeforeLog"/>
        <bean id="afterLog" class="com.lbl.log.AfterLog"/>
        <bean id="myaop" class="com.lbl.myaop.DiyAop"/>
    
        
        <aop:config>
    <!--        配置切入点-->
            <aop:pointcut id="addpointcut" expression="execution(* com.lbl.service.UserServiceImp.add())"/>
            <aop:pointcut id="deletepointcut" expression="execution(* com.lbl.service.UserServiceImp.delete())"/>
    <!--        配置执行环绕-->
            <aop:advisor advice-ref="beforeLog" pointcut-ref="addpointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="deletepointcut"/>
        </aop:config>
    
    
    </beans>

Spring AOP 中pointcut expression表达式解析及配置_Java_敲码农的小代码   ;-CSDN博客

关于pointcut中的表达式(expression)详解

自定义切入

这种方法不使用Spring提供的接口,而在Spring配置文件里进行设置,不如前一种方法强大,因为前一种方法可以利用反射获取大量信息。

UserService接口和UserServiceImp实现类不变。新增一个自定义切面:

    package com.lbl.myaop;
    
    public class DiyAop {
        public void beforeMethod() {
            System.out.println("在方法执行之前");
        }
    
        public void afterMethod() {
            System.out.println("在方法执行之后");
        }
    }

修改配置文件:

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
    
    
        <bean id="userService" class="com.lbl.service.UserServiceImp"/>
        <bean id="beforeLog" class="com.lbl.log.BeforeLog"/>
        <bean id="afterLog" class="com.lbl.log.AfterLog"/>
        <bean id="myaop" class="com.lbl.myaop.DiyAop"/>
        <aop:config>
    <!--        注册一个切面-->
            <aop:aspect ref="myaop">
    <!--            注册一个切入点-->
                <aop:pointcut id="diypc" expression="execution(* com.lbl.service.UserServiceImp.*(..))"/>
    <!--            选择切入方法及位置和切入点-->
                <aop:before method="beforeMethod" pointcut-ref="diypc"/>
                <aop:after method="afterMethod" pointcut-ref="diypc"/>
            </aop:aspect>
        </aop:config>
    </beans>

使用注解实现AOP

    package com.lbl.myaop;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect //声明这是一个切面
    public class Annopc {
        //声明切入位置和切入点
        @Before("execution(* com.lbl.service.UserServiceImp.add())")
        public void beforeMethod() {
            System.out.println("注解方式,方法执行前");
        }
    }

在配置文件中注册bean并引入对AOP注解的支持:

    <bean id="annopc" class="com.lbl.myaop.Annopc"/>
    <!--    proxy-target-class为false是基于JDK,为true是基于cglib,但两者执行的结果相同-->
    <aop:aspectj-autoproxy proxy-target-class="false"/>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章