设计模式-代理模式(1)-静态代理
文章目录
学习说明
之前学习总结的时候也是学习过代理模式的 , 但是记忆不是很深刻 , 这次复习一下 , 慢一点 , 准备深度学习一下代理 , 主要是动态代理
代理模式
代理模式应用很多,
spring aop 就是基于动态代理 ,
spring 事务也是基于动态代理 ,
springboot 的拦截器基于aop ,
aop基于动态代理 ,
mybatis 的 mapper 接口 也是基于动态代理==> 而且是 jdk 动态代理 ,
aspect 基于动态代理 。。。 等等
代理模式
- 静态代理
- 动态代理
- jdk 动态代理
- cglib 动态代理
静态代理的案例
目标接口
/**
明星接口类
*/
public interface Star {
/**
* 唱歌方法
*/
void sing();
}
目标类(被代理类)
package com.example.staticproxy;
/**
* 明星本人
*/
public class RealStar implements Star {
@Override
public void sing() {
System.out.println("明星本人开始唱歌……");
}
}
代理类
package com.example.staticproxy;
public class ProxyStar implements Star {
private Star star;
public ProxyStar(Star star){
this.star=star;
}
@Override
public void sing() {
System.out.println("代理先进行谈判……");
// 唱歌只能明星自己唱
this.star.sing();
System.out.println("演出完代理去收钱……");
}
}
测试
package com.example.staticproxy;
public class App {
public static void main(String[] args) {
Star star = new RealStar();
Star proxy = new ProxyStar(star);
proxy.sing();
}
}
springboot快速整合aop复习回顾
重点说明
说明:Spring boot2.x 中AOP现在默认使用的CGLIB代理,假如需要使用JDK动态代理可以在配置文件(applicatiion.properties)中进行如下配置:
spring.aop.proxy-target-class=false
探究原因 -> 对应的源码
AopAutoConfiguration.java
@Configuration(
proxyBeanMethods = false
)
@EnableAspectJAutoProxy(
proxyTargetClass = false
)
@ConditionalOnProperty(
prefix = "spring.aop",
name = {"proxy-target-class"},
havingValue = "false",
matchIfMissing = false
)
static class JdkDynamicAutoProxyConfiguration {
JdkDynamicAutoProxyConfiguration() {
}
}
}
static class AspectJAutoProxyingConfiguration {
AspectJAutoProxyingConfiguration() {
}
@Configuration(
proxyBeanMethods = false
)
@EnableAspectJAutoProxy(
proxyTargetClass = true
)
@ConditionalOnProperty(
prefix = "spring.aop",
name = {"proxy-target-class"},
havingValue = "true",
matchIfMissing = true
)
static class CglibAutoProxyConfiguration {
CglibAutoProxyConfiguration() {
}
}
导入以来
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
目标接口
package com.example;
public interface MyService {
public int sayHello();
}
被代理类(目标类)
package com.example;
import org.springframework.stereotype.Service;
@Service("myServiceImpl")
public class MyServiceImpl implements MyService{
@Override
public int sayHello(){
System.out.println("myservice 的 say hello 方法.....");
return Integer.MAX_VALUE;
}
}
切面类
package com.example;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
bean 用于匹配指定bean对象的方法
within 用于匹配指定包下所有类内的方法
execution 用于按指定语法规则匹配到具体方法
@annotation 用于匹配指定注解修饰的方法
*/
@Component
@Aspect
public class MyAop {
private static final Logger log = LoggerFactory.getLogger(MyAop.class);
@Pointcut("bean(myServiceImpl)")
public void myPointcut() {}
@Around("myPointcut()")
public Object around(ProceedingJoinPoint jp)
throws Throwable{
try {
log.info("start:"+System.currentTimeMillis());
Object result=jp.proceed();//调用下一个切面方法或目标方法
log.info("after:"+System.currentTimeMillis());
return result;
}catch(Throwable e) {
log.error(e.getMessage());
throw e;
}
}
}
测试
package com.example;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootDemoApplicationTests {
@Autowired
private MyService myService;
@Test
void contextLoads() {
System.out.println(myService.sayHello());
System.out.println("hello");
}
}
面试题
JDK动态代理和CGLIB动态代理的区别?
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
- JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
- 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ 的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而
Spring AOP则无需特定的编译器处理。
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):
proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。
解释一下Spring AOP里面的几个名词
**连接点不一定是切入点,但切入点一定是连接点**
(1) 切面(Aspect):切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。
(2) 连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
(3) 通知(Advice):在AOP术语中,切面的工作被称为通知。
(4) 切入点(Pointcut):切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
(5) 引入(Introduction):引入允许我们向现有类添加新方法或属性。
(6) 目标对象(Target Object): 被一个或者多个切面(aspect)所通知
(advise)的对象。它通常是一个代理对象。也有人把它叫做 被通知
(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。
(7) 织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入:
- 编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
- 类加载期:切面在目标类加载到JVM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
- 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP 容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。
静态代理和动态代理的区别
相同点:都是代理模式
静态代理:由程序员创建或者是由特定工具创建,在代码编译时就确定了被代理的类是一个静态代理。静态代理通常只代理一个类;
动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口下的多个实现类。
springboot使用的是jdk动态代理还是cglib动态代理
Spring boot2.x 中AOP现在默认使用的CGLIB代理,假如需要使用JDK动态代理可以在配置文件(applicatiion.properties)中进行如下配置:
,AOP 容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。
静态代理和动态代理的区别
相同点:都是代理模式
静态代理:由程序员创建或者是由特定工具创建,在代码编译时就确定了被代理的类是一个静态代理。静态代理通常只代理一个类;
动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口下的多个实现类。
springboot使用的是jdk动态代理还是cglib动态代理
Spring boot2.x 中AOP现在默认使用的CGLIB代理,假如需要使用JDK动态代理可以在配置文件(applicatiion.properties)中进行如下配置:
spring.aop.proxy-target-class=false