✅设计模式笔记

👌单例模式

饿汉式:在类初始化时创建对象,无论你现在是不是需要这个对象。

懒汉式:在你调用可以获取这个类的对象的方法 时,才创建这个对象,即延迟创建对象。

案例1:饿汉式(静态常量(实例))
复制代码
public class Singleton {
    public static final Singleton INSTANCE = new Singleton();

    private Singleton() {}
}

在类中,创建一个public static final修饰的对象实例

私有化构造函数限制实例的创建权限

复制代码
// 使用 Singleton 的唯一实例
Singleton instance = Singleton.INSTANCE;

类名.静态常量(static 修饰的成员变量叫类变量也叫静态变量 fianl修饰的叫常量)

案例2:饿汉式(静态方法)
复制代码
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

在类中创建一个获取类对象的静态方法来获取对象实例

复制代码
// 获取单例实例(通过方法)
Singleton instance = Singleton.getInstance();

类名.方法名(静态方法)

案例3:饿汉式(枚举式)
复制代码
public enum Singleton{
    INSTANCE
}

// 获取唯一实例
Singleton instance = Singleton.INSTANCE;

枚举常量

案例4:懒汉式(同步方法)
复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton(){}

    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

在类中创建synchronized修饰的静态方法获取对象实例,而且要在方法中判断对象是否为null,如果为null才创建对象

案例5:懒汉式(同步代码块+双重条件)
复制代码
public class Singleton {
    private static Singleton instance;

    private Singleton(){}

    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

在类中创建一个静态方法,判断对象是否为null,为null

加本地锁,再一次判断对象是否为null,为null才创建对象

如果两个线程都调用获取类对象的方法,同时都会判对象为null,本地锁都能获取到,

如果某个线程进入,对象为null,就创建第一个对象,另一个线程也进进入判断对象是否为null,此时

对象不为null.则不会新建对象。

案例6:懒汉式(静态内部类)
复制代码
public class Singleton {
    private Singleton(){}
    private static class Inner{
        static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){
        return Inner.INSTANCE;
    }
}

在一个类中创建一个静态内部类,通过静态内部类获取类的对象的静态方法来获取对象实例

案例7:Spring Bean注册到容器

只需要把Bean注入到容器中,默认就是单例的,而且还是线程安全的单例模式

复制代码
@AutoConfiguration
@EnableConfigurationProperties(AiminCacheProperties.class)
public class AiminCacheAutoConfiguration {

    @Bean
    public CacheManager cacheManager(AiminCacheProperties aiminCacheProperties, RedisTemplate<String,Object> redisTemplate){
        return new AiminCacheManager(redisTemplate,aiminCacheProperties);
    }

    @Bean
    public L2Cache getInstance(RedisTemplate<String, Object> redisTemplate, AiminCacheProperties aiminCacheProperties) {
        return  new L2Cache(redisTemplate, aiminCacheProperties);
    }
}

👌工厂+策略1(简单工厂模式八股)

复制代码
/**
 * 咖啡接口
 */
public interface Coffee {

    /**
     *  获取名字
     * @return
     */
    public String getName();

    /**
     * 加牛奶
     */
    public void addMilk();

    /**
     * 加糖
     */
    public void addSuqar();
}

/**
 * 拿铁咖啡
 */
public class LatteCoffee implements Coffee {
    /**
     *  获取名字
     * @return
     */
    @Override
    public String getName() {
        return "latteCoffee";
    }

    /**
     * 加牛奶
     */
    @Override
    public void addMilk() {
        System.out.println("LatteCoffee...addMilk...");
    }

    /**
     * 加糖
     */
    @Override
    public void addSuqar() {
        System.out.println("LatteCoffee...addSuqar...");
    }
}

/**
 * 美式咖啡
 */
public class AmericanCoffee implements Coffee {

    /**
     *  获取名字
     * @return
     */
    @Override
    public String getName() {
        return "americanCoffee";
    }

    /**
     * 加牛奶
     */
    @Override
    public void addMilk() {
        System.out.println("AmericanCoffee...addMilk...");
    }

    /**
     * 加糖
     */
    @Override
    public void addSuqar() {
        System.out.println("AmericanCoffee...addSuqar...");
    }
}

/**
 * 咖啡店类
 */
public class CoffeeStore {

    public static void main(String[] args) {
        Coffee coffee = orderCoffee("latte");
        System.out.println(coffee.getName());
    }
    /**
     * 根据类型选择不同的咖啡
     * @param type
     * @return
     */
    public static Coffee orderCoffee(String type){
        Coffee coffee = null;
        if("american".equals(type)){
            coffee = new AmericanCoffee();
        }else if ("latte".equals(type)){
            coffee = new LatteCoffee();
        }
        //添加配料
        coffee.addMilk();
        coffee.addSuqar();
        return coffee;
    }
}

👌工厂+策略2(工厂方法模式八股)

复制代码
public interface Coffee {

    public String getName();

    public void addMilk();

    public void addSuqar();
}

public class LatteCoffee implements Coffee {
    @Override
    public String getName() {
        return "latteCoffee";
    }

    @Override
    public void addMilk() {
        System.out.println("LatteCoffee...addMilk...");
    }

    @Override
    public void addSuqar() {
        System.out.println("LatteCoffee...addSuqar...");
    }
}

public class AmericanCoffee implements Coffee {
    @Override
    public String getName() {
        return "americanCoffee";
    }

    @Override
    public void addMilk() {
        System.out.println("AmericanCoffee...addMilk...");
    }

    @Override
    public void addSuqar() {
        System.out.println("AmericanCoffee...addSuqar...");
    }
}

public class SimpleCoffeeFactory {

    public static Coffee createCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        return coffee;
    }
}

public class CoffeeStore {

    public static void main(String[] args) {

        Coffee coffee = orderCoffee("latte");
        System.out.println(coffee.getName());

    }


    public static Coffee orderCoffee(String type) {
        //通过工厂获得对象,不需要知道对象实现的细节
        SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
        Coffee coffee = factory.createCoffee(type);
        //添加配料
        coffee.addMilk();
        coffee.addSuqar();
        return coffee;
    }
}

👌工厂+策略3(抽象工厂模式八股)

复制代码
public interface Coffee {

    public String getName();

    public void addMilk();

    public void addSuqar();
}

public class LatteCoffee implements Coffee {
    @Override
    public String getName() {
        return "latteCoffee";
    }

    @Override
    public void addMilk() {
        System.out.println("LatteCoffee...addMilk...");
    }

    @Override
    public void addSuqar() {
        System.out.println("LatteCoffee...addSuqar...");
    }
}

public class AmericanCoffee implements Coffee {
    @Override
    public String getName() {
        return "americanCoffee";
    }

    @Override
    public void addMilk() {
        System.out.println("AmericanCoffee...addMilk...");
    }

    @Override
    public void addSuqar() {
        System.out.println("AmericanCoffee...addSuqar...");
    }
}

抽象工厂:

复制代码
/**
 * 咖啡工厂接口
 */
public interface CoffeeFactory {

    /**
     * 创建咖啡
     * @return
     */
    public Coffee createCoffee();
}

具体工厂:返回对应的实现类

复制代码
/**
 * 拿铁咖啡工厂类
 */
public class LatteCoffeeFactory implements CoffeeFactory {

    /**
     * 创建拿铁咖啡
     * @return
     */
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

/**
 * 美式咖啡工厂类
 */
public class AmericanCoffeeFactory implements CoffeeFactory {

    /**
     * 创建美式咖啡
     * @return
     */
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

public class CoffeeStore {

    public static void main(String[] args) {
        //可以根据不同的工厂,创建不同的产品
        CoffeeStore coffeeStore = new CoffeeStore(new LatteCoffeeFactory());
        Coffee latte = coffeeStore.orderCoffee();
        System.out.println(latte.getName());
    }

    private CoffeeFactory coffeeFactory;

    public CoffeeStore(CoffeeFactory coffeeFactory){
        this.coffeeFactory = coffeeFactory;
    }


    public Coffee orderCoffee(){
        Coffee coffee = coffeeFactory.createCoffee();
        //添加配料
        coffee.addMilk();
        coffee.addSuqar();
        return coffee;
    }
}

👌策略(八股)

复制代码
/**
 * 出行策略接口
 */
public interface TravelStrategy {

    /**
     * 出行方式
     */
    public void travel();
}

/**
 * 自行车策略类
 */
public class Bicycle implements TravelStrategy{

    @Override
    public void travel() {
        System.out.println("选择自行车出行...");
    }
}

/**
 * 汽车策略类
 */
public class Car implements TravelStrategy{
    @Override
    public void travel() {
        System.out.println("选择汽车出行...");
    }
}

/**
 * 火车策略类
 */
public class Train implements TravelStrategy{
    @Override
    public void travel() {
        System.out.println("选择火车出行...");
    }
}

/**
 * 策略环境类
 * 用户连接上下文
 */
public class TravelContext {

    private TravelStrategy travelStrategy;

    public TravelContext(TravelStrategy travelStrategy){
        this.travelStrategy = travelStrategy;
    }

    public void selectTravel(){
        this.travelStrategy.travel();
    }


    public static void main(String[] args) {

        TravelContext travelContext = new TravelContext(new Car());

        travelContext.selectTravel();
    }
}

public class StrategyDemo {
    public static void main(String[] args) {

        // 场景1:春节促销,销售员使用 StrategyA
        System.out.println("=== 春节大促 ===");
        Strategy festivalStrategy = new StrategyA();  // 创建春节策略
        SalesMan salesman1 = new SalesMan(festivalStrategy);
        salesman1.salesManShow();  // 输出:买一送一

        // 场景2:中秋节促销,销售员使用 StrategyB
        System.out.println("\n=== 中秋大促 ===");
        SalesMan salesman2 = new SalesMan(new StrategyB()); // 直接注入
        salesman2.salesManShow();  // 输出:满200元减50元

        // 场景3:圣诞节促销,销售员使用 StrategyC
        System.out.println("\n=== 圣诞大促 ===");
        SalesMan salesman3 = new SalesMan(new StrategyC());
        salesman3.salesManShow();  // 输出:满1000元加一元换购任意200元以下商品

        // 高级用法:运行时动态切换策略
        System.out.println("\n=== 销售员临时更换策略 ===");
        SalesMan flexibleSalesman = new SalesMan(new StrategyA());
        flexibleSalesman.salesManShow(); // 先展示春节活动

        // 临时改为圣诞活动(注意:由于是构造函数注入,这里需要重新创建 SalesMan)
        // 如果想运行时动态切换,可以改用"设值注入"(Setter Injection),但当前设计是不可变的。
        flexibleSalesman = new SalesMan(new StrategyC());
        flexibleSalesman.salesManShow(); // 现在展示圣诞活动
    }
}

👌工厂+策略(八股)

复制代码
/**
 * 抽象策略类
 */
public interface UserGranter{


    /**
     * 获取数据
     * @param loginReq 传入的参数
     *        0:账号密码
     *         1:短信验证
     *        2:微信授权
     * @return map值
     */
    LoginResp login(LoginReq loginReq);

}

/**
 *策略:账号登录
 */
@Component
public class AccountGranter implements UserGranter{

    @Override
    public LoginResp login(LoginReq loginReq) {
       System.out.println("策略:登录方式为账号登录");
       // TODO
       // 执行业务操作

       return new LoginResp();
    }

}

/**
 * 策略:短信登录
 */
@Component
public class SmsGranter implements UserGranter{

    @Override
    public LoginResp login(LoginReq loginReq)  {
       System.out.println("策略:登录方式为短信登录");
       // TODO
       // 执行业务操作

       return new LoginResp();
    }

}

/**
 * 策略:微信登录
 */
@Component
public class WeChatGranter implements UserGranter{

    @Override
    public LoginResp login(LoginReq loginReq)  {
        System.out.println("策略:登录方式为微信登录");
        // TODO
        // 执行业务操作

        return new LoginResp();
    }
}

@Data
public class LoginResp{
    private Integer userId;
    private String userName;
    private String roleCode;
    private String token; //jwt令牌
    private boolean success;

}

@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "login")
public class LoginTypeConfig {

    private Map<String,String> types;

}

login:
  types:
    account: accountGranter
    sms: smsGranter
    we_chat: weChatGranter

package com.itheima.strategy;

import com.itheima.config.LoginTypeConfig;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 操作策略的上下文环境类 工具类
 * 将策略整合起来 方便管理
 */
@Component
public class UserLoginFactory implements ApplicationContextAware {

    private static Map<String, UserGranter> granterPool = new ConcurrentHashMap<>();

    @Autowired
    private LoginTypeConfig loginTypeConfig;

    /**
     * 从配置文件中读取策略信息存储到map中
     * {
     * account:accountGranter,
     * sms:smsGranter,
     * we_chat:weChatGranter
     * }
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //获取所有策略  y对应bean的名称 例如:AccountGranter 类的默认 Bean 名称就是 accountGranter
        loginTypeConfig.getTypes().forEach((k, y) -> {
            granterPool.put(k, (UserGranter) applicationContext.getBean(y));
        });
    }
    //这里也可以使用@Postcut作为初始化方法( implements ApplicationContextAware)

    /**
     * 对外提供获取具体策略
     *
     * @param grantType 用户的登录方式,需要跟配置文件中匹配
     * @return 具体策略
     */
    public UserGranter getGranter(String grantType) {
        UserGranter tokenGranter = granterPool.get(grantType);
        return tokenGranter;
    }


}

@Data
public class LoginReq {

    private String name;
    private String password;

    private String phone;
    private String validateCode;//手机验证码

    private String wxCode;//用于微信登录
    /**
     * account : 用户名密码登录
     * sms : 手机验证码登录
     * we_chat : 微信登录
     */
    private String type;
}

@RestController
@RequestMapping("/api/user")
@Slf4j
public class LoginController {

    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public LoginResp login(@RequestBody LoginReq loginReq) throws InterruptedException {
        if(loginReq.getType().equals("abc")){
            log.error("没有这种登录方式:{}",loginReq.getType());
        }
        if(loginReq.getType().equals("123")){
            throw new RuntimeException("错误的登录方式");
        }

        return userService.login(loginReq);
    }
}

@Service
public class UserService {

    @Autowired
    private UserLoginFactory factory;

    public LoginResp login(LoginReq loginReq){

        UserGranter granter = factory.getGranter(loginReq.getType());
        if(granter == null){
            LoginResp loginResp = new LoginResp();
            loginResp.setSuccess(false);
            return loginResp;
        }
        LoginResp loginResp = granter.login(loginReq);
        return loginResp;

        /*if(loginReq.getType().equals("account")){
            System.out.println("用户名密码登录");

            //执行用户密码登录逻辑

            return new LoginResp();

        }else if(loginReq.getType().equals("sms")){
            System.out.println("手机号验证码登录");

            //执行手机号验证码登录逻辑

            return new LoginResp();
        }else if (loginReq.getType().equals("we_chat")){
            System.out.println("微信登录");

            //执行用户微信登录逻辑

            return new LoginResp();
        }
        LoginResp loginResp = new LoginResp();
        loginResp.setSuccess(false);
        System.out.println("登录失败");
        return loginResp;*/


    }
}

👌工厂+策略(实战1)

复制代码
public interface SubjectTypeHandler {
    /**
     * 枚举身份的标识
     */
    SubjectInfoTypeEnum getHandlerType();

    /**
     * 实际的题目的插入
     */
    void add(SubjectInfoBO subjectInfoBO);

    /**
     * 实际的题目的详情
     */
    SubjectOptionBO query(int subjectId);
}

/**
 * 简答题目的策略类
 */
@Component
public class BriefTypeHandler implements SubjectTypeHandler{

    @Resource
    private SubjectBriefService subjectBriefService;

    //返回枚举值,枚举常量
    @Override
    public SubjectInfoTypeEnum getHandlerType() {
        return SubjectInfoTypeEnum.BRIEF;
    }

    /**
     * 简答题插入
     * @param subjectInfoBO
     */
    @Override
    public void add(SubjectInfoBO subjectInfoBO) {
        //简答题插入 个人觉得不是集合是因为只有一个选项
        SubjectBrief subjectBrief = BriefSubjectConverter.INSTANCE.convertBoToEntity(subjectInfoBO);
        subjectBrief.setSubjectId(subjectInfoBO.getId().intValue());
        subjectBrief.setIsDeleted(IsDeletedFlagEnum.NO_DELETE.getCode());
        subjectBriefService.insert(subjectBrief);
    }

    /**
     * 简答题查询
     * @param subjectId
     * @return
     */
    @Override
    public SubjectOptionBO query(int subjectId) {
        SubjectBrief subjectBrief = new SubjectBrief();
        subjectBrief.setSubjectId(subjectId);
        SubjectBrief result = subjectBriefService.queryByCondition(subjectBrief);
        SubjectOptionBO subjectOptionBO = new SubjectOptionBO();
        subjectOptionBO.setSubjectAnswer(result.getSubjectAnswer());
        return subjectOptionBO;
    }
}

/**
 * 判断题目的策略类
 */
@Component
public class JudgeTypeHandler implements SubjectTypeHandler{

    @Resource
    private SubjectJudgeService subjectJudgeService;
    @Override
    public SubjectInfoTypeEnum getHandlerType() {
        return SubjectInfoTypeEnum.JUDGE;
    }

    /**
     * 判断题插入
     * @param subjectInfoBO
     */
    @Override
    public void add(SubjectInfoBO subjectInfoBO) {
        //判断题目的插入 个人觉得不是集合是因为只有一个选项
        SubjectJudge subjectJudge = new SubjectJudge();
        SubjectAnswerBO subjectAnswerBO = subjectInfoBO.getOptionList().get(0);
        subjectJudge.setSubjectId(subjectInfoBO.getId());
        subjectJudge.setIsCorrect(subjectAnswerBO.getIsCorrect());
        subjectJudge.setIsDeleted(IsDeletedFlagEnum.NO_DELETE.getCode());
        subjectJudgeService.insert(subjectJudge);
    }

    /**
     * 判断题查询
     * @param subjectId
     * @return
     */
    @Override
    public SubjectOptionBO query(int subjectId) {
        SubjectJudge subjectJudge = new SubjectJudge();
        subjectJudge.setSubjectId(Long.valueOf(subjectId));
        List<SubjectJudge> result = subjectJudgeService.queryByCondition(subjectJudge);
        List<SubjectAnswerBO> subjectAnswerBOS = JudgeSubjectConverter.INSTANCE.convertEntityToBoList(result);
        SubjectOptionBO subjectOptionBO = new SubjectOptionBO();
        subjectOptionBO.setOptionList(subjectAnswerBOS);
        return subjectOptionBO;
    }
}

/**
 * 多选题目的策略类
 */
@Component
public class MultipleTypeHandler implements SubjectTypeHandler{

    @Resource
    private SubjectMultipleService subjectMultipleService;
    @Override
    public SubjectInfoTypeEnum getHandlerType() {
        return SubjectInfoTypeEnum.MULTIPLE;
    }

    /**
     * 多选题插入
     * @param subjectInfoBO
     */
    @Override
    public void add(SubjectInfoBO subjectInfoBO) {
        //多选题插入 个人觉得是集合是因为有多个选项
        List<SubjectMultiple> subjectMultipleList = new LinkedList<>();
        subjectInfoBO.getOptionList().forEach(option ->{
            SubjectMultiple subjectMultiple = MultipleSubjectConverter.INSTANCE.convertBoToEntity(option);
            subjectMultiple.setSubjectId(subjectInfoBO.getId());
            subjectMultiple.setIsDeleted(IsDeletedFlagEnum.NO_DELETE.getCode());
            subjectMultipleList.add(subjectMultiple);
        });
        subjectMultipleService.batchInsert(subjectMultipleList);
    }

    /**
     * 多选题查询
     * @param subjectId
     * @return
     */
    @Override
    public SubjectOptionBO query(int subjectId) {
        SubjectMultiple subjectMultiple = new SubjectMultiple();
        subjectMultiple.setSubjectId(Long.valueOf(subjectId));
        List<SubjectMultiple> result = subjectMultipleService.queryByCondition(subjectMultiple);
        List<SubjectAnswerBO> subjectAnswerBOS = MultipleSubjectConverter.INSTANCE.convertEntityToBoList(result);
        SubjectOptionBO subjectOptionBO = new SubjectOptionBO();
        subjectOptionBO.setOptionList(subjectAnswerBOS);
        return subjectOptionBO;
    }
}

/**
 * 单选题目的策略类
 */
@Slf4j
@Component
public class RadioTypeHandler implements SubjectTypeHandler{

    @Resource
    private SubjectRadioService subjectRadioService;
    @Override
    public SubjectInfoTypeEnum getHandlerType() {
        return SubjectInfoTypeEnum.RADIO;
    }

    /**
     * 单选题插入
     * @param subjectInfoBO
     */
    @Override
    public void add(SubjectInfoBO subjectInfoBO) {
        //单选题目的插入单选题目  个人觉得是集合是因为有多个选项
        List<SubjectRadio> subjectRadioList = new LinkedList<>();
        if (log.isInfoEnabled()){
            if (subjectInfoBO.getOptionList()==null){
                log.info("subjectInfoBO.getOptionList()==null",subjectInfoBO.getOptionList());
            }
        }
        subjectInfoBO.getOptionList().forEach(option ->{
            SubjectRadio subjectRadio = RadioSubjectConverter.INSTANCE.convertBoToEntity(option);
            subjectRadio.setSubjectId(subjectInfoBO.getId());
            subjectRadio.setIsDeleted(IsDeletedFlagEnum.NO_DELETE.getCode());
            subjectRadioList.add(subjectRadio);
        });
        subjectRadioService.batchInsert(subjectRadioList);
    }

    /**
     * 单选题查询
     * @param subjectId
     * @return
     */
    @Override
    public SubjectOptionBO query(int subjectId) {
        SubjectRadio subjectRadio = new SubjectRadio();
        subjectRadio.setSubjectId(Long.valueOf(subjectId));
        List<SubjectRadio> result = subjectRadioService.queryByCondition(subjectRadio);
        List<SubjectAnswerBO> subjectAnswerBOS = RadioSubjectConverter.INSTANCE.convertEntityToBoList(result);
        SubjectOptionBO subjectOptionBO = new SubjectOptionBO();
        subjectOptionBO.setOptionList(subjectAnswerBOS);
        return subjectOptionBO;
    }
}

/**
 * 题目类型工厂
 */
@Component
public class SubjectTypeHandlerFactory implements InitializingBean {

    //四个实现类
    @Resource
    private List<SubjectTypeHandler> subjectTypeHandlerList;

    //SubjectInfoTypeEnum枚举的某个对象如 RADIO(1, "单选")
    private Map<SubjectInfoTypeEnum,SubjectTypeHandler> handlerMap = new HashMap<>();

    //工厂提供实现类出去,从Map中拿SubjectTypeHandler
    public SubjectTypeHandler getHandler(int subjectType){
        //枚举中的静态方法获取枚举对象如 RADIO(1, "单选")
        SubjectInfoTypeEnum subjectInfoTypeEnum = SubjectInfoTypeEnum.getByCode(subjectType);
        return handlerMap.get(subjectInfoTypeEnum);
    }

    //handlerMap是空的,bean加载完后执行的方法
    @Override
    public void afterPropertiesSet() throws Exception {
        for (SubjectTypeHandler subjectTypeHandler : subjectTypeHandlerList) {
            //subjectTypeHandler.getHandlerType()枚举的某个常量 subjectTypeHandler对应的实现类
            handlerMap.put(subjectTypeHandler.getHandlerType(),subjectTypeHandler);
        }
    }
}

SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfo.getSubjectType());
subjectInfoBO.setId(subjectInfo.getId());
handler.add(subjectInfoBO);

👌工厂+策略(实战2)

复制代码
public interface WxChatMsgHandler {
    //消息枚举类型
    WxChatMsgTypeEnum getMsgType();

    //发送的内容类型
    String dealMsg(Map<String, String> messageMap);
}

@Component
@Slf4j
public class SubscribeMsgHandler implements WxChatMsgHandler{
    @Override
    public WxChatMsgTypeEnum getMsgType() {
        return WxChatMsgTypeEnum.SUBSCRIBE;
    }

    @Override
    public String dealMsg(Map<String, String> messageMap) {
        log.info("触发用户关注事件");
        String toUserName = messageMap.get("ToUserName");//公众号
        String fromUserName = messageMap.get("FromUserName");//个人号
        String subscribeContent  = "感谢您的关注,我是叮咚!欢迎您来到叮咚club世界mu~a";
        String content = "<xml>\n" +
                "  <ToUserName><![CDATA["+fromUserName+"]]></ToUserName>\n" +
                "  <FromUserName><![CDATA["+toUserName+"]]></FromUserName>\n" +
                "  <CreateTime>12345678</CreateTime>\n" +
                "  <MsgType><![CDATA[text]]></MsgType>\n" +
                "  <Content><![CDATA["+subscribeContent+"]]></Content>\n" +
                "</xml>";
        return content;
    }
}

@Component
@Slf4j
public class ReceiveTextMsgHandler implements WxChatMsgHandler{

    private static final String KEY_WORD = "验证码";

    private static final String LOGIN_PREFIX = "loginCode";

    @Resource
    private RedisUtil redisUtil;

    @Override
    public WxChatMsgTypeEnum getMsgType() {
        return WxChatMsgTypeEnum.TEXT_MSG;
    }

    @Override
    public String dealMsg(Map<String, String> messageMap) {
        log.info("接收到文本消息事件");
        String content = messageMap.get("Content");//获取用户输入的内容
        if (!KEY_WORD.equals(content)){
            return "";
        }
        String toUserName = messageMap.get("ToUserName");//公众号
        String fromUserName = messageMap.get("FromUserName");//个人号
        Random random = new Random();
        int num = random.nextInt(1000);
        //用验证码作为key
        String numKey = redisUtil.buildKey(LOGIN_PREFIX,String.valueOf(num));
        //openId作为value并设置过期时间
        redisUtil.setNx(numKey,fromUserName,5L, TimeUnit.MINUTES);
        String numContent  = "您当前的验证码是: "+ num +" " +"5分钟内有效";
        String replyContent = "<xml>\n" +
                "  <ToUserName><![CDATA["+fromUserName+"]]></ToUserName>\n" +
                "  <FromUserName><![CDATA["+toUserName+"]]></FromUserName>\n" +
                "  <CreateTime>12345678</CreateTime>\n" +
                "  <MsgType><![CDATA[text]]></MsgType>\n" +
                "  <Content><![CDATA["+numContent+"]]></Content>\n" +
                "</xml>";
        return replyContent;

    }
}

@Component
public class WxChatMsgFactory implements InitializingBean {

    //实现类
    @Resource
    private List<WxChatMsgHandler> wxChatMsgHandlerList;

    //WxChatMsgTypeEnum 枚举的某个对象   WxChatMsgHandler实现类
    private Map<WxChatMsgTypeEnum, WxChatMsgHandler> handlerMap = new HashMap<>();

    /**
     * 通过枚举类型获取到对应的实现类
     * @param msgType
     * @return
     */
    public WxChatMsgHandler getHandlerByMsgType(String msgType){
        //调用枚举类里的静态方法,获取对应的枚举对象
        WxChatMsgTypeEnum msgTypeEnum = WxChatMsgTypeEnum.getByMsgType(msgType);
        return handlerMap.get(msgTypeEnum);
    }


    /**
     * 初始化bean后的操作  将枚举对象K,实现类V存入到HashMap
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        for (WxChatMsgHandler wxChatMsgHandler : wxChatMsgHandlerList) {
            handlerMap.put(wxChatMsgHandler.getMsgType(),wxChatMsgHandler);
        }
    }
}

@RestController
@Slf4j
public class CallBackController {


    private static final String token = "adwidhaidwoaid";

    @Resource
    private WxChatMsgFactory wxChatMsgFactory;

    @RequestMapping("/test")
    public String test(){
        return "hello world";
    }

    /**
     * 回调消息校验
     */
    @GetMapping("/callback")
    public String callback(@RequestParam("signature") String signature,
                           @RequestParam("timestamp") String timestamp,
                           @RequestParam("nonce") String nonce,
                           @RequestParam("echostr") String echostr){
        log.info("get验签请求参数:signature:{},timestamp:{},nonce:{},echostr:{}",
                signature,timestamp,nonce,echostr);
        String shaStr = SHA1.getSHA1(token, timestamp, nonce, "");
        if (signature.equals(shaStr)){
            return echostr;
        }
        return "unknown";
    }

    @PostMapping(value = "/callback", produces = "application/xml;charset=UTF-8")
    public String callback(
                         @RequestBody String requestBody,
                         @RequestParam("signature") String signature,
                         @RequestParam("timestamp") String timestamp,
                         @RequestParam("nonce") String nonce,
                         @RequestParam(value = "msg_signature" ,required = false) String msgSignature) {
        log.info("接受到微信的请求:requestBody:{},signature:{},timestamp:{},nonce:{}",
                requestBody, signature, timestamp, nonce);
        Map<String, String> messageMap = MessageUtil.parseXml(requestBody);
        String msgType = messageMap.get("MsgType");
        String event = messageMap.get("Event") == null ? "" : messageMap.get("Event");
        log.info("msgType:{},event:{}", msgType, event);

        StringBuilder sb = new StringBuilder();
        sb.append(msgType);
        if (!StringUtils.isEmpty(event)){
            sb.append(".");
            sb.append(event);
        }
        String msgTypeKey =sb.toString();
        WxChatMsgHandler wxChatMsgHandler = wxChatMsgFactory.getHandlerByMsgType(msgTypeKey);
        if (Objects.isNull(wxChatMsgHandler)){
            return "unknown";
        }
        String replyContent = wxChatMsgHandler.dealMsg(messageMap);
        log.info("replyContent:{}", replyContent);
        return replyContent;
    }

}

👌适配器模式(实战1)

复制代码
/**
 * 文件存储适配器
 */
public interface StorageAdapter {


    /**
     * 创建bucket桶
     */
    void createBucket(String bucket);


    /**
     * 上传文件
     * <p>
     * long objectSize   上传的对象的总大小 当设置为 -1时,在很多库或框架中意味着"未知大小"或者"未指定大小"。
     * Integer.MAX_VALUE 表示每个分片的最大可能大小  2的31次方-1 约等于2GB
     **/
    void uploadFile(MultipartFile uploadFile, String bucket, String objectName);


    /**
     * 列出所以桶
     */
    List<String> getAllBucket();


    /**
     * 列出当前桶及文件
     */
    List<FileInfo> getAllFile(String bucket);


    /**
     * 下载文件
     */
    InputStream downLoad(String bucket, String objectName);


    /**
     * 删除桶
     */
    void deleteBucket(String bucket) throws Exception;


    /**
     * 删除文件
     */
    void deleteObject(String bucket, String objectName);

    /**
     *获取文件url
     * @param bucket
     * @param objectName
     * @return
     */
    String getUrl(String bucket, String objectName);

}

/**
 * 阿里云oss适配器
 */

public class AliStorageAdapter implements StorageAdapter {
    @Override
    public void createBucket(String bucket) {

    }

    @Override
    public void uploadFile(MultipartFile uploadFile, String bucket, String objectName) {

    }

    @Override
    public List<String> getAllBucket() {
        List<String> bucketNameList = new LinkedList<>();
        bucketNameList.add("aliyun");
        return bucketNameList;
    }

    @Override
    public List<FileInfo> getAllFile(String bucket) {
        return null;
    }

    @Override
    public InputStream downLoad(String bucket, String objectName) {
        return null;
    }

    @Override
    public void deleteBucket(String bucket) throws Exception {

    }

    @Override
    public void deleteObject(String bucket, String objectName) {

    }

    @Override
    public String getUrl(String bucket, String objectName) {
        return null;
    }
}

/**
 * minioIO存储适配器
 */

public class MinioStorageAdapter implements StorageAdapter {

    @Resource
    private MinioUtil minioUtil;


    /**
     * minioUrl
     */
    @Value("${minio.url}")
    private String url;

    @Override
    @SneakyThrows
    public void createBucket(String bucket) {
        minioUtil.createBucket(bucket);
    }

    @Override
    @SneakyThrows
    public void uploadFile(MultipartFile uploadFile, String bucket, String objectName) {
        minioUtil.createBucket(bucket);
        if (objectName != null) {
            minioUtil.uploadFile(uploadFile.getInputStream(), bucket, objectName + "/" + uploadFile.getOriginalFilename());
        } else {
            //对象名不是必填的
            minioUtil.uploadFile(uploadFile.getInputStream(), bucket, uploadFile.getOriginalFilename());
        }
    }

    @Override
    @SneakyThrows
    public List<String> getAllBucket() {
        return minioUtil.getAllBucket();
    }

    @Override
    @SneakyThrows
    public List<FileInfo> getAllFile(String bucket) {
        return minioUtil.getAllFile(bucket);
    }

    @Override
    @SneakyThrows
    public InputStream downLoad(String bucket, String objectName) {
        return minioUtil.downLoad(bucket, objectName);
    }

    @Override
    @SneakyThrows
    public void deleteBucket(String bucket) {
        minioUtil.deleteBucket(bucket);
    }

    @Override
    @SneakyThrows
    public void deleteObject(String bucket, String objectName) {
        minioUtil.deleteObject(bucket, objectName);
    }

    /**
     * 获取文件url
     * @param bucket
     * @param objectName
     * @return
     */
    @Override
    public String getUrl(String bucket, String objectName) {
        return url + "/" + bucket + "/" + objectName;
    }
}

/**
 * 文件存储config
 */
@Configuration
@RefreshScope
public class StorageConfig {

    @Value("${storage.service.type}")
    private String storageType;



    @Bean
    @RefreshScope
    public StorageAdapter storageService(){
        if ("minio".equals(storageType)){
            return new MinioStorageAdapter();
        } else if ("aliyun".equals(storageType)) {
            return new AliStorageAdapter();
        }else {
            throw new IllegalArgumentException("未找到对应的文件处理器");
        }
    }
}

👌责任链模式(八股)

抽象处理者

复制代码
package com.itheima.designpattern.chain;

/**
 * 抽象处理者
 */
public abstract class Handler {

    protected Handler handler;

    public void setNext(Handler handler) {
        this.handler = handler;
    }

    /**
     * 处理过程
     * 需要子类进行实现
     */
    public abstract void process(OrderInfo order);
}

订单信息类:

复制代码
package com.itheima.designpattern.chain;


import java.math.BigDecimal;

public class OrderInfo {

    private String productId;
    private String userId;

    private BigDecimal amount;

    public String getProductId() {
        return productId;
    }

    public void setProductId(String productId) {
        this.productId = productId;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }
}

具体处理者:

复制代码
/**
 * 订单校验
 */
public class OrderValidition extends Handler {

    @Override
    public void process(OrderInfo order) {
        System.out.println("校验订单基本信息");
        //校验
        handler.process(order);
    }

}

/**
 * 补充订单信息
 */
public class OrderFill extends Handler {
    @Override
    public void process(OrderInfo order) {
        System.out.println("补充订单信息");
        handler.process(order);
    }

}

/**
 * 计算金额
 */
public class OrderAmountCalcuate extends Handler {
    @Override
    public void process(OrderInfo order) {
        System.out.println("计算金额-优惠券、VIP、活动打折");
        handler.process(order);
    }

}

/**
 * 订单入库
 */
public class OrderCreate extends Handler {
    @Override
    public void process(OrderInfo order) {
        System.out.println("订单入库");
    }
}

客户类:

复制代码
public class Application {

    public static void main(String[] args) {
        //检验订单
        Handler orderValidition = new OrderValidition();
        //补充订单信息
        Handler orderFill = new OrderFill();
        //订单算价
        Handler orderAmountCalcuate = new OrderAmountCalcuate();
        //订单落库
        Handler orderCreate = new OrderCreate();

        //设置责任链路
        orderValidition.setNext(orderFill);
        orderFill.setNext(orderAmountCalcuate);
        orderAmountCalcuate.setNext(orderCreate);

        //开始执行
        orderValidition.process(new OrderInfo());
    }

}

👌彻底理解代理模式(静态动态CGLIB)

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

  • 代理模式的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途

  • Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK动态代理和cglib 动态代理两种。

  • 优点与缺点

    • 优点:
      • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
      • 代理对象可以扩展目标对象的功能
      • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度
    • 缺点:
      • 增加了系统的复杂度;
2. 结构
  • 代理(Proxy)模式分为三种角色:
    • 抽象主题(Subject)类: 通过接口或抽象类声明真实主题和代理对象实现的业务方法。
    • 真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
    • 代理(Proxy)类 : 提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
3. 静态代理模式
  • 静态代理:在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的字节码文件就已经生成。
3.1 案例
  • 客户可以通过房东(被代理类)直接买房子,也可以通过代理来让中间商(代理类)买房子
    • 抽象主题类:卖房子接口

      public interface RentHouse {
      void rentHouse();
      }

创建一个接口,声明一个卖房子的抽象方法

    • 被代理类:房东

      public class Landlord implements RentHouse{
      @Override
      public void rentHouse() {
      System.out.println("房东卖出房子...");
      }
      }

被代理类实现这个接口重写这个方法

    • 代理类:中间商

      public class Middleman implements RentHouse{
      //通过组合的方式与被代理类产生联系
      private Landlord landlord;

      复制代码
      public Middleman(Landlord landlord){
          this.landlord = landlord;
      }
      
      //代理房东去卖房子
      @Override
      public void rentHouse() {
          //调用房东的rentHouse()
          landlord.rentHouse();
      }

      }

代理类也实现这个接口,通过构造函数注入被代理类对象,重写方法里调用被代理类的方法

    • 客户端类

      public class Customer {
      public static void main(String[] args) {
      //创建一个房东
      Landlord landlord = new Landlord();

      复制代码
          //创建一个中间商
          Middleman middleman = new Middleman(landlord);
          
          //客户可以通过房东买房子
          System.out.print("直接找房东买房子=>");
          landlord.rentHouse();
          
          System.out.println("======================");
          
          //客户也可以通过中间商买房子
          System.out.print("通过中间商卖买房子=>");
          middleman.rentHouse();
      }

      }

然后就是可以创建一个代理类传入被代理类,通过代理类调用买房子的方法

    • 输出结果

日常生活中,我们是很难直接找到房东卖房子的,一般都是通过中间商去买房子,让中间商去代理房东卖房子,这就是代理模式。我们也可以在代理类中添加其他方法,这样可以在不修改被代理类的代码的同时进行扩展。

    • 改进

      public class Middleman implements RentHouse{
      private Landlord landlord;

      复制代码
      public Middleman(Landlord landlord){
          this.landlord = landlord;
      }
      
      //代理房东去卖房子
      @Override
      public void rentHouse() {
          seeHouse();
          signContract();
          //调用房东的rentHouse()
          landlord.rentHouse();
      }
      
      public void seeHouse(){
          System.out.println("中间商带顾客看房子");
      }
      
      public void signContract(){
          System.out.println("达成协议=>签合同");
      }

      }

    • 输出结果
3.2 小结

在代理类中定义其他方法,在被代理类的真实方法执行前后,就可以添加其他方法。这种操作,也是使用代理模式的一个很大的优点。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。

4. 动态代理模式
4.1 JDK动态代理
4.1.1 概述
  • 在静态代理中,代理类是定义好的,在程序运行之前就已经编译完成。而在动态代理中,代理类是在运行时根据代码中的指示动态生成的。与静态代理相比, 动态代理的优势在于可以很方便的对代理类的方法进行统一处理,而不用修改每个代理类中的方法。

  • 在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

  • InvocationHandler

    • InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

      public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable;

  • Object proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0

  • Method method:我们所要调用某个对象真实的方法的Method对象

  • Object[] args:指代代理对象方法传递的参数

  • Proxy

    • Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。

      public static Object newProxyInstance(ClassLoader loader,
      Class<?>[] interfaces,
      InvocationHandler h)
      throws IllegalArgumentException{}

  • loader:一个类加载器对象,定义了由哪个类加载器对生成的代理类进行加载

  • interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口。如果我们提供了这样一个接口对象数组,那生成的代理类也实现了这些接口,代理类就可以调用接口中声明的所有方法。

  • h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

4.1.2 案例
  • 将上述案例改为动态代理
    • 抽象主题类:卖房子接口

      public interface RentHouse {
      void rentHouse();
      }

创建一个接口,声明一个卖房子的抽象方法

    • 被代理类:房东

      public class Landlord implements RentHouse{
      @Override
      public void rentHouse() {
      System.out.println("房东卖出房子...");
      }
      }

被代理类实现这个接口重写这个方法

    • 代理类实现接口

      public class MiddleInvocationHandler implements InvocationHandler {
      //invocationHandler持有的被代理对象
      Object target;

      复制代码
      public MiddleInvocationHandler(Object target){
          this.target = target;
      }
      
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          System.out.println("执行了=>" + method.getName());
          Object result = method.invoke(target, args);
          return result;
      }

      }

创建一个类实现InvocationHandler,通过构造函数注入被代理对象,重写invoke方法,使用反射机制,把当前被调用的方法转发给被代理对象去执行。

创建MiddleInvocationHandler类,实现InvocationHandler接口,这个类中组合了一个被代理对象的实例target。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法。在invoke方法中执行被代理对象target的相应方法。在代理过程中,我们在真正执行被代理对象的方法前加入自己其他处理,这也是Spring中的AOP实现的主要原理。

    • 客户端类

      public class Customer {
      public static void main(String[] args) {
      //创建一个被代理的对象
      Landlord landlord = new Landlord();
      //创建一个与代理对象相关的InvocationHandler
      MiddleInvocationHandler handler = new MiddleInvocationHandler(landlord);
      //创建一个代理对象MiddleProxy
      RentHouse MiddleProxy = (RentHouse) Proxy.newProxyInstance(
      Landlord.class.getClassLoader(),
      Landlord.class.getInterfaces(),
      handler);
      MiddleProxy.rentHouse();
      }
      }

通过Proxy.newProxyInstance()传入被代理对象的类加载器,接口,以及实现InvocationHandler的类对象

来创建代理对象,然后调用方法

  • 创建了一个需要被代理的房东类Landlord,将landlord对象传给了MiddleInvocationHandler中创建handle实例,调用Proxy类的newProxyInstance创建动态代理对象
  • 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。是因为所有被代理执行的方法,都是通过在InvocationHandler中的invoke方法调用的,所以我们只要在invoke方法中统一处理,就可以对所有被代理的方法进行相同的操作了。
4.1.3 实现原理
  • 上述案例中,我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象,关键源码如下

不难看出 ==Class<?> cl = getProxyClass0(loader, intfs)==这行代码产生了代理类,后面代码中的构造器也是通过这里产生的类来获得,可以看出,这个类的产生就是整个动态代理的关键。代理类是程序在运行过程中动态的在内存中生成的类

我们在命令行对MiddleInvocationHandler.java使用命令javac生成MiddleInvocationHandler.class文件,再通过jd-gui工具对.class文件进行反编译,得到代理类的结构:

复制代码
package com.suqu.patterns.proxy.dynamic_proxy;

import ;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class Proxy0 extends Proxy implements RentHouse {
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;
  
    /**
    * 这是生成代理类的构造方法,传入一个InvocationHandler类型的参数
    */
  public Proxy0(InvocationHandler paramInvocationHandler) {
      /*
      * 调用父类的Proxy构造方法
      * 父类持有protected InvocationHandler h
      * Proxy构造方法:
      * protected Proxy(InvocationHandler h) {
      *         Objects.requireNonNull(h);
      *         this.h = h;
      *     }
      */
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject) {...}
  public final String toString() {...}
    
    //这里的rentHouse方法,直接调用了InvocationHandler中的invoke方法并把m3作为参数传递进去
  public final void rentHouse() {
      try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
      }
  }
  public final int hashCode() {...}
  
    //通过反射获得rentHouse方法,并命名为名m3
  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("com.suqu.patterns.proxy.dynamic_proxy.RentHouse").getMethod("rentHouse", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    } catch (NoSuchMethodException noSuchMethodException) {
      throw new NoSuchMethodError(noSuchMethodException.getMessage());
    } catch (ClassNotFoundException classNotFoundException) {
      throw new NoClassDefFoundError(classNotFoundException.getMessage());
    } 
  }
}

解析:在生成代理类的构造方法中,会传入一个InvocationHandler类型的参数,实际上传入的就是我们new的MiddleInvocationHandler,这也就是为什么代理类对象调用的是MiddleInvocationHandler中的invoke()方法,而MiddleInvocationHandler中又有一个被代理类的实例对象landlord,所有最终调用的还是被代理类Landlord中的方法。

  • JDK为我们的生成了一个叫$Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。

  • 我们可以把InvocationHandler看做一个中介类,中介类持有一个被代理对象,在中介类的invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。也就是说,动态代理通过中介类实现了具体的代理功能

  • 总结:生成的代理类:Proxy0 extends Proxy implements RentHouse,我们看到代理类继承了Proxy类,所以也就决定了JDK动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理。上面的动态代理的例子,其实就是AOP的一个简单实现了,在目标对象的方法执行之前和执行之后进行了处理,对方法耗时统计。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个东西的。

部分资料参考自该博客代理模式的使用总结

4.2 CGLIB动态代理
4.2.1 概述
  • 在上述案例中,如果Landlord并没有实现RentHouse接口,JDK动态代理就无法使用了。因为JDK动态代理要求必须定义接口,对接口进行代理。这个时候就可以使用CGLIB动态代理,他为没有实现接口的类提供代理,为JDK的动态代理提供了补充
  • CGLIB动态代理是一个功能强大,高性能的代码生成包,其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作
4.2.2 案例
  • CGLIB是第三方提供的jar包,需要导入。

    <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
  • 被代理类

    public class Landlord {
    public void rentHouse() {
    System.out.println("房东卖出房子...");
    }
    }

  • CglibProxy类,实现了MethodInterceptor

    public class CglibProxy implements MethodInterceptor {
    //重写拦截方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    //可以在方法执行前添加操作
    System.out.println("cglib动态代理start");
    Object result = methodProxy.invokeSuper(o,objects);
    //可以在方法执行后添加操作
    System.out.println("cglib动态代理end");
    return result;
    }

    复制代码
      //获取代理对象的方法
      public Object getCglibProxy(Class<?> clazz){
          //创建动态代理增强类
          Enhancer enhancer = new Enhancer();
          //设置父类,cglib是针对指定的类生成一个子类,所以需要指定父类
          enhancer.setSuperclass(clazz);
          //设置回调函数
          enhancer.setCallback(this);
          //创建代理类
          return enhancer.create();
      }

    }

  • Customer测试类

    public class Customer {
    public static void main(String[] args) {
    Landlord cglibProxy = (Landlord) new CglibProxy().getCglibProxy(Landlord.class);
    cglibProxy.rentHouse();
    }
    }

CGLIB动态代理会让生成的代理类继承被代理类,并在代理类中对代理方法进行强化处理(前置处理、后置处理等)。在CGLIB底层,其实是借助了ASM这个非常强大的Java字节码生成框架。

4.2.3 实现过程
  • 实现步骤:
  1. 创建动态代理增强类Enhancer的实例对象
  2. 通过setSuperclass方法来设置目标类。因为生成的代理类要继承被代理类,所有要指定一个父类
  3. 通过setCallback方法来设置拦截对象;
  4. create方法生成目标类的代理类,并返回代理类的实例。
  • Enhancer是CGLIB中非常常用的一个类。和Proxy不同的是,Enhancer既能够代理普通的class,也能够代理接口。 Enhancer创建一个被代理对象的子类 并且拦截所有的方法调用 (包括从Object中继承的toString和hashCode方法)。Enhancer不能够拦截final方法 ,例如Object.getClass()方法,这是由于Java final方法语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作

部分参考自CGLIB动态代理

5. 三种代理模式的比较
  • 静态代理和动态代理

    • 动态代理相较于静态代理,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
    • 如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题。
  • JDK动态代理和CGLIB动态代理

    • JDK动态代理只能对实现了接口的类生成代理,而不能针对类;CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
    • JDK动态代理的实现是通过Java的反射技术;CGLIB实现动态代理,底层采用ASM字节码生成框架,使用字节码技术生成代理类
    • 由于底层使用反射,JDK动态代理生成类的过程比较高效;而CGLIB采用ASM字节码框架,相关执行的过程比较高效,生成类的过程可以利用缓存弥补。需要注意的是,由于CGLIB原理是动态生成被代理类的子类,故CGLib不能对声明为final的类或者方法进行代理。

在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。

    • JDK动态代理是不需要第三方库支持,只需要JDK环境就可以进行代理;CGLIB必须依赖于CGLIB的类库。
6. 使用场景
  • 远程(Remote)代理

本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常。为良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。

  • 防火墙(Firewall)代理

当你将浏览器配置成使用代理功能时,防火墙就将你的浏览器的请求转给互联网;当互联网返回响应时,代理服务器再把它转给你的浏览器。

  • 保护(Protect or Access)代理

控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。

👌Cglib的Enhancer实现动态代理?

口语化回答

好的,面试官,cglib 代理相比 jdk 动态代理不同的就是不需要被代理的类实现接口。假设我们现在有一个MyService,其中有一个方法是performTask,我们只需要定义一个新的类,实现MethodInterceptor 接口,然后再里面的 intercept 方法实现需要增强的方法。最终通过 cglib 的enhancer 类,先设置父类,父类就是我们要增强的类,再设置 callback 也就是我们要增强的功能。最后使用 create 就生成了 cglib 的一个代理类。以上

题目解析

和 jdk 的动态代理异曲同工,相比 jdk 反而 cglib 的更常考一点。大家理解一下下面的代码流程就很容易说出其中的过程。

面试得分点

目标类,方法拦截器,创建代理对象

题目详细答案

CGLIB是一种强大的代码生成库,能够在运行时生成代理类。与 JDK 动态代理不同的是,CGLIB 不需要接口,可以直接代理具体类。CGLIB 通过创建目标类的子类并覆盖其中的方法来实现代理。

实现步骤

1、 引入 CGLIB 库:确保在项目中添加 CGLIB 依赖。

2、 创建目标类:定义需要代理的具体类。

3、 创建方法拦截器:实现MethodInterceptor接口,并在intercept方法中定义代理逻辑。

4、 创建代理对象:通过Enhancer类创建代理对象。

代码 Demo

假设我们有一个简单的服务类MyService,通过 CGLIB 动态代理为MyService创建一个代理对象,并在方法调用前后添加日志。

引入 CGLIB 依赖
复制代码
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
创建目标类
复制代码
public class MyService {
    public void performTask() {
        System.out.println("Performing task");
    }
}
创建方法拦截器
复制代码
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LoggingMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Logging before method execution: " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("Logging after method execution: " + method.getName());
        return result;
    }
}
创建代理对象并使用
复制代码
import net.sf.cglib.proxy.Enhancer;

public class MainApp {
    public static void main(String[] args) {
        // 创建 Enhancer 对象
        Enhancer enhancer = new Enhancer();

        // 设置目标类为代理类的父类
        enhancer.setSuperclass(MyService.class);

        // 设置方法拦截器
        enhancer.setCallback(new LoggingMethodInterceptor());

        // 创建代理对象
        MyService proxyInstance = (MyService) enhancer.create();

        // 调用代理对象的方法
        proxyInstance.performTask();
    }
}

详细解释

引入 CGLIB 库:在项目中添加 CGLIB 依赖,以便使用 CGLIB 提供的类和接口。

目标类:MyService是一个具体类,定义了一个方法performTask。

方法拦截器:LoggingMethodInterceptor实现了MethodInterceptor接口。它的intercept方法在代理对象的方法调用时被调用。

intercept方法接收四个参数:obj:代理对象。method:被调用的方法。args:方法参数。proxy:用于调用父类方法的代理对象。

在intercept方法中,我们在方法调用前后添加了日志打印,并通过proxy.invokeSuper调用父类的方法。

创建代理对象:使用Enhancer类创建代理对象。setSuperclass方法设置目标类为代理类的父类。setCallback方法设置方法拦截器。create方法创建代理对象。

使用代理对象

通过代理对象调用方法时,实际调用的是LoggingMethodInterceptor的intercept方法。

在intercept方法中,首先打印日志,然后通过proxy.invokeSuper调用目标对象的方法,最后再打印日志。

相关推荐
报错小能手3 小时前
linux学习笔记(13)文件操作
linux·笔记·学习
haoly19893 小时前
数据结构与算法篇--语义智能指针设计模式
数据结构·设计模式
青草地溪水旁3 小时前
设计模式(C++)详解——状态模式(State)(1)
c++·设计模式·状态模式
要做朋鱼燕5 小时前
【OpenCV】图像处理实战:边界填充与阈值详解
图像处理·笔记·opencv·计算机视觉
青草地溪水旁5 小时前
设计模式(C++)详解——策略模式(1)
c++·设计模式·策略模式
secondyoung5 小时前
Markdown转换为Word:Pandoc模板使用指南
开发语言·经验分享·笔记·c#·编辑器·word·markdown
o0向阳而生0o6 小时前
105、23种设计模式之策略模式(14/23)
设计模式·策略模式
孞㐑¥6 小时前
Linux网络部分—网络层
linux·c++·经验分享·笔记
Aurora-silas7 小时前
RAG技术全栈指南学习笔记------基于Datawhale all-in-rag开源项目
笔记·学习