记录Spring的学习(关于AOP 整合JDBC 事务)

AOP

我个人理解的AOP,就是有一个目标对象,有通知类,要将通知织入目标对象,在目标对象执行某个方法的前后以及出现异常等等情况做一些事情,也就是加入通知。
通知分为:

  1.前置通知:在目标方法执行前执行此通知
  2.后置通知(出现异常不会调用):在执行目标方法后面执行
  3.环绕通知:出现目标方法前后都调用
  4.异常拦截通知:目标方法出现异常会调用此通知方法
  5.后置通知(无论有无异常都会调用此方法):执行目标方法后执行

在这里要特别的说一下 aop所用到的jar包:基础包(4+1)+2+2
基础包我之前博客有说过,这个就不说了,只说后面两个+2+2:

  1. spring-aop-4.2.4.RELEASE.jar
  2. spring-aspects-4.2.4.RELEASE.jar
  3. com.springsource.org.aopalliance-1.0.0.jar
  4. com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

先给大家看看我通知类的写法(注意里面的环绕通知方法如何写的):

public class MyAdvice {

	//前置通知
	public void before() {
		System.out.println("这是前置通知");
	}
	
	//这是后置通知 出现异常不会调用
	public void afterReturning() {
		System.out.println("这个是后置通知 出现异常不会调用");
	}
	
	//环绕通知 出现在目标方法前后都调用
	public Object around(ProceedingJoinPoint pJoinPoint) throws Throwable {
		
		System.out.println("这个是调用目标方法前的环绕通知");
		Object proceed = pJoinPoint.proceed(); //调用目标方法
		System.out.println("这个是调用目标方法后的环绕通知");
		return proceed;
	}
	
	//异常拦截通知
	public void afterException() {
		System.out.println("出异常了!");
	}
	
	//后置通知  无论出不出现异常 都会调用这个方法
	public void after() {
		System.out.println("后置通知 无论出不出现异常都会调用这个方法");
	}
}

然后说一下配置文件:

<?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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	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-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

	<!-- 1.配置目标对象 -->
	<bean name="userService" class="com.itheima.service.UserServiceImpl"></bean>
	<!-- 2.配置通知对象 -->
	<bean name="myAdvice" class="com.itheima.aop.MyAdvice"></bean>
	<!-- 3.将通知织入目标对象 -->
	<aop:config>
		<aop:pointcut expression="execution(public void com.itheima.service.UserServiceImpl.addUser())" id="pc"/>
		
		<aop:aspect ref="myAdvice">
			<aop:before method="before" pointcut-ref="pc"/>
			<aop:after method="after" pointcut-ref="pc"/>
			<aop:after-returning method="afterReturning" pointcut-ref="pc"/>
			<aop:after-throwing method="afterException"  pointcut-ref="pc"/>
			<aop:around method="around" pointcut-ref="pc"/>
		</aop:aspect>
	</aop:config>
</beans>

aop 的配置还是分为三步:

  1. 配置目标对象
  2. 配置通知对象
  3. 将通知织入目标对象

注意其中切点和通知的配置
然后是测试类了:

public class test {
	
	@Test
	public void fun1(){
		//读取配置文件
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
		//获取目标对象
		UserService userService = (UserService) applicationContext.getBean("userService");
		//执行方法
		userService.addUser();
	}
}

测试结果(目标对象方法无异常的情况):

这是前置通知
这个是调用目标方法前的环绕通知
添加用户
这个是调用目标方法后的环绕通知
这个是后置通知 出现异常不会调用
后置通知 无论出不出现异常都会调用这个方法

有异常的运行结果:

这是前置通知
这个是调用目标方法前的环绕通知
添加用户
出异常了!
后置通知 无论出不出现异常都会调用这个方法

又有了新发现,目标方法出现异常后,调用目标方法之后环绕通知不会执行,因为到异常那一步,就不会继续运行了。
这就是我理解的AOP,比较短浅。

Spring整合JDBC

我用的是mysql,首先连接数据库肯定要加jar包:
jhjdbc
jar包就这些然后说一下步骤:

  1. 建表 t_user :两个字段 id 和 name
  2. 写bean类:user
  3. 写UserDao接口 :有增删改查查查 这几个方法 后续有代码
  4. 写UserDao接口的实现类UserDaoImpl:写具体实现
  5. 写db.properties :也就是数据库的配置信息
  6. 写applicationContext.xml :也就是写spring的相关配置信息 配置数据库连接池 和配置userDao啊等等
  7. 写demo测试类: 现在就可以在自己的类里面测试了测试dao中的方法

表结构就不贴了,直接说一下User的代码:

public class User {
	private Integer id;
	private String name;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + "]";
	}
	
}

UserDao:

public interface UserDao {

	//增
	public void addUser(User user);
	//刪 根据id删除用户
	public void deleteUser(Integer id);
	//改 根据id修改用户信息
	public void updateUser(User user);
	//查 根据id查询相应用户
	public User findUserById(Integer id);
	//查 查询所有用户的数量
	public int getTotalCount();
	//查 查询所有用户 返回list集合
	public List<User> findAllUer();
}

UserDaoImpl:注意他这里还继承了JdbcDaoSupport 就不用自己配置创建JdbcTemplate模板对象了,可以直接用,这个需要导入spring-tx包 图中有

public class UserDaoImpl extends JdbcDaoSupport implements UserDao {


	@Override
	public void addUser(User user) {
		//先写一个增加的方法再说其他的
		String sql = "insert into t_user values(null,?)";
		super.getJdbcTemplate().update(sql,user.getName());
	}

	@Override
	public void deleteUser(Integer id) {
		String sql = "delete from t_user where id=?";
		
		super.getJdbcTemplate().update(sql,id);
		
	}

	@Override
	public void updateUser(User user) {
		String sql = "update t_user set name=? where id=?";
		
		super.getJdbcTemplate().update(sql,user.getName(),user.getId());
	}

	@Override
	public User findUserById(Integer id) {
		String sql = "select * from t_user where id=?";
		return super.getJdbcTemplate().queryForObject(sql, new RowMapper<User>() {

			@Override
			public User mapRow(ResultSet rs, int arg1) throws SQLException {
				User user = new User();
				user.setId(rs.getInt("id"));
				user.setName(rs.getString("name"));
				return user;
			}
			
		}, id);
	}

	@Override
	public int getTotalCount() {
		String sql = "select count(*) from t_user";
		Integer count = super.getJdbcTemplate().queryForObject(sql, Integer.class);
		return count;
	}

	@Override
	public List<User> findAllUer() {
		String sql = "select * from t_user";
		List<User> list = super.getJdbcTemplate().query(sql, new RowMapper<User>() {

			@Override
			public User mapRow(ResultSet rs, int arg1) throws SQLException {
				User user = new User();
				user.setId(rs.getInt("id"));
				user.setName(rs.getString("name"));
				return user;
			}
			
		});
		return list;
	}
}

db.properties:数据库的配置信息,这里的url什么的前缀可以随便加,为的就是和其他的名称不重复

jdbc.driverClass = com.mysql.jdbc.Driver
jdbc.jdbcUrl = jdbc:mysql:///user
jdbc.user = root
jdbc.password = 123456

下面是最重要的Spring的配置信息:注意此处的给Spring配置连接池的时候怎么配置的
第二不要把这个配置好的连接池给实现类进行操作

<?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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	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-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
	
	<context:property-placeholder location="db.properties"/>
	
	<!-- 1.将连接池放入spring容器 -->
	<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	<!-- 2.将userdao放入spring容器 -->
	<bean name="userDao" class="com.itheima.jdbc.UserDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

</beans>

然后就到了测试类了,这里用到了Springtest 和Junit4 test整合测试,也就是图中Spring-test包的作用
@RunWith(SpringJUnit4ClassRunner.class) : 指定test测试
@ContextConfiguration(“classpath:applicationContext.xml”) : 指定配置文件,此时候,配置文件在src下,可以不加路径
@Resource(name = “userDao”) : 将配置文件里的userDao的值注入到这个类里面的userDao中来进行操作

package com.itheima.jdbc;


import java.util.List;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.itheima.bean.User;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class demo {
	
	@Resource(name = "userDao")
	private UserDao userDao;
	
//	@Test
//	public void fun1() throws Exception  {
//		
//		//配置连接池
//		ComboPooledDataSource dataSource = new ComboPooledDataSource();
//		dataSource.setDriverClass("com.mysql.jdbc.Driver");
//		dataSource.setJdbcUrl("jdbc:mysql:///user");
//		dataSource.setUser("root");
//		dataSource.setPassword("123456");
//		
//		//自己new对象
//		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//		String sql = "insert into t_user values(null,'houge')";
//		jdbcTemplate.update(sql);
//	}
	
	//测试添加用户
	@Test
	public void fun2() throws Exception  {
		User user = new User();
		user.setName("张三");
		userDao.addUser(user);
	}
	
	//测试删除用户
	@Test
	public void fun3() throws Exception  {
		userDao.deleteUser(1005);
	}
	
	//测试更新用户信息
	@Test
	public void fun4() throws Exception  {
		User user = new User();
		user.setId(2);
		user.setName("chengge");
		userDao.updateUser(user);
	}
	
	//测试查询用户 根据id查询
	@Test
	public void fun5() throws Exception {
		User user = userDao.findUserById(2);
		System.out.println(user);
	}
	
	//测试查询所有用户的数量
	@Test
	public void fun6() throws Exception {
		int count = userDao.getTotalCount();
		System.out.println(count);
	}
	
	//测试查询所有用户
	@Test
	public void fun7() throws Exception {
		List<User> allUer = userDao.findAllUer();
		System.out.println(allUer);
	}
	
	
	//测试添加一千个用户
	@Test
	public void fun8() {
		for(int i = 0; i<1000; i++) {
			User user = new User();
			user.setName("hello world!");
			userDao.addUser(user);
		}
	}
	
	//测试删除一千个用户
	@Test
	public void fun9() {
		long startTime = System.currentTimeMillis();
		for (int i = 2290; i > 4; i--) {
			userDao.deleteUser(i);
		}
		long endTime = System.currentTimeMillis();
		System.out.println("删除一千个用户成功!全程共花费"+(endTime-startTime)/1000+"秒");
	}
	
}

后面测试加那么多用户只是乱玩的,可以忽略不看。这就是Spring整合jdbc。

事务

事务这个最常用有两种配置方式 有xml配置的,有注解配置的,我先说xml配置的吧
还是先说明jar包:
事务
首先说一下事务的理解:可以看这个大佬的文章:https://blog.csdn.net/weixin_42047611/article/details/80767113
然后就是步骤:

  1. 先做准备工作:创建表 account 三个字段 id name money money是double类型的
  2. 创建AccountDao 里面两个方法一个加钱一个减钱的 然后写实现类AccountDaoImpl
  3. 再写AccountService有一个transfer方法 谁转给谁多少 再写实现类AccountServiceImpl
  4. 这些准备工作完成之后就是最重要的Spring的配置文件了
  5. 然后写demo类测试

首先是步骤2:
AccountDao

public interface AccountDao {
	
	//加钱
	public void increaseMoney(Integer id, double money);
	//减钱
	public void decreaseMoney(Integer id, double money);
}

AccountDaoImpl:

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{

	@Override
	public void increaseMoney(Integer id, double money) {
		String sql = "update account set money = money+? where id=?";
		getJdbcTemplate().update(sql,money,id);
	}

	@Override
	public void decreaseMoney(Integer id, double money) {
		String sql = "update account set money = money-? where id=?";
		getJdbcTemplate().update(sql,money,id);
	}

}

然后是步骤三:
AccountService:

public interface AccountService {
	//转账方法 谁给谁多少钱
	public void transfer(Integer from, Integer to, double money);
}

AccountServiceImpl:

public class AccountServiceImpl implements AccountService {

	AccountDao aDao;
	public void setaDao(AccountDao aDao) {
		this.aDao = aDao;
	}
	@Override
	public void transfer(Integer from, Integer to, double money) {
		//减钱
		aDao.decreaseMoney(from, money);
//		int i = 1/0;  这个是用来制造异常来检验事务是否真正的起作用
		//加钱
		aDao.increaseMoney(to, money);
	}
}

步骤四:Spring的配置文件:注意看注释

<?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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	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-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
	 
	
	<context:property-placeholder location="db.properties"/>
	
	<!-- 事务核心管理器 封装了所有事务操作 依赖于连接池 -->
	<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置事务通知 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="transfer" isolation="REPEATABLE_READ" read-only="false" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 配置织入 -->
	<aop:config>
		<!-- 配置切入点 -->
		<aop:pointcut expression="execution(* com.itheima.service.AccountServiceImpl.transfer(Integer, Integer, double))" id="txPc"/>
		<!-- 配置切面 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPc"/>
	</aop:config>
	
	<!-- 1.将连接池放入spring容器 -->
	<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	<!-- 2.将accountdao放入spring容器 -->
	<bean name="accountDao" class="com.itheima.dao.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 3.accountService -->
	<bean name="accountService" class="com.itheima.service.AccountServiceImpl">
		<property name="aDao" ref="accountDao"></property>
	</bean>
</beans>

然后就是demo:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class demo {
	
	@Resource(name = "accountService")
	AccountService accountService;
	
	@Test
	public void fun1() {
		accountService.transfer(1, 2, 100);
	}
}

这样就是xml的配置方法,有一个更为简单的注解配置方法:
配置文件如下:
<tx:annotation-driven/ > 这一行的意思就是告诉spring去看注解 不用我们在这里配置通知和将通知织入目标对象了

<?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"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	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-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
	 
	 
	<context:property-placeholder location="db.properties"/>
	
	<!-- 事务核心管理器 封装了所有事务操作 依赖于连接池 -->
	<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<tx:annotation-driven/>
		
	<!-- 1.将连接池放入spring容器 -->
	<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	<!-- 2.将accountdao放入spring容器 -->
	<bean name="accountDao" class="com.itheima.dao.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 3.accountService -->
	<bean name="accountService" class="com.itheima.service.AccountServiceImpl">
		<property name="aDao" ref="accountDao"></property>
	</bean>
</beans>

然后在AccountServiceImpl中的transfer方法上面加上注解,告诉spring是要对这个方法进行事务管理,然后再里面也可以写事务的相关属性
@Transactional(isolation = Isolation.DEFAULT, readOnly = false, propagation = Propagation.REQUIRED)
isolation 是事务的隔离级别
readOnly 是这个方法是否只读数据库 ,因为咱们在这里要对数据库进行修改,所以就是false
Propagation (事务的传播属性)有很多属性,绝大部分用required :表示当前方法必须在一个事务中运行。如果一个现有事务正在进行中,该方法将在那个事务中运行,否则就要开始一个新事务。

代码如下:

public class AccountServiceImpl implements AccountService {

	AccountDao aDao;
	public void setaDao(AccountDao aDao) {
		this.aDao = aDao;
	}
	@Override
	@Transactional(isolation = Isolation.DEFAULT, readOnly = false, propagation = Propagation.REQUIRED)
	public void transfer(Integer from, Integer to, double money) {
		//减钱
		aDao.decreaseMoney(from, money);
//		int i = 1/0;
		//加钱
		aDao.increaseMoney(to, money);
	}
	
}

这就是这几天关于Spring的学习,做一下笔记,磨刀不误砍柴工,不过写博客的过程中也梳理了一下凌乱的代码…

weixin073智慧旅游平台开发微信小程序+ssm后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
python017基于Python贫困生资助管理系统带vue前后端分离毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值