每周小记——Spring框架

Day01——快速入门

 一、简介

Spring:轻量级的、开源的JavaEE解决方案

二、工厂模式

2.1 概述

工厂设计模式是一种创建型设计模式,其核心思想是通过一个工厂类来封装对象的创建过程,客户端无需直接实例化具体类,而是通过工厂获取对象。这种模式解耦了对象的创建与使用,提升了系统的灵活性和可维护性。

 例如

Dao层中存在多个UserDaoImpl实现,以UserDaoMybatisImpl举例

public class UserDaoMybatisImpl implements UserDao {
    public UserDaoMybatisImpl() {
    }

    @Override
    public void save() {
        System.out.println("Mybatis implements");
    }
}

在bean.xml文件中有如下定义:

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="userDao" class="com.zzy.user.UserDaoMybatisImpl"/>
</beans>

创建类XmlBeanFactory读取xml文件并利用反射生产类

public class XmlBeanFactory {
    public static Object getBean(String fileName, String key) {
        SAXReader reader = new SAXReader();
        try {
            Document doc = reader.read(fileName);
            Element root = doc.getRootElement();
            List<Element> beans = root.elements("bean");
            for (Element bean : beans) {

                if (key.contentEquals(bean.attributeValue("id"))) {
                    String className = bean.attributeValue("class");
                    //使用类名反射创建Class对象
                    Class<?> aClass = Class.forName(className);
                    return aClass.getConstructor().newInstance();
                }
            }
        } catch (DocumentException | ClassNotFoundException | NoSuchMethodException | InstantiationException |
                 IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

在Service层中定义save方法

实现类中如下

public class UserServiceImpl implements UserService {
    public UserServiceImpl() {
    }

    @Override
    public void save() {
        UserDao dao = (UserDao) XmlBeanFactory.getBean("G:\\Spring\\spring_core\\day01\\target\\classes\\bean.xml", "userDao");
        if (dao != null) {
            dao.save();
        }
    }
}

在测试类中调用Serviceimpl的save方法后,控制台输出

Mybatis implements

2.2 Spring工厂模式的核心实现 

1.BeanFactory
Spring的BeanFactory是工厂模式的典型代表,它是IoC容器的核心接口,负责管理Bean的生命周期和依赖注入。其核心方法getBean()通过唯一标识符动态创建或获取Bean实例,例如:

BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
UserService userService = (UserService) factory.getBean("userService");

延迟加载BeanFactory默认延迟初始化Bean,仅在首次调用getBean()时创建实例,减少启动时的资源占用。

2.ApplicationContext

作为BeanFactory的子接口,ApplicationContext扩展了更多功能(如事件发布、国际化支持等),并支持预加载所有单例Bean。常见实现类包括:

  • ClassPathXmlApplicationContext:加载类路径下的XML配置文件。

  • AnnotationConfigApplicationContext:基于Java注解配置Bean。

  • FileSystemXmlApplicationContext:从文件系统加载XML配置。

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = context.getBean("user", User.class);

3.FactoryBean
当Bean的创建逻辑复杂时(如依赖外部资源或动态代理),可通过实现FactoryBean接口自定义实例化过程。例如:

public class CarFactoryBean implements FactoryBean<Car> {
    @Override
    public Car getObject() {
        return new Car("Tesla", "red"); // 复杂初始化逻辑
    }
    @Override
    public Class<?> getObjectType() { return Car.class; }
}
  • 特点:通过getBean("carFactoryBean")获取的是Car实例而非工厂本身。

三、控制反转(Ioc)与依赖注入(DI)

一、IoC与DI的本质:控制权的转移

1. 传统开发模式:程序员掌控一切

在传统编码中,对象的创建、依赖关系由开发者显式控制。例如:

// 传统方式:直接在代码中创建依赖对象
public class Car {
    private Engine engine = new GasolineEngine();  // 直接绑定具体实现
    public void start() {
        engine.ignite();
    }
}
  • 问题Car类与GasolineEngine强耦合。若想改用ElectricEngine,必须修改Car的源码,违反开闭原则(OCP)。

2. IoC(控制反转):将控制权交给容器

IoC的核心是反转对象创建和依赖绑定的控制权,由容器(如Spring)统一管理。例如:

public class Car {
    private Engine engine;  // 不再直接创建对象
    public Car(Engine engine) {  // 依赖通过外部传入
        this.engine = engine;
    }
}
  • 关键点Car不再关心Engine的具体实现,只需声明依赖接口,由容器决定注入GasolineEngine还是ElectricEngine

3. DI(依赖注入):实现IoC的具体手段

DI是IoC的一种实现方式,通过外部注入依赖而非内部创建。这里先简单讲讲Spring支持三种注入方式,下一节中将重点讲解注入(injection):

  • 构造函数注入(最推荐,保证依赖不可变):

    @Component
    public class Car {
        private final Engine engine;
        @Autowired  // Spring自动注入Engine实例
        public Car(Engine engine) {
            this.engine = engine;
        }
    }
  • Setter方法注入(适合可选依赖):

    public class Car {
        private Engine engine;
        @Autowired
        public void setEngine(Engine engine) {
            this.engine = engine;
        }
    }
  • 字段注入(简洁但隐藏依赖关系,不推荐):

    public class Car {
        @Autowired
        private Engine engine;
    }

二、IoC与DI的核心价值:解耦与灵活性

1. 解耦的实战意义
  • 场景1:替换数据库驱动
    若项目从MySQL迁移到PostgreSQL,只需修改Spring配置中的JDBC连接参数,无需改动业务层代码。

  • 场景2:Mock测试
    单元测试时,可用Mock对象(如Mockito)注入到被测试类,无需启动真实数据库:

    @Test
    public void testCar() {
        Engine mockEngine = Mockito.mock(Engine.class);
        Car testCar = new Car(mockEngine);  // 注入Mock对象
        testCar.start();
        Mockito.verify(mockEngine).ignite();
    }
2. Spring容器的实现机制
  • BeanFactory:基础容器,提供Bean的创建与获取。

  • ApplicationContext:扩展了BeanFactory,支持国际化、事件传播等企业级功能。

  • 工作流程

    1. 容器启动时,扫描配置(XML或注解)并初始化Bean定义。

    2. 根据依赖关系(如@Autowired)构建Bean之间的关联图。

    3. 通过反射或CGLIB动态代理创建Bean实例,完成依赖注入。


三、IoC与DI的进阶理解

1. IoC的两种实现方式对比
方式 依赖注入(DI) 依赖查找(DL)
代表框架 Spring、Guice EJB(通过JNDI查找)
代码侵入性 低(通过注解/配置声明依赖) 高(需主动调用查找API)
灵活性 依赖关系由容器管理,透明化 需手动管理对象生命周期
2. Spring中的DI高级特性
  • 条件化注入:使用@Conditional根据环境动态注入Bean(如区分开发与生产环境配置)。

  • 多Bean冲突解决

    • @Primary:标记优先注入的Bean。

    • @Qualifier:按名称指定具体Bean。

  • 延迟注入@Lazy注解延迟初始化Bean,优化启动性能。

3. IoC与设计模式的关系
  • 工厂模式:Spring容器本质上是一个超级工厂,负责生产和管理Bean。

  • 策略模式:通过注入不同的接口实现,动态切换算法(如支付方式:支付宝 vs 微信支付)。

  • 模板方法模式:Spring JdbcTemplate通过依赖注入DataSource,封装了数据库操作的通用流程。


四、总结:为什么IoC/DI是Spring的灵魂?

  • 代码层面:通过解耦提升可维护性和可测试性。

  • 架构层面:使模块化开发成为可能,支持微服务、云原生等现代架构。

  • 哲学层面:体现了“好莱坞原则”(Don’t call us, we’ll call you),框架掌控流程,开发者专注业务逻辑。


 Day02 03——注入

我们创建一个测试类Student,其中包含如下属性(这里使用Lombok注解自动生成Getter Setter)

@Data
public class Student {
    private String name;
    private int age;
}

还有一个更复杂的TeacherService类,包含更多更全面的测试属性

@Data
public class TeacherService {
    private TeacherDao dao;
    private String name;
    private String major;
    private boolean isSingle;
    private String[] addr;
    private Set<Student> students;
    private List<String> list;
    private Map<String, Double> map;
    private Properties properties;
}

现在我们需要在Spring的配置文件中开始对这些属性进行注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="teacherService" class="com.zzy.inject.TeacherService">
        <!-- 针对基本数据类型的操作 -->
        <property name="name" value="Eric"/>
        <!-- 赋空值 -->
        <!-- <property name="name"><null/></property> -->
        <property name="major" value="Computer Science"/>
        <property name="single" value="true"/>
        <!-- 针对数组的操作 -->
        <property name="addr">
            <list>
                <value>SH</value>
                <value>BJ</value>
                <value>LZ</value>
                <value>CZ</value>
            </list>
        </property>
        <!-- 针对Set<Student>的操作 -->
        <property name="students">
            <set>
                <!-- 写法一 -->
                <!--<bean id="student1" class="com.zzy.inject.Student">
                    <property name="name" value="Stu1"/>
                    <property name="age" value="18"/>
                </bean>
                <bean id="student2" class="com.zzy.inject.Student">
                    <property name="name" value="Stu2"/>
                    <property name="age" value="81"/>
                </bean>-->
                <!-- 写法二 -->
                <ref bean="student1"/>
                <ref bean="student2"/>
            </set>
        </property>
        <!-- 针对List集合 -->
        <property name="list">
            <list>
                <value>list value 1</value>
                <value>list value 2</value>
                <value>list value 3</value>
                <value>list value 4</value>
            </list>
        </property>
        <!-- 针对Map<K,V> -->
        <property name="map">
            <
接入第三方登录是让用户方便快捷地使用已有账号登录你的网站或应用程序,提高用户体验的一种方式。本文将介绍如何使用 PHP 实现微信公众号第三方登录。 1. 获取微信授权 首先,需要获取微信用户的授权。具体步骤如下: 1)引导用户打开微信授权页面: ```php $appid = 'your_appid'; $redirect_uri = urlencode('http://yourdomain.com/callback.php'); $scope = 'snsapi_userinfo'; $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appid&redirect_uri=$redirect_uri&response_type=code&scope=$scope&state=STATE#wechat_redirect"; header("Location: $url"); ``` 其中,`$appid` 是你的微信公众号的 AppID,`$redirect_uri` 是授权后回调的 URL,`$scope` 是授权作用域,可以是 `snsapi_base` 或 `snsapi_userinfo`,`$state` 是自定义参数,用于防止 CSRF 攻击。 2)获取授权码: 用户同意授权后,会重定向到 `$redirect_uri` 指定的 URL,带上授权码 `code` 和 `state` 参数。 ```php $code = $_GET['code']; $state = $_GET['state']; ``` 3)获取 access_token 和 openid: 使用授权码 `code` 获取 `access_token` 和 `openid`。 ```php $access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$secret&code=$code&grant_type=authorization_code"; $response = file_get_contents($access_token_url); $result = json_decode($response, true); $access_token = $result['access_token']; $openid = $result['openid']; ``` 其中,`$secret` 是你的微信公众号的 AppSecret。 2. 获取用户信息 获取到 `access_token` 和 `openid` 后,可以使用以下代码获取用户信息: ```php $userinfo_url = "https://api.weixin.qq.com/sns/userinfo?access_token=$access_token&openid=$openid&lang=zh_CN"; $response = file_get_contents($userinfo_url); $userinfo = json_decode($response, true); ``` 其中,`$userinfo` 包含用户的昵称、头像等信息。 3. 将用户信息保存到数据库 最后,将获取到的用户信息保存到数据库中,以便下次使用时快速登录。 ```php // 连接数据库 $con = mysqli_connect('localhost', 'username', 'password', 'database'); mysqli_set_charset($con, "utf8"); // 查询用户是否已存在 $sql = "SELECT * FROM users WHERE openid='$openid'"; $result = mysqli_query($con, $sql); if (mysqli_num_rows($result) == 0) { // 用户不存在,插入新用户信息 $nickname = mysqli_real_escape_string($con, $userinfo['nickname']); $headimgurl = mysqli_real_escape_string($con, $userinfo['headimgurl']); $sql = "INSERT INTO users (openid, nickname, headimgurl) VALUES ('$openid', '$nickname', '$headimgurl')"; mysqli_query($con, $sql); } // 保存用户登录状态 $_SESSION['openid'] = $openid; ``` 以上就是使用 PHP 实现微信公众号第三方登录的步骤。需要注意的是,为了确保安全性,应该对用户输入的数据进行过滤和验证,防止 SQL 注入和 XSS 攻击等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值