Spring笔记

Spring笔记

1. Spring概述(了解)

1.1 Spring是什么

  • Spring是分层的JavaSE/EE应用full-stack轻量级开源框架

  • 以IOC(控制反转)和AOP(面向切面编程)为内核,提供了展现层SpringMVC和持久层 Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多的著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架

  • 使服务器开发更加简单

  • <!--导入springMVC框架-->
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.2.RELEASE</version>
            </dependency>
        </dependencies>
    
  • <!--导入spring jdbc-->
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.2.RELEASE</version>
            </dependency>
        </dependencies>
    

1.2 Spring的发展历程

  • 2004 年 03 月,1.0 版发布。
  • 2006 年 10 月,2.0 版发布。
  • 2007 年 11 月,更名为 SpringSource,同时发布了 Spring 2.5。
  • 2009 年 12 月,Spring 3.0 发布。
  • 2013 年 12 月,Pivotal 宣布发布 Spring 框架 4.0。
  • 2017 年 09 月,Spring 5.0 发布。

1.3 Spring的优势

  • 非侵入式设计:Spring是一种非侵入式(non-invasive)框架,它可以使应用程序代码对框架的依赖最小化。
  • 方便解耦、简化开发:Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护工作都交给Spring容器的管理,大大的降低了组件之间的耦合性。
  • 支持AOP:Spring提供了对AOP的支持,它允许将一些通用任务,如安全、事物、日志等进行集中式处理,从而提高了程序的复用性。
  • 支持声明式事务处理:只需要通过配置就可以完成对事物的管理,而无须手动编程。
  • 方便程序的测试:Spring提供了对Junit4的支持,可以通过注解方便的测试Spring程序。
  • 方便集成各种优秀框架:Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如Struts、Hibernate、MyBatis、Quartz等)的直接支持。
  • 降低Jave EE API的使用难度:Spring对Java EE开发中非常难用的一些API(如JDBC、JavaMail等),都提供了封装,使这些API应用难度大大降低。
  • IOC(控制反转)和AOP(面向切面编程)

1.4 Spring的体系结构

1.5 扩展

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M2xed76p-1618824670870)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1618541779225.png)]

  • 现代化的java开发,就是基于spring开发

  • Spring Boot

    • 一个快速开发的脚手架
    • 基于Spring Boot可以快速的开发单个微服务
    • 约定大于配置
  • Spring Cloud

    • Spring Cloud是基于Spring Boot实现的

2. IOC理论推导

2.1 IOC理论

  • UserDao接口

    public interface UserDao {
        void getUser();
    }
    
  • UserDaoImpl实现类

    public class UserDaoImpl implements UserDao {
        public void getUser(){
            System.out.println("默认获取用户的数据");
        }
    }
    
  • UserService业务接口

    public interface UserService {
        void getUser();
    }
    
  • UserServiceImpl业务实现类

    public class UserServiceImpl implements UserService {
        private UserDao userDao;
    
        //利用set进行动态实现值的注入
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void getUser() {
            userDao.getUser();
        }
    }
    
  • 测试

    public class MyTest {
        public static void main(String[] args) {
            //用户实际调用的是业务层,dao层他们不需要接触
            UserService userService = new UserServiceImpl();
            userService.setUserDao(new UserDaoImpl());
    
            userService.getUser();
        }
    }
    
  • 测试结果

  • 在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码,如果修改代码非常多,则代价很大

  • 控制反转:之前的程序是主动创建对象,控制权在程序员手上

  • 使用了set注入后,程序不具有主动性,而是变成了被动的接受对象!

  • 程序元不用再去管理对象的创建,系统的耦合性大大降低,可以更加专注在业务的实现上!这是IOC的原型

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jk7hRbiy-1618824670873)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1618553544851.png)]

2.2 IOC本质

  • 控制反转IOC是一种设计思想,DI(依赖注入)是实现IOC的一种方法

  • 面向对象编程,对象的创建与对象间的依赖关系完全由硬编码在程序中,对象的创建由程序自己控制

  • 控制反转后的对象的创建转移给第三方,其实就是获得依赖对象的方式反转了

  • IOC是Spring框架的核心内容,使用多种方式完美的实现IOC,可以使用xml配置,也可以使用注解,新版本的Spring也可以零配置实现IOC

  • Spring容器在初始化时先读取配置问价你,根据配置文件或者元数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Lh5VtDP-1618824670875)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1618554231912.png)]

  • 采用xml方式配置Bean的时候,Bean的定义信息和实现是分离的,而采用注解的方式可以把两者为一体,Bean的顶会议信息直接以注解的形式定义在实现类中,从而达到零配置的目的

  • 控制反转时一种通过描述(xml或者注解)并通过第三方去生产或者获取特定对象的方式,实现控制反转的时IOC容器,其实现方法时依赖注入(DI)

3. HelloSpring

  • 编写Hello类

    public class Hello {
        private String str;
    
        public String getStr() {
            return str;
        }
        public void setStr(String str) {
            this.str = str;
        }
    
        @Override
        public String toString() {
            return "Hello{" +
                    "str='" + str + '\'' +
                    '}';
        }
    }
    
  • 编写配置文件(beans.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    
        <!--使用Spring来创建对象,在Spring这些都称为Bean
    
            类型 变量名 = new 类型();
            Hello hello = new Hello();
    
            id = 变量名
            class = new的对象
            property 相当于给对象中的属性赋一个值
    
            ref:引用Spring容器中创建好的对象
            value:具体的值,基本数据类型
        -->
        <bean id="hello" class="com.kuang.pojo.Hello">
            <property name="str" value="Spring"/>
        </bean>
    
    </beans>
    
  • 测试

    public class MyTest {
        public static void main(String[] args) {
            //提供给ApplicationContext构造函数的位置路径是资源字符串,
            // 这些资源字符串使容器可以从各种外部资源(例如本地文件系统,
            // Java CLASSPATH等)加载配置元数据。
    
            //获取Spring的上下文对象
            ApplicationContext context =
                    new ClassPathXmlApplicationContext("beans.xml");
            //我们的对象现在都在Spring中管理,当我们需要使用的时候,直接取出来就可以了
            Hello hello = (Hello) context.getBean("hello");
            System.out.println(hello.toString());
        }
    
    }
    

4. IOC创建对象的方式

  • 使用无参构造创建对象

  • 假设使用有参构造创建对象

    • 下标赋值

    • <!--第一种:下标赋值-->
          <bean id="hello" class="com.kuang.pojo.Hello">
              <constructor-arg index="0" value="韩清宗"/>
          </bean>
      
    • 类型匹配

    •  <!--第二种:通过类型匹配,不建议使用-->
          <bean id="hello" class="com.kuang.pojo.Hello">
              <constructor-arg type="java.lang.String" value="韩清宗"/>
          </bean>
      
    • 通过参数名

    •     <!--第三种:直接通过参数名-->
          <bean id="hello" class="com.kuang.pojo.Hello">
              <constructor-arg name="str" value="韩清宗"/>
          </bean>
      
    • 在配置文件加载的时候,容器中的对象已经初始化了

5. Spring配置

5.1 别名

<!--别名,如果添加了别名,我们也可以通过别名获取到这个对象-->
    <alias name="hello" alias="hello"/>

5.2 Bean的配置

<!--
id:bean的唯一的标识符,也就是相当于我们的对象名
class:bean对象所对应的全限定名:包名+类名
name:也是别名,而且name可以同时取多个别名
-->
   <bean id="hello" class="com.kuang.pojo.Hello" name = "user,u2,u3" >
    </bean>

5.3 import

  • 有多个配置文件可以在一个配置文件中引用其他的,直接使用总的就可以了

  • applicationContext.xml

  • <import resource = "beans.xml">
        <import resource = "beans2.xml">
            <import resource = "beans3.xml">
    

6. DI依赖注入环境

6.1 构造器注入

6.2 Set方式注入【重点】

  • 依赖注入:set注入

    • 依赖:bean对象的创建依赖于容器
    • 注入:bean对象的所有属性,都由容器来注入
  • 环境搭建

  • 复杂类型

  • public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
    
  • 真实对象测试

  • public class Student {
        private String name;
        private Address address;
        private String[] books;
        private List<String> hobbys;
        private Map<String,String> card;
        private Set<String> game;
        private String wife;
        private Properties info;
    }
    
  • beans.xml

  • <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="student" class="com.kuang.pojo.Student">
            <!--第二种:普通值注入,value-->
            <property name="name" value="韩清宗"/>
        </bean>
    
    </beans>
    
  • 测试类

  • public class MyTest {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext classPathXmlApplicationContext
                    = new ClassPathXmlApplicationContext("beans.xml");
            Student student = (Student)classPathXmlApplicationContext.getBean("student");
    
            System.out.println(student.getName());
    
        }
    }
    
  • 完善注入信息

  • <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="address" class="com.kuang.pojo.Address">
            <property name="address" value="西安"/>
        </bean>
    
        <bean id="student" class="com.kuang.pojo.Student">
            <!--第二种:普通值注入,value-->
            <property name="name" value="韩清宗"/>
    
            <!--第二种:bean注入,ref-->
            <property name="address" ref="address"/>
    
            <!--数组注入-->
            <property name="books">
                <array>
                    <value>水浒传</value>
                    <value>西游记</value>
                    <value>三国演义</value>
                    <value>红楼梦</value>
                </array>
            </property>
    
            <!--List-->
            <property name="hobbys">
                <list>
                    <value>听歌</value>
                    <value>看电影</value>
                    <value>吃饭</value>
                </list>
            </property>
    
            <!--Map-->
            <property name="card">
                <map>
                    <entry key="身份证" value="111111222222223333"/>
                    <entry key="银行卡" value="12324325326434313243"/>
                </map>
            </property>
    
            <!--set-->
            <property name="game">
                <set>
                    <value>LOL</value>
                    <value>BOB</value>
                    <value>COC</value>
                </set>
            </property>
    
            <!--null-->
            <property name="wife">
                <null></null>
            </property>
    
            <!--properties-->
            <property name="info">
                <props>
                    <prop key="学号">2019</prop>
                    <prop key="性别"></prop>
                    <prop key="姓名">小明</prop>
                </props>
            </property>
    
        </bean>
    
    </beans>
    

6.3 其他方式注入

  • 我们可以使用p命名空间和c命名空间进行注入

  • 官方解释

  • <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:c="http://www.springframework.org/schema/c"
    
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        
        <!--p命名空间注入,可以直接注入属性的值:property-->
       <bean id="user" class="com.kuang.pojo.User" p:name="韩清宗" p:age="18"/>
    
        <!--p命名空间注入,可以构造器注入属性的值:construct-args -->
        <bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="韩清宗"/>
    
    </beans>
    
  • 测试

  •   @Test
        public void test(){
            ClassPathXmlApplicationContext context
                    = new ClassPathXmlApplicationContext("userbeans.xml");
            User user = (User) context.getBean("user2");
            System.out.println(user);
        }
    
  • p命名空间注入,p命名空间注入不能直接使用

  • xmlns:p="http://www.springframework.org/schema/p"
           xmlns:c="http://www.springframework.org/schema/c"
    

6.4 bean的作用域

  • ScopeDescription
    singleton(默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。
    prototype将单个 bean 定义的作用域限定为任意数量的对象实例。
    request将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext中有效。
    session将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
    application将单个 bean 定义的范围限定为ServletContext的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
    websocket将单个 bean 定义的范围限定为WebSocket的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
  • 单例模式(spring的默认机制)

  •    <bean id="user" class="com.kuang.pojo.User" p:name="韩清宗" p:age="18" scope="singleton"/>
    
    
  • 原型模式:每次从容器中get都会产生新对象

  •     <bean id="user2" class="com.kuang.pojo.User" c:age="18" c:name="韩清宗" scope="prototype"/>
    
    
  • 其余的在web开发中使用

7. bean的自动装配

  • 自动装配是spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性!

在spring中有三种装配的方式

  • 在xml中显示的配置
  • 在java中显示的配置
  • 隐式的自动装配(掌握)

7.1 测试

  • 环境搭建:一个人有两个宠物

7.2 ByName自动装配

  • <!--byName:会自动在上下文中查找,和自己对象set方法后面的值对应的beanid-->
        <bean id="people" class="com.kuang.pojo.People" autowire="byName">
            <property name="name" value="韩清宗"/>
        </bean>
    

7.3 ByType自动装配

  •  <!--byName:会自动在上下文中查找,和自己对象类型一样,id可以省略-->
        <bean id="people" class="com.kuang.pojo.People" autowire="byType">
            <property name="name" value="韩清宗"/>
        </bean>
    

7.4 使用注解进行自动装配

  • 使用注解须知

    • 导入约束

    • 配置注解支持( context:annotation-config/ [重要])

    • <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:context="http://www.springframework.org/schema/context"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
      
          <context:annotation-config/>
      
      </beans>
      
    • @Autowired

      • 直接在属性上使用即可
      • 还能够在set方法上使用
      • 可以忽略set()方法
    • 科普:

    • @Nullable 字段标记了这个注解,说明这个字段可以为null
      
    • public @interface Autowired{
          boolean required() default true;
      }
      
    • public class People {
      
          //如果显示的定义了Autowired的required的属性为false,说明这个对象可以为null,否则不允许为空
          @Autowired(required = false)
          private Cat cat;
          @ Autowired
          private Dog dog;
          @Autowired
          private String name;
      
    • @Autowired自动装配环境复杂,自动装配无法只通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier="xxx"去配置@Autowired的使用,指定唯一的bean对象注入

    • public class People {
      
          @Autowired
      	@Qualifier(value="cat111")
          private Cat cat;
          @ Autowired
          @Qualifier(value="dog222")
          private Dog dog;
          @Autowired
          private String name;
      
    • @Resource注解

    • @Resource(name = "cat2")
      private Cat cat;
      

    小结:

    • @Resource和@Autowired区别:
      • 都是用来自动装配的,都可以放在属性字段上
      • @Autowired通过byType实现,而且必须要求对象存在
      • @Resource通过默认byname实现,如果找不到名字,则通过bytype实现,都找不到,报错

8. 使用注解开发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ogIS0bir-1618824670877)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1618813240020.png)]

8.1 bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

8.2 属性如何注入

//等价于 < bean id="user" class="com.kuang.pojo.User"/>
    //@Component 主键

@Component
public class User {
    //相当于给属性赋值,也可以注入到ser方法上
    @Value("kuang")
    public String name;
}

8.3 衍生的注解

  • @Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层
  • dao【@Repository】
  • controller【@Controller】
  • service【@service】
  • 这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean

8.4 自动装配配置

8.5 作用域

  • @Component
    @Scope("singleton")
    public class User {
        //相当于给属性赋值,也可以注入到ser方法上
        @Value("kuang")
        public String name;
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    

8.6 小结

  • xml与注解

    • xml更加万能,适用于任何场合
    • 不是自己的类使用不了,维护相对复杂
  • xml与注解的最佳实现

    • xml用来管理bean;

    • 注解只负责完成属性的注入

    • 需求要开启注解的支持

    • !--指定要扫描的包,这个包下的注解会直接生效-->
          <context:component-scan base-package="com.kuang.pojo"/>
          <context:annotation-config/>
      
      

9. 使用java的方式配置Spring

  • 我们不适用Spring的xml的配置了,全部用java做

  • 实体类

  • //就是把这个类注册到容器中
    @Component
    public class User {
        private String name;
    
        public User(String name) {
            this.name = name;
        }
    
        public User() {
        }
    
        public String getName() {
            return name;
        }
    
        @Value("shenxian")
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    
  • //配置文件
    package com.kuang.config;
    
    import com.kuang.pojo.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    
    @Configuration
    @Import(kuangConfig2.class)//两个配置文件整合
    public class kuangconfig {
        @Bean
        public User getUser(){
            return new User();
        }
    }
    
    
  • //测试
    import com.kuang.pojo.User;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContextExtensionsKt;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import java.lang.reflect.AnnotatedElement;
    
    public class MyTest {
        public static void main(String[] args) {
            ApplicationContext context
                    = new AnnotationConfigApplicationContext("kuangconfig.class");
            User getUser = (User)context.getBean("getUser");
            System.out.println(getUser.getName());
    
        }
    }
    

10. 代理模式

  • SpringAOP的底层是代理模式

代理模式的分类:

10.1 静态代理

  • 角色分析:
  • 抽象角色:一般会用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实的角色,代理真是角色后,我们一般会做一些附属操作
  • 客户访问代理对象的人

代理模式的好处:

  • 可以使角色的操作更加纯粹,不用关注公众的业务
  • 公众也就交给了代理角色,实现了业务的分工
  • 公众业务发生扩展的时候,方面集中管理

缺点:

  • 一个真实角色就会产生一个代理角色,会使编码的效率变低

  • 代码步骤:

  • 接口

  • //租房的的接口
    public interface Rent {
        public void rent();
    }
    
  • 真实角色

  • //房东
    public class Host implements Rent{
    
        public void rent() {
            System.out.println("房东出租房子");
        }
    }
    
  • 代理角色

  • package com.kuang.demo01;
    
    import com.kuang.Host;
    
    /**
     * Created by admin on 2021/4/19.
     */
    public class Proxy implements Rent {
        private Host host;
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        public Proxy() {
        }
    
        public void rent(){
            host.rent();
        }
    
        public void seeHouse(){
            System.out.println("kanfang");
        }
    
        public void fare(){
            System.out.println("zhongjiefei");
        }
    
        public void heTong(){
            System.out.println("hetong");
        }
    }
    
    
  • 客户端访问代理角色

  • public class Client {
        public static void main(String[] args) {
            Host host = new Host();
            Proxy proxy = new Proxy(host);
            proxy.rent();
        }
    }
    
    

10.2 加深理解

10.3 动态代理

  • 动态代理和静态代理角色一样

  • 动态代理的代理类是动态生成的,不是我们直接写好的

  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理

    • 基于接口—JDK动态代理
    • 基于类:cglib
    • java字节码:javasist
  • 需要了解两个类:Proxy 代理,invocationHander 调用处理程序

  • invocationHander

    this.host = host;
    }

    public Proxy() {
    }
    
    public void rent(){
        host.rent();
    }
    
    public void seeHouse(){
        System.out.println("kanfang");
    }
    
    public void fare(){
        System.out.println("zhongjiefei");
    }
    
    public void heTong(){
        System.out.println("hetong");
    }
    

    }

    
    
  • 客户端访问代理角色

  • public class Client {
        public static void main(String[] args) {
            Host host = new Host();
            Proxy proxy = new Proxy(host);
            proxy.rent();
        }
    }
    
    

10.2 加深理解

  • [外链图片转存中…(img-vy0e8COO-1618824670879)]

10.3 动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口—JDK动态代理
    • 基于类:cglib
    • java字节码:javasist
  • 需要了解两个类:Proxy 代理,invocationHander 调用处理程序
  • invocationHander
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值