***模式详解(静态***,动态***,cglib)

静态***

***类的对象并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务

  • ***模式的UML图
  • ***类与具体类需集成自同一接口
  • perfomancetestsql为组合关系

引入问题:业务代码与监测代码的强耦合

long begin = System.currentTimeMillis(); 

query();

long end = System.currentTimeMillis();
long step = end - begin;
System.out.println("执行花费 :" + step);
复制代码

***模式:意味着有一方代替另一方完成一件事。这里,我们会编写两个类:TestSQL 为query 执行逻辑,Performance 为性能测试类。这里 Performance 会代替 TestSQL 去执行 query 逻辑。

// 接口
public interface IDatabase {
    void query();
}


public class TestSQL implements IDatabase {

    @Override
    public void query() {
        System.out.println("执行 query。。。。");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


// ***类
public class PerformanceMonitor implements IDatabase {
    TestSQL sql;

    public PerformanceMonitor(TestSQL sql) {
        this.sql = sql;
    }


    @Override
    public void query() {
        long begin = System.currentTimeMillis();

        // 业务逻辑。
        sql.query();

        long end = System.currentTimeMillis();
        long step = end - begin;
        System.out.println("执行花费 : " + step);
    }
}


// 测试代码
public class Main {
    public static void main(String[] strings) {
        TestSQL sql = new TestSQL();

        PerformanceMonitor performanceMonitor = new PerformanceMonitor(sql);
        // 由 Performance 代替 testSQL 执行
        performanceMonitor.query();
    }
}

复制代码

动态***

动态***的原理是利用Java反射在运行阶段动态生成任意类型的动态***类,这不仅简化了程序员的编程工作,也提高了系统的可扩展性,不管我们的业务接口和委托类如何变,***类都可以不变化

使用动态***需要使用到JDK提供的InvocationHandler接口和java.lang.reflect 包中的Proxy类

  • 模拟用户的登录计数
// 接口
public interface User {
    /**
     * 登录
     * @param name 用户名
     * @param pwd 密码
     * @return
     */
    public boolean login(String username, String pwd);

    /**
     * 退出
     */
    public void logout(String username);
}

// 实现类
public class UserImpl implements User{

    @Override
    public boolean login(String username, String pwd) {
        // 简化问题,直接登录成功
        System.out.println(username+" 登录成功.");
        return true;
    }

    @Override
    public void logout(String username) {
        System.out.println(username+" 成功退出.");
    }

}
复制代码
  • 添加一个***类,真实的登录操作和退出操作还是在UserImpl中进行,但在***对象中,我们还进行一点其他的操作,比如统计一下登录成功的次数
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/** * 继承动态***接口的***类 */
public class UserDynamicProxy implements InvocationHandler{

    // 在线人数
    public static int count = 0;
    // 委托对象
    private Object target; 

    /** * 返回***对象 * @param target * @return */
    @SuppressWarnings("unchecked")
    public <T> T getProxyInstance(Object target) {
        // 委托对象,真正的业务对象
        this.target = target;
        // 获取Object类的ClassLoader
        ClassLoader cl = target.getClass().getClassLoader();
        // 获取接口数组
        Class<?>[] cs = target.getClass().getInterfaces();
        // 获取***对象并返回
        return (T)Proxy.newProxyInstance(cl, cs, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object r = null;
        // 执行之前
        r = method.invoke(target, args);
        // 判断如果是登录方法
        if("login".equals(method.getName())) {
            if((boolean)r == true) {
                // 当前在线人数+1
                count += 1;
            }
        } 
        // 判断如果是退出方法
        else if("logout".equals(method.getName())) {
            // 当前在线人数-1
            count -= 1;
        }
        showCount(); // 输出在线人数
        // 执行之后
        return r;
    }

    /** * 输出在线人数 */
    public void showCount() {
        System.out.println("当前在线人数:"+count+" 人.");
    }

}

复制代码

getProxyInstance是返回一个***对象,通过这个***对象可以做与原委托对象一样的事,因为他们都是实现的同一接口

public class Main {

    public static void main(String[] args) {
        // 真实角色,委托人
        User user = new UserImpl();    // 可执行真正的登录退出功能

        // ***类
        UserDynamicProxy proxy = new UserDynamicProxy();

        // 获取委托对象user的***对象
        User userProxy = proxy.getProxyInstance(user);

        // 系统运行,用户开始登录退出
        userProxy.login("小明", "111");
        userProxy.login("小红", "111");
        userProxy.login("小刚", "111");
        userProxy.logout("小明");
        userProxy.logout("小刚");
        userProxy.login("小黄", "111");
        userProxy.login("小黑", "111");
        userProxy.logout("小黄");
        userProxy.login("小李", "111");
        userProxy.logout("小李");
        userProxy.logout("小黄");
        userProxy.logout("小红");
    }
}

复制代码

Cglib实现的动态***

  • 问题引入:JDK实现的动态***需要委托对象必须是继承了接口的,那如果我们的委托类就是一个类,没有继承哪个接口怎么办?
public class UserManage {

    public boolean login(String username, String pwd) {
        // 简化问题,直接登录成功
        System.out.println(username+" 登录成功.");
        return true;
    }

    public void logout(String username) {
        System.out.println(username+" 成功退出.");
    }
}

复制代码

Cglib是针对类来实现***的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行***。

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class UserCglibProxy implements MethodInterceptor {
    // 在线人数
    public static int count = 0;
    // 委托类对象
    private Object target;  

    /** 
     * 创建***对象 
     * @param target 
     * @return 
     */  
    @SuppressWarnings("unchecked")
    public <T>T getProxyInstance(Object target) {  
        this.target = target;  
        // 增强类对象
        Enhancer enhancer = new Enhancer();  
        // 设置其超类为target的类类型
        // 使用的是继承方式
        enhancer.setSuperclass(this.target.getClass());  
        // 回调方法  
        enhancer.setCallback(this);  
        // 创建***对象  
        return (T)enhancer.create();  
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object r = null;
        // 执行之前
        r = proxy.invokeSuper(obj, args);
        // 判断如果是登录方法
        if("login".equals(method.getName())) {
            if((boolean)r == true) {
                // 当前在线人数+1
                count += 1;
            }
        } 
        // 判断如果是退出方法
        else if("logout".equals(method.getName())) {
            // 当前在线人数-1
            count -= 1;
        }
        showCount(); // 输出在线人数
        // 执行之后
        return r;
    }

    /**
     * 输出在线人数
     */
    public void showCount() {
        System.out.println("当前在线人数:"+count+" 人.");
    }

}
复制代码
  • 场景
public class Main2 {

    public static void main(String[] args) {
        // 委托类对象
        UserManage userManage = new UserManage();

        // ***类
        UserCglibProxy proxy = new UserCglibProxy();

        // 获取委托对象user的***对象
        UserManage userProxy = proxy.getProxyInstance(userManage);

        // 系统运行,用户开始登录退出
        userProxy.login("小明", "111");
        userProxy.login("小红", "111");
        userProxy.login("小刚", "111");
        userProxy.logout("小明");
        userProxy.logout("小刚");
        userProxy.login("小黄", "111");
        userProxy.login("小黑", "111");
        userProxy.logout("小黄");
        userProxy.login("小李", "111");
        userProxy.logout("小李");
        userProxy.logout("小黄");
        userProxy.logout("小红");
    }
}

复制代码
全部评论

相关推荐

不愿透露姓名的神秘牛友
11-27 10:48
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务