代理模式详解

1. 代理模式

之前其实也有写
https://blog.csdn.net/alpha0808/article/details/122945343

定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

1.1概述

1.1.1 优点有:
  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
1.1.2 缺点是:
  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

解决缺点的方式是实现动态代理

1.2 模式的结构

代理模式的主要角色如下。

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S62OCxwW-1654447361082)(img/image-20220526095918214.png)]

代理模式分为静态代理和动态代理。

  • 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
  • 动态:在程序运行时,运用反射机制动态创建而成
  • 应当注意一点的是,静态代理、动态代理都是基于接口上的开发,而如果只是类代理应该采用Cglib代理模式。

1.3 静态代理

在这里插入图片描述

1.3.1 优缺点

优点:在不修改目标对象的功能的前提下,能通过代理对象对目标功能进行扩展

缺点:因为代理对象需要与目标对象实现相同的接口,所以会有很多的代理类

一旦接口增加方法,目标对象与代理对象都需要维护

1.3.2 demo实现
//target interface
public interface Subject {
    void request();
}
//SubjectImpl
public class SubjectImpl implements Subject{
    @Override
    public void request() {
        //do something
        System.out.println("SubjectImpl request...");
    }
}
//ProxySubject
public class ProxySubject implements Subject{
    //创建目标接口的实例
    private Subject target;
    @Override
    public void request() {
        if (target == null){
            target = new SubjectImpl();
        }

        //扩展功能
        preSubject();
        target.request();
        postSubject();
    }

    public void preSubject(){
        //do something
        System.out.println("preSubject Approach(preprocessing) ...");
    }

    public void postSubject(){
        //do something
        System.out.println("postSubject Approach(after processing)...");
    }
}
//test
    @Test
    public void myDemo(){
        ProxySubject target = new ProxySubject();
        target.request();
    }
//output
preSubject Approach(preprocessing) ...
SubjectImpl request...
postSubject Approach(after processing)...

1.4 动态代理

在这里插入图片描述

1.4.1 概述

代理对象,不需要实现接口,但是目标对象要实现接口,否则不能使用动态代理

代理对象的生成,是利用JDK的API,即java.lang.reflect.proxy,动态的在内存中构建对象,实现代理只需构建newProxyInstance()即可。

动态代理也被称为JDK代理,接口代理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j3jC6l3C-1654447361083)(img/image-20220526105029002.png)]

1.4.2 code demo
//target implements
public interface Subject {
    String request(String args);
}
//SubjectImpl
public class SubjectImpl implements Subject{
    @Override
    public String request(String args) {
        //do something
        System.out.println("SubjectImpl request...");
        return args;
    }
}
//DynamicProxy
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxy {
    //创建目标对象
    private Object target;
    public DynamicProxy(Object target){
        this.target = target;
    }
	
    /**
     * 获取代理实例
     * @return
     */
    public Object getProxyInstance() {
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        
        Object proxy = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
               /*
                invoke方法中写扩展的代码
                参数说明:
                proxy:代理对象
                method:执行的目标方法
                args:调用目标方法传入的参数
             */@Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //预处理
                preProxy();
                //获取方法名
                String methodName = method.getName();
                //执行目标方法
                Object targetMethod = method.invoke(target, args);
                System.out.println("method name is: " +methodName+ "result args is :" +targetMethod);
                //后续处理
                afterTargetProxy();
                return targetMethod;
            }
        });
        return proxy;
    }

    public void preProxy(){
        System.out.println("dynamic proxy beginning...");
    }

    public void afterTargetProxy(){
        System.out.println("dynamic proxy commit...");

    }
}
//test   
@Test
    public void myDynamic(){
        Subject target = new SubjectImpl();
        Subject proxyInstance = (Subject) new DynamicProxy(target).getProxyInstance();
        proxyInstance.request("args test");
    }
dynamic proxy beginning...
SubjectImpl request...
method name is: requestresult args is :args test
dynamic proxy commit...

1.5 Cglib

1.5.1 概述

cglib代理也被称为子类代理,它是在内存中构建一个子类的对象,从而实现对目标对象 进行功能扩展,本质是拦截器。

它的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。

实现implements MethodInterceptor()接口中的public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {}

1.5.2 思路图解
1.5.3 准备工作

在这里插入图片描述

导入cglib的包

<!-- https://mvnrepository.com/artifact/org.ow2.asm/asm-commons -->
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-commons</artifactId>
            <version>9.2</version>
        </dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
 <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>9.2</version>
        </dependency>
<!-- https://mvnrepository.com/artifact/org.ow2.asm/asm-tree -->
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-tree</artifactId>
            <version>9.2</version>
        </dependency>
1.5.4 code demo
//target class
public class Subject {
    public void request(){
        //do something
        System.out.println("request....");
    }
}
//ProxyFactory
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class ProxyFactory implements MethodInterceptor {
    //维护一个目标对象
    private Object target;

    //构造器,传入被代理对象
    public ProxyFactory(Object target){
        this.target = target;
    }

    /**
     * 返回一个代理对象,是target对象的代理对象
     * @return
     */
    public Object getProxyInstance(){
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    /**
     * 重写intercept(),会调用目标对象的方法
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        preProxy();
        String methodName = method.getName();
        Object result = method.invoke(target, objects);
        System.out.println("method name is: " +methodName+ "result args is :" +result);
        afterTargetProxy();
        return result;
    }
    //预处理
    public void preProxy(){
        System.out.println("dynamic proxy beginning...");
    }
    //后续处理
    public void afterTargetProxy(){
        System.out.println("dynamic proxy commit...");

    }
}
//test    
@Test
    public void myCglib() {
        Subject proxyInstance = (Subject) new ProxyFactory(new Subject()).getProxyInstance();
        proxyInstance.request();
    }
dynamic proxy beginning...
request....
method name is: requestresult args is :null
dynamic proxy commit...

1.6 Spring Aop

1.6.1在AOP编程中如何选择代理模式?
  • 目标对象需要实现接口,使用JDK代理
  • 目标对象不需要实现接口,使用CGLIB代理
1.6.2 概述
  1. 是一种通过动态代理实现程序功能扩展和统一维护的一种技术。

  2. 降低耦合度,提高程序的可重用性,同时提高了开发的效率。

  3. AOP编程操作的主要对象是切面(aspect),而切面用于模块化横切关注点(公共功能)。

1.6.3 基本概念
名称功能
连接点(JoinPoint)指那些被拦截的方法
切入点(PointCut)指代拦截的位置
通知(Advice)指代拦截方法成功后要进行的操作
切面(Aspect)描述通知与切入点的关系
目标(Target)被代理的对象
代理(Proxy)向目标对象应用通知之后创建的代理对象
通知类定义通知的类
横切关注点从每个方法中抽取出来的同一类非核心业务

在这里插入图片描述

1.6.4 注解

① @Before:前置通知,在方法执行之前执行

② @After:后置通知,在方法执行之后执行,不管方法是否发生异常

③ @AfterReturning:返回通知,在方法返回结果之后执行

④ @AfterThrowing:异常通知,在方法抛出异常之后执行

⑤ @Around:环绕通知,围绕着方法执行,相当于动态代理的全过程

⑥@Order:设置切面的优先级,值越小优先级越高

⑦@Aspect:设置当前类为切面

⑧@Pointcut(value=“切入表达式”) : 指定切入表达式

1.6.5 code demo
//导包
<dependencies>
        <!--spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.19</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.1</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
//切面
@Aspect
@Component
public class aspectDemo {
    @Pointcut(value = "execution(void com.service.BookService.save())")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void method() {
        System.out.println(System.currentTimeMillis());
    }
}
//配置类
@Configuration
@ComponentScan("com")
@EnableAspectJAutoProxy
public class SpringConfig {
}
//接口
public interface BookService {
     void save();
}
//实现类
@Service
public class BookServiceImpl implements BookService {
//public class BookServiceImpl{
    @Override
    public void save() {
        System.out.println("save...");
    }
}
//test
public class aopTest {
    @Test
    public void test() {
        ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookService bookService = ioc.getBean(BookService.class);
        bookService.save();
    }
}
//执行结果
1654444898144
save...
1.6.6 切入点表达式

语法:execution(权限修饰符 返回值类型 全类名.类中的方法(形参列表))

其中*号表示任意目录、方法或者返回值,…表示任意参数,并且可以使用逻辑运算符||/&&/!/not

//语法:execution(权限修饰符 返回值类型 全类名.类中的方法(形参列表))
execution(* *.*(..))      为所有类的所有方法添加功能,)),其中*号表示任意目录、方法或者返回值,..表示任意参数
execution(* Calculator.add(..))||execution(* Calculator.sub(..))
要求Calculator中的所有add方法和所有sub方法增加功能
execution(* Calculator.*(..))&&!execution(* Calculator.add(..))
要求Calculator中的所有方法,将add方法排除
not execution(* Calculator.*(String,..))排除Calculator中方法,第一个参数是String,其他任意
!execution(* Calculator.*(String,..))

致谢:
本文AOP有参考https://blog.csdn.net/jc_hook/article/details/113743008
代理模式有参考http://c.biancheng.net/view/1359.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值