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 {
}