JAVA的代理模式 静态代理 jdk动态代理 cgilb动态代理 SpringBoot的切面

1.什么事代理模式

代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。

 2.租房为例子,理解带代理模式

来到新的城市我们想租房,但是不熟悉哪里有房子出租,租房平台上的二房东上就有很多房源,他们作为中介高价出租房子给我们,代理模式恰恰是这个思维,一手房东是被代理者, 中介是代理者 。

3.Java的代理模式实现方式

3.1 静态代理

通过代理类(HouseAgent )二手房东 去实现(Tenement)出租房子接口,达到代理的效果,缺点就是需要实现接口和创造出一个代理类。

/**
 * @Description 租房接口
 * @Version 1.0.0
 * @Date 2022/9/28 22:01
 * @Author NiKaBoy
 * @Email 123456789@qq.com
 */
public interface Tenement {

    /**
     * @Description:  出租接口
     * @Data:[houseDesc, price]
     * @return: void
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-271 22:16:57
     */
    void rent();
}

 

/**
 * @Description  二手房东 中介
 * @Version 1.0.0
 * @Date 2022/9/28 22:03
 * @Author NiKaBoy
 * @Email 123456789@qq.com
 */
public class HouseAgent implements Tenement{


    private Landlord landlord;

    public HouseAgent(Landlord landlord) {
        this.landlord = landlord;
    }

    /**
     * @Description:
     * @Data:[houseDesc, price]
     * @return: void
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-271 22:12:09
     */
    @Override
    public void rent() {
        landlord.setHouseDesc("豪华单人间,水电全免");
        landlord.setPrice(new BigDecimal(3000));
        landlord.rent();
    }
}
/**
 * @Description 房东
 * @Version 1.0.0
 * @Date 2022/9/28 22:02
 * @Author NiKaBoy
 * @Email 123456789@qq.com
 */
public class Landlord implements  Tenement{


    private String houseDesc ="普通单间人";
    private BigDecimal  price = new BigDecimal(1000);

    public void setHouseDesc(String houseDesc) {
        this.houseDesc = houseDesc;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    /**
     * @Description:  租售房子
     * @Data:[houseDesc, price]
     * @return: void
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-271 22:08:47
     */
    @Override
    public void rent(){
        System.out.println("市中心房子,"+this.houseDesc+"出租"+",每月"+this.price+"元");
    }

}
public static void main(String[] args) {
    HouseAgent houseAgent = new HouseAgent(new Landlord());
    houseAgent.rent();//市中心房子,豪华单人间,水电全免出租,每月3000元
}

3.2 JDK动态代理

使用静态代理的时候,我们需要手动去写一个代理类,而JDK的动态代理就可以帮我们生成代理对象,从而实现对代理,缺点需要实现接口,对类无法代理。

/**
 * @Description jdk 动态代理,会自动生成一个代理对象
 * @Version 1.0.0
 * @Date 2022/9/28 22:02
 * @Author NiKaBoy
 * @Email 123456789@qq.com
 */
public class JdkProxyHandler implements InvocationHandler {

    private Object target;

    public JdkProxyHandler(Object target) {
        this.target = target;
    }

    /***切面类**/
    private Aspect aspect;

    public void setAspect(Aspect aspect) {
        this.aspect = aspect;
    }


    /**
     * @Description: 调用方法
     * @Data:[proxy, method, args]
     * @return: java.lang.Object
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-273 22:35:19
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        if (aspect != null) {
            JoinPoint joinPoint = new JoinPoint();
            joinPoint.setMethodName(method.getName());
            joinPoint.setTarget(target);
            aspect.before(joinPoint);
            try {
                result = method.invoke(target, args);
            } catch (Exception e) {
                aspect.cast(joinPoint, e);
            }
            aspect.after(joinPoint);
            return result;
        }
        return method.invoke(target, args);
    }
}
public interface Tenement {

    /**
     * @Description:  出租接口
     * @Data:[houseDesc, price]
     * @return: void
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-271 22:16:57
     */
    void rent();
}
/**
 * @Description 切面顶级类
 * @Version 1.0.0
 * @Date 2022/9/30 21:53
 * @Author NiKaBoy
 * @Email 123456789@qq.com
 */
public abstract class Aspect {
    /**
     * @Description: 前置方法
     * @Data:[]
     * @return: void
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-273 21:54:46
     */
    public abstract void before(JoinPoint joinPoint);
    /**
     * @Description: 后置方法
     * @Data:[]
     * @return: void
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-273 21:57:00
     */
    public abstract void after(JoinPoint joinPoint);

    /**
     * @Description: 异常方法
     * @Data:[]
     * @return: void
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-273 21:57:27
     */
    public abstract void cast(JoinPoint joinPoint,Exception ex);



}
@Data
public class JoinPoint {
    /**实现的目标类**/
    private Object target;
    /**目标方法名**/
    private String methodName;
    /**目标方法的参数**/
    private Object [] args;

}
public class LandlordAspect extends  Aspect{
    @Override
    public void before(JoinPoint joinPoint) {
        Object target = joinPoint.getTarget();
        if(target instanceof Landlord){
            System.out.println("【前置方法】");
            Landlord landlord = (Landlord) target;

        }
    }

    @Override
    public void after(JoinPoint joinPoint) {
        Object target = joinPoint.getTarget();
        if(target instanceof Landlord){
            System.out.println("【后置方法】");
        }
    }

    @Override
    public void cast(JoinPoint joinPoint, Exception ex) {
        Object target = joinPoint.getTarget();
        if(target instanceof Landlord){
            System.out.println("【异常方法】"+ex);
        }
    }
}
public static void main(String[] args) {
    //被代理类
    Landlord landlord = new Landlord();
    JdkProxyHandler invocationHandler = new JdkProxyHandler(landlord);
    invocationHandler.setAspect(new LandlordAspect());
    Tenement tenement= (Tenement)Proxy.newProxyInstance(
            landlord.getClass().getClassLoader(),
            landlord.getClass().getInterfaces(),
            invocationHandler);

    tenement.rent();
}

3.3 CGLIB代理

jdk代理虽然帮我们生成了代理对象,但是它不能对类直接进行生成代理对象,需要存在一个接口,而cglib通过类就可以生成“代理对象”,不需要接口,当然也存在问题,对于代理方法是静态方法或者final修饰是无法实现拦截代理。

public class ProxyInterceptor implements MethodInterceptor {

    

    /**
     * 当对基于代理的方法回调时,拦截其方法,进行自定义处理
     *
     * @param target 代理对象
     * @param method 拦截的方法
     * @param args   拦截的方法的参数
     * @param proxy  代理
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if (target instanceof Landlord) {
            System.out.println("[前置处理]");
            args[0] = "豪华一室一厅";
            args[1] = new BigDecimal(3000);
        }
        Object o = proxy.invokeSuper(target, args);
        System.out.println("[后置处理]");
        return o;
    }
}
public class Landlord {

    /**
     * @Description:  租售房子
     * @Data:[houseDesc, price]
     * @return: void
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-271 22:08:47
     */
    public  void rent(String houseDesc,BigDecimal price){
        System.out.println("市中心房子,"+houseDesc+"出租"+",每月"+price+"元");
    }

}
public static void main(String[] args) {
    // 创建Enhancer类,用作实现Landlord类的代理
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Landlord.class);
    ProxyInterceptor proxyInterceptor = new ProxyInterceptor();
    enhancer.setCallback(proxyInterceptor);
    Landlord landlord =(Landlord) enhancer.create();
    landlord.rent("一室一厅",new BigDecimal(1000));
}

3.4使用hutool工具实现代理并切面

开发中使用到代理场景一般都是用于切面,对某个被代理对象进行拦截进行前置处理和后置处理,Spring的aop就是利用jdk和cglib两种方式实现切面,当然也可以使用一些封装好的工具类去实现切面,例如hutool工具。

public class  Landlord  {

    /**
     * @Description:  租售房子
     * @Data:[houseDesc, price]
     * @return: void
     * @Author: NiKaBoy
     * @Email: 123456789@qq.com
     * @Date: 22-09-271 22:08:47
     */
    public void rent(String houseDesc,BigDecimal price){
        System.out.println("市中心房子,"+houseDesc+"出租"+",每月"+price+"元");
    }

}
/**
 * @Description 重写hutool工具的简单代理类
 * @Version 1.0.0
 * @Date 2022/10/13 21:32
 * @Author NiKaBoy
 * @Email 123456789@qq.com
 */
public class LandlordAspect extends SimpleAspect {

    @Override
    public boolean before(Object target, Method method, Object[] args) {
        args[0] = "豪华一室一厅";
        args[1] = new BigDecimal(3000);
        return super.before(target, method, args);
    }

    @Override
    public boolean after(Object target, Method method, Object[] args, Object returnVal) {
        return super.after(target, method, args, returnVal);
    }

    @Override
    public boolean afterException(Object target, Method method, Object[] args, Throwable e) {
        return super.afterException(target, method, args, e);
    }
}
public static void main(String[] args) {
    Landlord proxy = ProxyUtil.proxy(new Landlord(), new LandlordAspect());
    proxy.rent("一室一卫",new BigDecimal(1000));

}

3.5 SpringBoot 项目使用代理

@Aspect
@Component
public class TestAspect {

    @Autowired
    private AspectServer aspectServer;
    /**
     * 注解的方式指定切面的切入点
     */
    @Pointcut("@annotation(com.nika.boy.aop.TestAnnotation)")
    public void pointcutAnnotation() {
    }

    /***
    *根据包路径方式切入点
    */
    @Pointcut("execution(* com.nikaboy.service.api.*.*(..))")
	public void pointcutAnnotation() {
	}  

    /**
     * 前置通知:执行目标方法前 触发
     */
    @Before(value = "pointcutAnnotation()")
    public void methodBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
       
      
    }
 
    /**
     * 后置通知:执行目标方法后触发
     */
    @After(value = "pointcutAnnotation()")
    public void methodAfter(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Son arg = (Son)args[0];
         System.out.println(":::"+arg.getName());
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[" + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
                + "]后置通知:方法名:" + methodName + ",参数" + Arrays.asList(joinPoint.getArgs()));



    }
 
    /**
     * 返回通知:目标方法执行完并返回参数后触发。
     */
    @AfterReturning(value = "pointcutAnnotation()", returning = "result")
    public void methodAfterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[" + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
                + "]返回通知:方法名:" + methodName + "," +
                "参数" + Arrays.asList(joinPoint.getArgs()) + "," +
                "返回结果:");
        if (result instanceof String[]) {
            Arrays.stream((String[]) result).forEach(System.out::println);
        } else {
            System.out.println(result);
        }
    }
 
    /**
     * 异常通知:目标方法抛出异常后触发
     */
    @AfterThrowing(value = "pointcutAnnotation()", throwing="ex")
    public void methodExceptionOccurred(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[" + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
                + "]异常通知:方法名:" + methodName + ",参数" + Arrays.asList(joinPoint.getArgs()) + ",异常信息:" + ex.getMessage());
        if(ex != null){
            ex = null;
        }
    }
    /**
     * 环绕通知,围绕着方法执行
     * 环绕通知需要携带ProceedingJoinPoint类型的参数
     * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
     * 而且环绕通知必须有返回值,返回值即为目标方法的返回值
     */
    @Around(value = "pointcutAnnotation()")
    public Object methodAround(ProceedingJoinPoint proceedingJoinPoint) {
        Object result = null;
        String methodName = proceedingJoinPoint.getSignature().getName();
        System.out.println("[" + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss")
                + "]环绕通知:方法名:" + methodName + ",参数" + Arrays.asList(proceedingJoinPoint.getArgs()));
        //执行目标方法
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        return result;
    }
/**
 * 测试切面注解
 */
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MethodInterceptor是CGLib提供的一个拦截器接口,它可以在执行目标方法前后进行一些额外的操作,比如记录日志、性能监控、事务管理等。下面是一个注解实现MethodInterceptor的例子。 首先定义一个注解@Interceptor,用来标识需要被拦截的方法: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Interceptor { } ``` 然后定义一个拦截器类MyInterceptor,实现MethodInterceptor接口: ```java public class MyInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { if (method.isAnnotationPresent(Interceptor.class)) { // 判断方法是否被注解@Interceptor标识 // 在目标方法执行前进行一些额外的操作 System.out.println("Before method invocation: " + method.getName()); // 执行目标方法 Object result = proxy.invokeSuper(obj, args); // 在目标方法执行后进行一些额外的操作 System.out.println("After method invocation: " + method.getName()); return result; } else { // 如果方法没有被注解@Interceptor标识,则直接执行目标方法 return proxy.invokeSuper(obj, args); } } } ``` 最后,在需要被拦截的方法上使用@Interceptor注解即可: ```java public class UserService { @Interceptor public void saveUser(User user) { // 保存用户信息 } } ``` 使用CGLib生成UserService的代理类,并调用代理类的saveUser方法: ```java public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); enhancer.setCallback(new MyInterceptor()); UserService userService = (UserService) enhancer.create(); userService.saveUser(new User()); } ``` 输出结果如下: ``` Before method invocation: saveUser After method invocation: saveUser ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值