javaee之spring3

news2024/12/29 10:50:21

模拟一个银行转账事务

先来看一下基础文件

先来看这个spring中的bean.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">
    <!-- 配置Service -->
    <bean id="accountService" class="com.pxx.service.impl.AccountServiceImpl">
        <!-- 注入dao -->
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!--配置Dao对象-->
    <bean id="accountDao" class="com.pxx.dao.impl.AccountDaoImpl">
        <!-- 注入QueryRunner -->
        <property name="runner" ref="runner"></property>
    </bean>

    <!--配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="user" value="root"></property>
        <property name="password" value="1234"></property>
    </bean>
</beans>

现在来个一Service的实现类和一个dao的实现类

AccountServiceImpl.java

package com.pxx.service.impl;


import com.pxx.dao.IAccountDao;
import com.pxx.domain.Account;
import com.pxx.service.IAccountService;

import java.util.List;

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    }

    @Override
    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }


    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }


    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }


    public void deleteAccount(Integer acccountId) {
        accountDao.deleteAccount(acccountId);
    }
}

AccountDaoImpl.java

package com.pxx.dao.impl;


import com.pxx.dao.IAccountDao;
import com.pxx.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.util.List;

/**
 * 账户的持久层实现类
 */
public class AccountDaoImpl implements IAccountDao {

    private QueryRunner runner;

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    @Override
    public List<Account> findAllAccount() {
        try{
            return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Account findAccountById(Integer accountId) {
        try{
            return runner.query("select * from account where id = ? ",new BeanHandler<Account>(Account.class),accountId);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void saveAccount(Account account) {
        try{
            runner.update("insert into account(name,money)values(?,?)",account.getName(),account.getMoney());
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void updateAccount(Account account) {
        try{
            runner.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteAccount(Integer accountId) {
        try{
            runner.update("delete from account where id=?",accountId);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

现在我们要去service里面增加一个方法,来进行银行业务转账

 

 

 测试一下我们的配置

 上面测试方法一旦执行,aaa账户就会-100,然后bbb账户就会+100

先来看原始账户

然后调用测试方法

 

 但是这个代码很容易出现一个问题,那就是如果这段代码中间出现一个异常呢

 先来看一下原始账户

 果然报了一个算术异常

 

这里已经违背了数据的原子性(Atomicity) ,事务要么都完成,要么都失败,同时也违背了一致性(consistency)事务前后数据总量必须保持一致。

分析事务问题,并编写ConnectionUtils工具

先拿一张图来说明一下

 问题的出现,在于,我们调用了四个连接对象,所以每一个连接对象都代表了一个事务。我们现在必须想办法把这个四个事务变成一个事务,也就是说把四个Connection连接对象变成一个Connection连接对象

这里的做法就是把这个Connection对象进行事务绑定到一个线程上

package com.pxx.utils;

import javax.sql.DataSource;
import java.sql.Connection;

/**
 * 连接工具类,用于从数据源中获取一个连接,并且实现和线程绑定
 */
public class ConnectionUtils {
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    //这里采用属性类型注入
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 获取当前线程上的连接
     */
    public Connection getThreadConnection() {
        try{
            //1.先从ThreadLocal上获取
            Connection conn = tl.get();
            //2.判断当前线程上是否有连接
            if (conn == null) {
                //3.从数据源中获取一个连接,并且存入ThreadLocal中
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

 那么下面我们用动态代理解决一下事务的问题

来说一下思路:

一、我们先来写一个事务管理工具

TransactionManager.java

package com.pxx.utils;

/**
 *
 * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
 */
public class TransactionManager {

    //引入一个连接对象
    private ConnectionUtils connectionUtils;

    //我们要采用set注入,必须添加一个set方法
    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    //开启事务
    public void beginTransaction() {
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    public  void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
    public  void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 释放连接
     */
    public  void release(){
        try {
            connectionUtils.getThreadConnection().close();//还回连接池中
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 然后添加这个代理工厂BeanFactory.java

package com.pxx.factory;

import com.pxx.service.IAccountService;
import com.pxx.utils.TransactionManager;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class BeanFactory {
    private IAccountService accountService;

    private TransactionManager txManager;

    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }


    public final void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }

    /**
     * 把service里面的方法全部进行增强
     */
    public Object getInstance() {

        return Enhancer.create(accountService.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //这里面就是具体增强代码
                try {
                    Object value = null;
                    //主要是我们需要在执行一个方法前后添加事务控制
                    txManager.beginTransaction();//开启事务
                    value = method.invoke(accountService,args);//有啥参数就拿过来匹配
                    //提交事务
                    txManager.commit();
                    return value;
                } catch (Exception e) {
                    //有异常出现,把数据进行回滚
                    txManager.rollback();;
                    throw new RuntimeException(e);
                } finally {
                    //释放连接
                    txManager.release();
                }
            }
        });

    }
}

然后去配置一下bean.xml各个对象之间的依赖关系

上一下这个完整的配置文件

bean.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">

    <!--配置BeanFactory-->
    <bean id="beanFactory" class="com.pxx.factory.BeanFactory">
        <!--
        注入数据类型
        -->
        <property name="accountService" ref="accountService"></property>
        <property name="txManager" ref="txManager"></property>
    </bean>

    <!--配置一下代理的service
    这个是通过某个对象的方法得到的
    -->
    <bean id="proxyAccountService" factory-bean="beanFactory" factory-method="getInstance"></bean>

    <!-- 配置Service -->
    <bean id="accountService" class="com.pxx.service.impl.AccountServiceImpl">
        <!-- 注入dao -->
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!--配置Dao对象-->
    <bean id="accountDao" class="com.pxx.dao.impl.AccountDaoImpl">
        <!-- 注入QueryRunner -->
        <property name="runner" ref="runner"></property>
    </bean>

    <!--配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
        <property name="user" value="root"></property>
        <property name="password" value="5201314"></property>
    </bean>

    <!--配置Connection工具类 ConnectionUtils-->
    <bean id="connectionUtils" class="com.pxx.utils.ConnectionUtils">
        <!--它需要一个数据源对象-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置事务管理器-->
    <bean id="txManager" class="com.pxx.utils.TransactionManager">
        <!--注入ConnectionUtils工具类-->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>

</beans>

 然后去看一下测试文件AccountServiceTest

 那么原因还是在于

 

 下面看一下test测试方法

 在去测试一下业务,先看一下没出异常之前,会不会出问题

 测试之前的数据表

 测试之后的数据表

正常操作

现在给这个业务添加一个异常

 

 异常有了,看看数据啥情况

我说一下在进行数据rollback之前,必须确认客户端的事务提交是如下状态,也就是关闭自动提交

 来看一下原始数据

 异常出现

 

数据没有变化

面向切面编程AOP 

Spring中的AOP

1.AOP的相关术语

连接点:

 

什么叫切入点

就是对拦截点的增强的方法,所以切入点一定是拦截点,但是拦截点不一定是切入点,很可能我们只是对某一个普通方法进行拦截,但并没有增强具体的业务功能

 

现阶段没有用处,了解一下

 

就是给目标对象动态增加功能的过程

 

这个就是一个编码过程

spring中基于XML的AOP具体配置步骤

在使用这个AOP配置之前,现在maven里面导入一个依赖jar包

 上面这个就是给我们解析切入点表示式的jar包 


        1、把通知Bean也交给spring来管理
        2、使用aop:config标签表明开始AOP的配置
        3、使用aop:aspect标签表明配置切面
                id属性:是给切面提供一个唯一标识
                ref属性:是指定通知类bean的Id。
        4、在aop:aspect标签的内部使用对应标签来配置通知的类型
               我们现在示例是让printLog方法在切入点方法执行之前执行:所以是前置通知
               aop:before:表示配置前置通知
                    method属性:用于指定Logger类中哪个方法是前置通知
                    pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强

            切入点表达式的写法:
                关键字:execution(表达式)
                表达式:
                    访问修饰符  返回值  包名.包名.包名...类名.方法名(参数列表)
                标准的表达式写法:
                    public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
                访问修饰符可以省略
                    void com.itheima.service.impl.AccountServiceImpl.saveAccount()
                返回值可以使用通配符,表示任意返回值
                    * com.itheima.service.impl.AccountServiceImpl.saveAccount()
                包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
                    * *.*.*.*.AccountServiceImpl.saveAccount())
                包名可以使用..表示当前包及其子包
                    * *..AccountServiceImpl.saveAccount()
                类名和方法名都可以使用*来实现通配
                    * *..*.*()
                参数列表:
                    可以直接写数据类型:
                        基本类型直接写名称           int
                        引用类型写包名.类名的方式   java.lang.String
                    可以使用通配符表示任意类型,但是必须有参数
                    可以使用..表示有无参数均可,有参数可以是任意类型
                全通配写法:
                    * *..*.*(..)

                实际开发中切入点表达式的通常写法:
                    切到业务层实现类下的所有方法
                        * com.itheima.service.impl.*.*(..)

    比如现在utils包下面有一个类

然后我们去看一下业务层,有如下的方法

 具体配置信息

 四种常用通知类型

先在logger.java里面看几个配置好的方法

 然后再去看一下bean.xml的配置

上面执行效果不说了

上面感觉有点繁琐,如果可以把pointcut直接引入就好了

 下面直接引入这个切入点就好了

 注意这个切入点标签配置的使用位置

 另外还需要注意的是这个切入标签的配置,必须放在最前面aspect切面标签前面,反正就是如果配置没问题报错,你就调整一下配置位置

Spring中的环绕通知

再说环绕通知之前,先来看一张图

上面的环绕通知就会有一个明确的切入点,也就是说想要业务代码执行,就必须给环绕通知配一个业务的切入点,下面说一下解决方案

解决:
     *      Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
     *
     * spring中的环绕通知:
     *      它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
     */
    public Object aroundPringLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try{
            Object[] args = pjp.getArgs();//得到方法执行所需的参数

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");

            rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");

            return rtValue;
        }catch (Throwable t){
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
            throw new RuntimeException(t);
        }finally {
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
        }
    }

pring中的环绕通知:
          它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。

去看一下它的bean.xml配置

下面我们还是来看一下具体的代码

AccountServiceImpl.java

package com.pxx.service.impl;

import com.pxx.service.IAccountService;

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService{

    @Override
    public void saveAccount() {
        System.out.println("执行了保存");
//        int i=1/0;
    }

    @Override
    public void updateAccount(int i) {
        System.out.println("执行了更新"+i);

    }

    @Override
    public int deleteAccount() {
        System.out.println("执行了删除");
        return 0;
    }
}

 Logger.java

package com.pxx.utils;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 用于记录日志的工具类,它里面提供了公共的代码
 */
public class Logger {

    /**
     * 前置通知
     */
    public  void beforePrintLog(){
        System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
    }

    /**
     * 后置通知
     */
    public  void afterReturningPrintLog(){
        System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
    }
    /**
     * 异常通知
     */
    public  void afterThrowingPrintLog(){
        System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
    }

    /**
     * 最终通知
     */
    public  void afterPrintLog(){
        System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
    }

    /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
     * 解决:
     *      Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
     *
     * spring中的环绕通知:
     *      它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
     */
    public Object aroundPringLog(ProceedingJoinPoint pjp){
        Object rtValue = null;
        try{
            Object[] args = pjp.getArgs();//得到方法执行所需的参数

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");

            rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)

            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");

            return rtValue;
        }catch (Throwable t){
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
            throw new RuntimeException(t);
        }finally {
            System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
        }
    }
}

 AOPTest.java

package com.pxx.test;

import com.pxx.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 测试AOP的配置
 */
public class AOPTest {

    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        IAccountService as = (IAccountService)ac.getBean("accountService");
        //3.执行方法
       // as.saveAccount();
        //业务层的每一个方法都会执行通知
        as.updateAccount(1);
    }
}

运行结果:

Spring基于注解的AOP配置

我们先来创建一个工程

因为这个又是基于注解配置的,所以约束必须导入带context的内容,还是可以去Spring的核心里面找

直接上这些注解配置

 

 

 

 运行结果:

 JDBCTemplate的基本使用

先来导一些相关jar包

 说一下JdbcTemplate最基本的用法

 那么我们就可以用Spring容器的方式来获取对象和设置数据源,来看一下bean.xml里面的代码

 贴一段操作代码

package com.itheima.jdbctemplate;

import com.itheima.domain.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * JdbcTemplate的CRUD操作
 */
public class JdbcTemplateDemo3 {

    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        JdbcTemplate jt = ac.getBean("jdbcTemplate",JdbcTemplate.class);
        //3.执行操作
        //保存
//        jt.update("insert into account(name,money)values(?,?)","eee",3333f);
        //更新
//        jt.update("update account set name=?,money=? where id=?","test",4567,7);
        //删除
//        jt.update("delete from account where id=?",8);
        //查询所有
//        List<Account> accounts = jt.query("select * from account where money > ?",new AccountRowMapper(),1000f);
//        List<Account> accounts = jt.query("select * from account where money > ?",new BeanPropertyRowMapper<Account>(Account.class),1000f);
//        for(Account account : accounts){
//            System.out.println(account);
//        }
        //查询一个
//        List<Account> accounts = jt.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
//        System.out.println(accounts.isEmpty()?"没有内容":accounts.get(0));

        //查询返回一行一列(使用聚合函数,但不加group by子句)
        Long count = jt.queryForObject("select count(*) from account where money > ?",Long.class,1000f);
        System.out.println(count);


    }
}

/**
 * 定义Account的封装策略
 */
class AccountRowMapper implements RowMapper<Account>{
    /**
     * 把结果集中的数据封装到Account中,然后由spring把每个Account加到集合中
     * @param rs
     * @param rowNum
     * @return
     * @throws SQLException
     */
    @Override
    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
        Account account = new Account();
        account.setId(rs.getInt("id"));
        account.setName(rs.getString("name"));
        account.setMoney(rs.getFloat("money"));
        return account;
    }
}

上面也展示了基本的CURD操作

但是我们具体的业务操作,还是放到dao层的,所以这里来看一个dao层的业务文件

AccountDaoImpl1.java

package com.itheima.dao.impl;

import com.itheima.dao.IAccountDao;
import com.itheima.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import java.util.List;

/**
 * 账户的持久层实现类
 */
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

    @Override
    public Account findAccountById(Integer accountId) {
        List<Account> accounts = super.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    @Override
    public Account findAccountByName(String accountName) {
        List<Account> accounts = super.getJdbcTemplate().query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size()>1){
            throw new RuntimeException("结果集不唯一");
        }
        return accounts.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        super.getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/115447.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Dragonfly 和 Nydus Mirror 模式集成实践

文&#xff5c;戚文博 &#xff08;花名&#xff1a;百蓦&#xff09; Dragonfly Maintainer蚂蚁集团软件工程师 主要负责「基于 P2P 的文件以及镜像加速系统」。 本文 2175 字 阅读 15 分钟 PART. 1 背景 自 17 年开源以来&#xff0c;Dragonfly 被许多大规模互联网公司选…

C++贪吃蛇游戏开发实践

C贪吃蛇游戏开发实践 对象分析 我们首先需要确定一个像素点组成的地图&#xff08;画布&#xff09;&#xff0c;要确定行数、列数和像素点大小。这个地图上将会有两个对象&#xff1a;蛇和食物。 蛇由头和身子组成&#xff0c;他们都有自己的位置&#xff0c;所以考虑使用位置…

移动端测试必备技能: adb命令和抓包

移动端测试 是指对移动应用进行的测试&#xff0c;即实体的特性满足需求的程度&#xff0c;进行测试前需要搭建测试环境。 1 移动端自动化环境搭建 1.1 java安装 java JDK 安装jdk-8u181-windows-x64.exe 配置环境变量&#xff1a; JAVA_HOME&#xff1a;D:\developer to…

基于C#+SqlServer开发(WinForm)学生宿舍管理系统【100010056】

学生宿舍管理系统 一、前言 学生宿合是学生们最为熟悉的领域&#xff0c;假定学校有若干栋宿会楼&#xff0c;每栋宿合楼有若干层&#xff0c;每层有若干个寝室&#xff0c;每个寝室可住若干个学生。以往的手工操作已经不能适应现在办公的需要.为了摆脱繁琐的劳动,提高工作效…

HTML XHTML HTML5区别

文章目录HTML & XHTML & HTML5区别HTMLXHTMLHTML5区别HTML & XHTML & HTML5区别 HTML HTML&#xff0c;全称“HyperText Mark-up Language&#xff08;超文本标记语言&#xff09;”&#xff0c;它是构成网页文档的主要语言。我们常说的HTML&#xff0c;指的…

金融服务机构提高移动应用程序安全性的 3 种方式

金融移动应用程序的使用正在迅速加速&#xff0c; 2020 年用户会话数量增长了 49% 。VMware报告称&#xff0c;金融应用程序的网络攻击在同年也增长了 118%。 Intertrust的另一份报告显示&#xff0c;77% 的金融服务应用程序至少包含一个可能导致数据泄露的安全漏洞。最近发现…

【Three.js入门】纹理加载进度、环境贴图、经纬线映射贴图与高动态范围成像HDR

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…

VMwareLinux详细安装步骤

一、VmWare虚拟机的安装 目录 一、VmWare虚拟机的安装 1、安装虚拟机 二、在虚拟机上安装CentOS 1、创建新虚拟机 2、选择典型→ 下一步 3、选择稍后安装操作系统 4、选择操作系统和版本 5、输入虚拟机名称和安装路径 6、设置磁盘大小 7、选择CentOS安装镜像文件 8、…

射线检测中的像质计

像质计&#xff0c;透度计 Image Quality Indicators&#xff0c;Penetrameters 分类&#xff1a;线型像质计、阶梯孔型像质计、平板孔型像质计、双丝型像质计。 前三种像质计的作用&#xff1a;测定检测图像的厚度&#xff08;密度&#xff09;对比度&#xff1b; 后一种像质…

数论专题(1)数论函数,整数分块

从今天起,我们将要开始数论的学习,是不是感觉很难?难的话就听我讲吧,讲了后就不难了(bushi) 数论函数定义 (数论函数) 数论函数的定义:在全体正整数&#xff08;或者整数&#xff09;上定义的函数称作数论函数。 积性的定义&#xff1a;若 gcd(a,b)1,则f(ab)f(a)f(b)。举个栗…

适合制造业的ERP系统有哪些? 制造业的ERP对企业有什么作用?

在当前的激烈的市场竞争下&#xff0c;制造企业如果想要长期稳定地发展&#xff0c;除了需要把外部因素做好把控&#xff0c;还需要提升企业自身的管理水平&#xff0c;来提高自己的竞争力&#xff0c;而信息化是企业发展的必经之路。 适合制造业的ERP系统在企业管理中起到了至…

《Science》教你如何写好一篇博士毕业论文!

博士生涯的完美结束少不了一篇优秀的毕业论文。但是说起来容易&#xff0c;写起来有时让人痛不欲生。不仅内容多&#xff0c;还需要用严谨的逻辑把章节联系起来&#xff0c;常常耗时耗力。而且博士论文要的不仅仅是学术工作的质量&#xff0c;如何将这些工作合理、完整地呈现也…

基于Java实现(PC)大学班级事务管理系统【100010059】

大学班级事务管理系统 要求 本次设计要求利用 Java 实现 C/S 模式的大学班级内日常事务管理系统&#xff08;PC 版&#xff0c;应用于校内网有线网络访问&#xff0c;暂不开发移动端&#xff09;&#xff0c;不得依赖现有的建模框架&#xff0c;使用 swings 技术完成如下基本…

IP地址分类及范围详解

IP地址分为公网IP地址&#xff08;合法IP地址&#xff09;和私有IP地址 公网IP地址主要应用于Internet上的主机访问&#xff0c;而私有IP地址应用于局域网中计算机的相互通信。 IP地址的表示形式&#xff1a;分为二进制表示和点分十进制表示。现在使用的IP地址长度均为32位/4个…

阿里云oss访问图片出现跨域问题

需要服务器端支持&#xff0c;开一下cdn Access-Control-Allow-Origin字段是服务端添加了才有的&#xff0c;前端加了crossOrigin"anonymous"是想跨域获取这张图片&#xff0c;好用在canvas.toDataURL()上&#xff0c;但是服务端不一定同意&#xff0c;服务端添加了…

开始摸索学习go,具体内容和过程就慢慢补充吧。

计划学习路线 文章目录计划学习路线书籍开源项目资料网站课程书籍 《go语言核心编程》 -腾讯作者 《go语言编程之旅》 -5个项目 --对go语言能做的内容做了整体介绍吧&#xff0c;对细节还不够细化&#xff0c;对独立编写代码帮助有限 第二章 swaage 有版本冲突&#xff0c;等…

37.卷积神经网络(LeNet)的代码实现(在colab上)

ps&#xff1a;在教材上直接打开colab&#xff0c;运行原来的代码!pip install githttps://github.com/d2l-ai/d2l-zhrelease # installing d2l是会报错的&#xff0c;改成这句代码&#xff0c;可以正确运行&#xff1a;!pip install d2l0.14.&#xff0c;直接制定了d2l的版本 …

利用Bat打开exe程序并传入值

目录 一、分清楚exe接收值的方式 1、打开exe时提示输入1、2、3... 2、知道exe形参&#xff08;程序主函数中定义的argv[]&#xff09; 二、call和start的区别 一、分清楚exe接收值的方式 1、打开exe时提示输入1、2、3... 如图&#xff1a; 这种是程序运行时接收用户输入…

SuperMap GIS 三维硬件设置优化

目录一、简介二、查看硬件显卡三、显卡设置1、NVIDA显卡设置2、AMD显卡设置一、简介 我们都知道为了体验更好的大型3D游戏&#xff0c;那么好的显卡是必不可少的。但是有了好的显卡当配置不对时&#xff0c;此时体验感也会大打折扣。同样的道理&#xff0c;在SuperMap中也需要…

Redis原理篇—通信协议

Redis原理篇—通信协议 笔记整理自 b站_黑马程序员Redis入门到实战教程 RESP协议 Redis 是一个 CS 架构的软件&#xff0c;通信一般分两步&#xff08;不包括 pipeline 和 PubSub&#xff09;&#xff1a; 客户端&#xff08;client&#xff09;向服务端&#xff08;server&a…