Spring两大核心之一:AOP(面向切面编程)含设计模式讲解,通知类型切点;附有案例,实现spring事务管理

模拟转账业务

pom.xml

<dependencies><!--spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.29</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version></dependency><!--dbutil--><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.4</version></dependency><!--数据源c3p0--><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.2</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--spring测试环境--><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.29</version></dependency>
</dependencies>

Bean:Account

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account implements Serializable {private int aid;private String aname;private int amoney;public Account(String aname, int amoney) {this.aname = aname;this.amoney = amoney;}}

Dao层:

接口

public interface IAccountDao {public void save(Account account);public void update(Account account);public Account findByName(String name);public List<Account> findAll();
}

实现类:

public class AccountDaoImp implements IAccountDao {QueryRunner queryRunner;public void setQueryRunner(QueryRunner queryRunner) {this.queryRunner = queryRunner;}ConnectionUtil connectionUtil;public void setConnectionUtil(ConnectionUtil connectionUtil) {this.connectionUtil = connectionUtil;}@Overridepublic void save(Account account) {try {queryRunner.update(connectionUtil.createConn(),"insert into acount(aname,amoney) values (?,?)", account.getAname(), account.getAmoney());} catch (SQLException throwables) {throwables.printStackTrace();}}@Overridepublic void update(Account account) {try {queryRunner.update(connectionUtil.createConn(),"update acount set aname=?,amoney=? where aid=?", account.getAname(), account.getAmoney(), account.getAid());} catch (SQLException throwables) {throwables.printStackTrace();}}@Overridepublic Account findByName(String name) {try {Account query = queryRunner.query("select * from acount where aname=?", new BeanHandler<Account>(Account.class), name);return query;} catch (SQLException throwables) {throwables.printStackTrace();}return null;}@Overridepublic List<Account> findAll() {try {List<Account> all = queryRunner.query("select * from acount", new BeanListHandler<Account>(Account.class));return all;} catch (SQLException throwables) {throwables.printStackTrace();}return null;}
}

Service层:

接口:

public interface IAccountService {public void save(Account account);public void update(Account account);public Account findByName(String name);public List<Account> findAll();public void transfer(String sourceName,String targetName,int money);
}

实现类:

public class AccountServiceImp implements IAccountService {IAccountDao dao;public void setDao(IAccountDao dao) {this.dao = dao;}TransactionUtil transactionUtil;public AccountServiceImp(TransactionUtil transactionUtil) {this.transactionUtil = transactionUtil;}@Overridepublic void save(Account account) {dao.save(account);}@Overridepublic void update(Account account) {dao.update(account);}@Overridepublic Account findByName(String name) {Account a = dao.findByName(name);return a;}@Overridepublic List<Account> findAll() {List<Account> all = dao.findAll();return all;}// 转账业务@Overridepublic void transfer(String sourceName, String targetName, int money) {try {transactionUtil.openTran();// 查询俩个人的账户状态Account sourceAccount = dao.findByName(sourceName);Account targetAccount = dao.findByName(targetName);// 转账sourceAccount.setAmoney(sourceAccount.getAmoney()-money);targetAccount.setAmoney(targetAccount.getAmoney()+money);// 修改数据dao.update(sourceAccount);int a = 10/0;dao.update(targetAccount);transactionUtil.commitTran();} catch (Exception e) {e.printStackTrace();transactionUtil.rollbackTran();}finally {transactionUtil.closeTran();}}
}

连接工具类ConnectionUtil:

public class ConnectionUtil {// 装配数据源DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();Connection connection =null;// 获取连接public Connection createConn(){try {connection = threadLocal.get();if(connection ==null){connection = dataSource.getConnection();threadLocal.set(connection);}return this.connection;} catch (SQLException throwables) {throwables.printStackTrace();}return connection;}// 关闭连接public void closeConn(){threadLocal.remove();}
}

事务管理工具类TransactionUtil:

public class TransactionUtil {// 装配连接ConnectionUtil connectionUtil;public void setConnectionUtil(ConnectionUtil connectionUtil) {this.connectionUtil = connectionUtil;}// 开启事务public void openTran(){try {connectionUtil.createConn().setAutoCommit(false);} catch (SQLException throwables) {throwables.printStackTrace();}}// 提交事务public void commitTran(){try {connectionUtil.createConn().commit();} catch (SQLException throwables) {throwables.printStackTrace();}}// 回滚事务public void rollbackTran(){try {connectionUtil.createConn().rollback();} catch (SQLException throwables) {throwables.printStackTrace();}}// 关闭事务public void closeTran(){try {connectionUtil.createConn().close();} catch (SQLException throwables) {throwables.printStackTrace();}}
}

Controller类:

接口:

public interface IAccountController {public void save(Account account);public void update(Account account);public Account findByName(String name);public List<Account> findAll();public void transfer(String sourceName,String targetName,int money);
}

实现类:

public class AccountControllerImp implements IAccountController {IAccountService service;public void setService(IAccountService service) {this.service = service;}@Overridepublic void save(Account account) {service.save(account);}@Overridepublic void update(Account account) {service.update(account);}@Overridepublic Account findByName(String name) {Account account = service.findByName(name);return account;}@Overridepublic List<Account> findAll() {List<Account> all = service.findAll();return all;}@Overridepublic void transfer(String sourceName, String targetName, int money) {service.transfer(sourceName,targetName,money);}
}

Spring主配置文件applicationContext.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="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT"></property><property name="user" value="root"></property><property name="password" value="123456"></property></bean><!--注入连接工具类--><bean id="connectionUtil" class="com.dong.util.ConnectionUtil"><property name="dataSource" ref="dataSource"></property></bean><!--注入事务工具类--><bean id="transactionUtil" class="com.dong.util.TransactionUtil"><property name="connectionUtil" ref="connectionUtil"></property></bean><!--注入queryRunner--><bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"><constructor-arg name="ds" ref="dataSource"></constructor-arg></bean><!--注入dao--><bean id="accountDaoImp" class="com.dong.dao.AccountDaoImp"><property name="queryRunner" ref="queryRunner"></property><property name="connectionUtil" ref="connectionUtil"></property></bean><!--注入service--><bean id="accountServiceImp" class="com.dong.service.AccountServiceImp"><property name="dao" ref="accountDaoImp"></property><constructor-arg name="transactionUtil" ref="transactionUtil"></constructor-arg></bean><!--注入controller--><bean id="accountControllerImp" class="com.dong.controller.AccountControllerImp"><property name="service" ref="accountServiceImp"></property></bean>
</beans>

模拟转账业务中,如果直接使用spring的事务管理的话,如果业务逻辑中产生异常,eg在修改两个人的金额之间出现异常,第一个人的金额发生改变,并提交了数据,因为产生异常第二个人的数据没有修改,造成数据不一致的问题,所以就需要想办法让一个业务共用一个连接,所以自定义了连接管理类和事务管理工具类,实现业务共用一个连接对象,业务层公用的连接一起提交或回滚

Java的六大设计原则

设计原则是为了更好的设计软件的高层指导方针,它不提供具体的实现方式也不会绑定任何一种编程语言,最常用的原则是SOLID(SRP, OCP, LSP, ISP, DIP)原则

  • 开闭原则(OCP) - The Open-Closed Principle
  • 单一职责原则(SRP) - Single Responsibility Principle
  • 里氏替换原则(LSP) - Liskov Substitution Principle
  • 依赖倒置原则(DIP) - Dependency Inversion Principle
  • 接口隔离原则(ISP) - Interface Segregation Principle
  • 迪米特法则(DP) - Demeter Principle

设计模式

设计模式对关于面向对象问题的具体解决方案;比如说,如果你想创建一个类而且它在任何时刻只会有一个对象,那么你就应该使用单例类模式

设计模式是经过大量检测的安全的做法

单例模式

单例模式性能好,但是安全性差

  • 饿汉模式:不管用不用,先创建一个对象,用的时候直接给
  • 懒汉模式:用的时候在创建对象,只创建一次,第二次用直接给不在创建
  1. 饿汉模式演示

    public class Student {/*饿汉模式*/private static Student stu=new Student();private Student() {}public static synchronized Student getInstance(){return stu;}
    }
    
  2. 懒汉模式演示

    public class Student {/*懒汉模式*/private static Student stu=null;private Student() {}public static synchronized Student getInstance(){if(stu == null){stu = new Student();}return stu;}
    }
    

工厂模式

演示:

INoodels接口

public interface INoodels {public void NoodelsType();
}

INoodels接口实现类:

  1. YouPoNoodle

    public class YouPoNoodels implements INoodels {@Overridepublic void NoodelsType() {System.out.println("油泼面");}
    }
    
  2. ReGanNoodel

    public class ReGanNoodls  implements INoodels{@Overridepublic void NoodelsType() {System.out.println("武汉热干面");}
    }
    
  3. LanZhouNoodel

    public class LanZhouNoodels implements INoodels {@Overridepublic void NoodelsType() {System.out.println("兰州牛肉面");}
    }
    

工厂NoodleFactory

public class NoodelFactory {public  static final int YOUPO_NOODEL=1;public  static final int REGAN_NOODEL=2;public  static final int LANZHOU_NOODEL=3;public static INoodels createNoodel(int type){if(type ==1){return new YouPoNoodels();}else if(type ==2){return new ReGanNoodls();}else if(type ==3){return new LanZhouNoodels();}return null;}
}

Test01

public class Test01 {public static void main(String[] args) {NoodelFactory.createNoodel(NoodelFactory.LANZHOU_NOODEL).NoodelsType();NoodelFactory.createNoodel(3).NoodelsType();}
}

工厂模式是由工厂帮我们创建对象

模板模式

模板模式==spring框内

* JdbcTemplate

* JpaTemplate

模板模式首先要有一个抽象类,这个抽象类公开定义了执行它的方法的方式/模板。

它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式

“比如,在造房子一样,地基,铺线,房子户型都是一样的,由开发商决定,但是在交房之后,室内的装修风格和场景布置却是由业主决定,在这个场景中,开发商其实就是一个抽象类,地基,铺线,房子户型都是可以复用的,但是装修却是不可复用的,必须由业主决定,此时的每一个业主的房子就是一个实现的子类”

Spring中jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是Spring并没有使用这种方式,而是使用Callback模式与模板方法配合,既达到了代码复用的效果,同时增加了灵活性

所谓模板板式,就是在父类中定义算法的主要流程,而把一些个性化的步骤延迟到子类中去实现,父类始终控制着整个流程的主动权,子类只是辅助父类实现某些可定制的步骤

实现演示:

FatherClass:

public abstract class FatherClass {public final void life(){study();work();love();}public void study(){System.out.println("学C++");}public void work(){System.out.println("干嵌入式");}public abstract void love();
}

SonClass

public class SonClass extends FatherClass {@Overridepublic void study() {System.out.println("学java");}@Overridepublic void work() {System.out.println("干java开发");}@Overridepublic void love() {System.out.println("美女大妹");}
}

Test:

public class Test01 {public static void main(String[] args) {FatherClass fatherClass = new SonClass();fatherClass.人生();}
}

FatherClass类里面定义人生()方法被final修饰不能重写,规定了先学习,再工作,最后恋爱,体现了模板模式

代理模式

什么是代理模式?

​ 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。

为什么要用代理模式?

  • 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

  • 开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则

有哪几种代理模式?

有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话可以分为两种:

静态代理:

​ 静态代理是由程序员创建或特定工具自动生成源代码,在对其编译在程序员运行之前,代理类.class文件就已经被创建了

动态代理:

​ 动态代理是在程序运行时通过反射机制动态创建的

  1. 基于接口的动态代理(jdk自带)
  2. 基于子类的动态代理(第三方)

静态代理

演示:假如西门庆想找潘金莲,但不能直接找,这时候出现了“中介-王婆”

接口:IWoman

public interface IWoman {public void makeEye();
}

实现类:PanJinLian

public class Panjinlian implements IWoman{@Overridepublic void makeEye() {System.out.println("潘金莲向你发送了一个邀请");}
}

实现类:WangPo

public class Wangpo implements IWoman{/*被代理对象*/private IWoman woman;public Wangpo(IWoman woman) {this.woman = woman;}public Wangpo() {}@Overridepublic void makeEye() {System.out.println("武大郎今天不在家");woman.makeEye();}
}

XiMenQing:

public class XiMenQing {public static void main(String[] args) {Panjinlian panjinlian = new Panjinlian();Wangpo wangpo = new Wangpo(panjinlian);wangpo.makeEye();}
}

上述案例,王婆就是代理,潘金莲就是被代理对象,西门庆找潘金莲就要先找王婆,达到了中介隔离的作用,而在代理中添加别的功能,不会对被代理对象产生代码污染,就实现了增加功能

基于接口动态代理jdk

演示:假如粉丝找歌手开演唱会,我们需要找到经纪人

接口:ISinger

public interface ISinger {public void Sing();
}

实现类:CaiXuKunImpl

public class CaiXuKunImp implements ISinger {@Overridepublic void Sing() {System.out.println("只因你太美");}
}

测试:Fans

public class Fans {public static void main(String[] args) {// 被代理对象ISinger caiXuKun = new CaiXuKunImp();ISinger jingjiren = (ISinger) Proxy.newProxyInstance(CaiXuKunImp.class.getClassLoader(), CaiXuKunImp.class.getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object obj = method.invoke(caiXuKun, args);System.out.println("跳舞");System.out.println("篮球");System.out.println("RAP");return obj;}});jingjiren.Sing();}
}

通过调用经纪人的sing(),蔡徐坤唱了一首只因你太美,

经纪人还做了增强功能,跳舞篮球Rap

基于子类的动态代理cglib

第三方插件,需要导入坐标

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

演示:粉丝通过经纪人找歌手

接口:ISinger

public interface ISinger {public void sing();
}

实现类:CaiXuKunImpl

public class CaiXuKunImp implements ISinger {@Overridepublic void sing() {System.out.println("oi oi oi");}
}

测试:Fans

public class Fans {public static void main(String[] args) {ISinger caixukun = new CaiXuKunImp();ISinger jingjiren = (ISinger) Enhancer.create(caixukun.getClass(), new InvocationHandler() {@Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {System.out.println("跳舞篮球RAP");Object object = method.invoke(caixukun, objects);return object;}});jingjiren.sing();}
}

对模拟转账业务升级

对代理模式有了概念和理解之后,就可以对上面的转账业务升级

静态代理实现升级改造

创建代理类:BeansFactory

public class BeansFactory {// 装配被代理对象private IAccountService toService;public void setToService(IAccountService toService) {this.toService = toService;}// 装配事务管理工具private TransactionUtil transactionUtil;public void setTransactionUtil(TransactionUtil transactionUtil) {this.transactionUtil = transactionUtil;}public IAccountService serviceProxy(){IAccountService serviceProxy = (IAccountService) Proxy.newProxyInstance(toService.getClass().getClassLoader(), toService.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object obj = null;try {transactionUtil.openTran();obj = method.invoke(toService, args);transactionUtil.commitTran();} catch (Exception e) {e.printStackTrace();}  finally {transactionUtil.closeTran();}return obj;}});return serviceProxy;}}

Service层实现类不需要再关注事务问题

public class AccountServiceImp implements IAccountService {IAccountDao dao;public void setDao(IAccountDao dao) {this.dao = dao;}@Overridepublic void save(Account account) {dao.save(account);}@Overridepublic void update(Account account) {dao.update(account);}@Overridepublic Account findByName(String name) {Account a = dao.findByName(name);return a;}@Overridepublic List<Account> findAll() {List<Account> all = dao.findAll();return all;}// 转账业务@Overridepublic void transfer(String sourceName, String targetName, int money) {// 查询俩个人的账户状态Account sourceAccount = dao.findByName(sourceName);Account targetAccount = dao.findByName(targetName);// 转账sourceAccount.setAmoney(sourceAccount.getAmoney()-money);targetAccount.setAmoney(targetAccount.getAmoney()+money);// 修改数据dao.update(sourceAccount);int a = 10/0;          // 模拟出现异常dao.update(targetAccount);}
}

配置文件中的修改:

<!--注入数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT"></property><property name="user" value="root"></property><property name="password" value="123456"></property>
</bean><!--注入连接工具类-->
<bean id="connectionUtil" class="com.dong.util.ConnectionUtil"><property name="dataSource" ref="dataSource"></property>
</bean><!--注入事务工具类-->
<bean id="transactionUtil" class="com.dong.util.TransactionUtil"><property name="connectionUtil" ref="connectionUtil"></property>
</bean><!--注入queryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"><constructor-arg name="ds" ref="dataSource"></constructor-arg>
</bean><!--注入dao-->
<bean id="accountDaoImp" class="com.dong.dao.AccountDaoImp"><property name="queryRunner" ref="queryRunner"></property><property name="connectionUtil" ref="connectionUtil"></property>
</bean><!--注入service(被代理对象)-->
<bean id="accountServiceImp" class="com.dong.service.AccountServiceImp"><property name="dao" ref="accountDaoImp"></property>
</bean ><!--注入service代理业务-->
<bean id="accountServiceImpProxy" class="com.dong.service.AccountServiceImp" factory-bean="factory" factory-method="serviceProxy">
</bean><!--注入bean工厂-->
<bean id="factory" class="com.dong.factory.BeansFactory"><property name="toService" ref="accountServiceImp"></property><property name="transactionUtil" ref="transactionUtil"></property>
</bean><!--注入controller-->
<bean id="accountControllerImp" class="com.dong.controller.AccountControllerImp"><property name="service" ref="accountServiceImpProxy"></property>
</bean>

原来的service成为被代理对象,代理业务通过工厂造出,注入了事务管理对象和被代理业务对象,控制器中注入的对象变成被代理业务对象

Spring AOP

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP 的作用及其优势

  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

  • 优势:减少重复代码,提高开发效率,并且便于维护

Spring AOP 基于动态代理实现

○ 如果被代理的对象,已经实现某个接口,则 Spring AOP 会使用 JDK Proxy(反射),基于接口的方式,创建代理对象(JDK动态代理的核心是InvocationHandler接口和Proxy类);
○ 如果被代理的对象,没有实现某个接口,就无法使用 JDK Proxy 去进行代理了,这时被代理对象的子类来作为代理(Cglib动态代理的核心是MethodInterceptor接口和Enhancer类);

AOP主要用于

  1. 权限
  2. 日志
  3. 事务

AOP通知类型

AOP将抽取出来的共性功能称为通知;通知类型:以通知在上下文中的具体位置作为划分:

  1. 前置通知(Before)
  2. 返回通知(After-returning)
  3. 异常通知(After-throwing)
  4. 后置通知(After)
  5. 环绕通知(Around)

AOP连接点Join point

AOP将所有的方法都视为连接点,不管是接口里面的抽象方法,还是实现类里面的重写方法,都是连接点

AOP切点Pointcut

AOP将可能被抽取共性功能的方法称为切入点。切入点是连接点的子集

切点表达式配置语法

execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))
eg:execution(public void com.apesource.service.ServiceImp.findAll())
1.修饰符可以省略代表任意execution(返回值 包名称.类名称.方法名称(参数列表))
2.返回值可以使用“*”代表任意execution(* 包名称.类名称.方法名称(参数列表))
3.包名可以使用“*”代表任意名称execution(* *.*.*.类名称.方法名称(参数列表))
4.包名可以使用“..”代表任意个数execution(* *...类名称.方法名称(参数列表))
5.类名与方法名可以使用“*”代表任意execution(* *...*.*(参数列表))
6.参数列表可以使用".."代表任意个数任意类型execution(* *...*.*(..))如果有参数int======>intString===>java.lang.String

AOP目标对象Target

AOP目标对象(Target): 就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的

AOP织入Weaving

AOP织入(Weaving):就是将挖掉的功能回填的动态过程

AOP切面:切点+通知

配置文件实现AOP

  1. 导入坐标
  2. 配置

演示:模拟一个业务,实现日志

  1. 导入坐标

    <dependencies><!--Spring--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.29</version></dependency><!-- aspectj (AOP) --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency>
    </dependencies>
    
  2. 业务

    接口:

    public interface IService {public void save();public void delete();public void update();
    }
    

    实现类:

    public class ServiceImp implements IService {@Overridepublic void save() {System.out.println("新增咯");}@Overridepublic void delete() {System.out.println("删除咯");}@Overridepublic void update() {System.out.println("修改咯");}
    }
    
  3. 日志:

    public class Log {public void beforeLogger(){System.out.println("这是前置通知日志哦,时间:");}public void afterReturnningLogger(){System.out.println("这是返回通知日志哦,时间:");}public void exceptionLogger(){System.out.println("这是异常通知日志哦,时间:");}public void afterLogger(){System.out.println("这是后置通知日志哦,时间:");}public Object arroundLogger(ProceedingJoinPoint pjp){Object proceed =null;try {// 前置通知System.out.println("环绕通知----》前置通知");Object[] args = pjp.getArgs();proceed = pjp.proceed(args);// 返回通知System.out.println("环绕通知----》返回通知");} catch (Throwable throwable) {// 异常通知System.out.println("环绕通知----》异常通知");} finally {// 后置通知System.out.println("环绕通知----》后置通知");}return proceed;}
    }
    
  4. 配置文件:applicationContext.xml

    如果使用前置,返回,异常,后置通知,配置文件如下

    <!--注入Service-->
    <bean id="serviceImp" class="com.dong.service.ServiceImp"></bean>
    <!--注入log-->
    <bean id="log" class="com.dong.logs.Log"></bean><aop:config><aop:aspect id="aop" ref="log"><!--切点--><aop:pointcut id="dian" expression="execution(* com..dong.service.*.*(..))"/><!--前置通知--><aop:before method="beforeLogger" pointcut-ref="dian"></aop:before><!--返回通知--><aop:after-returning method="beforeLogger" pointcut-ref="dian"></aop:after-returning><!--异常通知--><aop:after-throwing method="exceptionLogger" pointcut-ref="dian"></aop:after-throwing><!--后置通知--><aop:after method="afterLogger" pointcut-ref="dian"></aop:after></aop:aspect>
    </aop:config>
    

    如果使用了环绕通知,则可以替代上面四种通知,配置文件如下

    <!--注入Service-->
    <bean id="serviceImp" class="com.dong.service.ServiceImp"></bean>
    <!--注入log-->
    <bean id="log" class="com.dong.logs.Log"></bean><aop:config><aop:aspect id="aop" ref="log"><!--切点--><aop:pointcut id="dian" expression="execution(* com..dong.service.*.*(..))"/><!--环绕通知--><aop:around method="arroundLogger" pointcut-ref="dian"></aop:around></aop:aspect>
    

注释实现AOP

  1. 导坐标,spring-context和aspectjweaver坐标

  2. 业务:

    接口:

    public interface IService {public void save();public void delete();public void update();
    }
    

    实现类:

    @Service
    public class ServiceImp implements IService {@Overridepublic void save() {System.out.println("新增咯");}@Overridepublic void delete() {System.out.println("删除咯");}@Overridepublic void update() {System.out.println("修改咯");}
    }
    
  3. 日志:

    @Component
    @Aspect
    public class Log {@Pointcut(value = "execution(* com.dong.service.*.*(..))")public void dian(){};@Before("dian()")public void beforeLog(){System.out.println("这是前置日志");}@AfterReturning("dian()")public void afterReturnningLog(){System.out.println("这是返回日志");}@AfterThrowing("dian()")public void exceptionLog(){System.out.println("这是异常日志");}@After("dian()")public void afterLog(){System.out.println("这是后置日志");}@Around("dian()")public Object arroundLog(ProceedingJoinPoint pjp){Object proceed =null;try {System.out.println("环绕日志----》前置日志");Object[] args = pjp.getArgs();proceed = pjp.proceed(args);System.out.println("环绕日志----》返回");} catch (Throwable throwable) {throwable.printStackTrace();System.out.println("环绕日志----》异常");} finally {System.out.println("环绕日志----》后置");}return proceed;}
    }
    
  4. 配置文件中,要扫包,并开启AOP

    <!--扫包-->
    <context:component-scan base-package="com.dong"></context:component-scan>
    <!--注解驱动-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

Spring整合Mybatis

  1. 导坐标

    <dependencies><!--spring容器--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.29</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.5</version></dependency><!--springJDBC--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.9.RELEASE</version></dependency><!--数据源--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.21</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.29</version></dependency>
    </dependencies>
    
  2. Bean实体类:Account

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @Component
    public class Account {private int aid;private String aname;private int amoney;public Account(String aname, int amoney) {this.aname = aname;this.amoney = amoney;}
    }
    
  3. Dao层

    public interface IAccountDao {@Insert("insert into acount(aname,amoney) values(#{aname},#{amoney})")public void save(Account account);@Update("update acount set aname=#{aname} , amoney=#{amoney} where aid = #{aid}")public void update(Account account);@Select("select * from acount")public List<Account> selectAll();@Select("select * from acount where aname=#{v}")public Account selectByName(String name);}
    
  4. Service层:

    接口:

    public interface IAccountService {public void save(Account account);public void update(Account account);public Account selectByName(String name);public List<Account> selectAll();
    }
    

    实现类:

    @Service
    public class AccountServiceImpl  implements IAccountService{@Autowiredprivate IAccountDao dao;@Overridepublic void save(Account account) {dao.save(account);}@Overridepublic void update(Account account) {dao.update(account);}@Overridepublic Account selectByName(String name) {Account account = dao.selectByName(name);return account;}@Overridepublic List<Account> selectAll() {List<Account> accounts = dao.selectAll();return accounts;}
    }
    
  5. Controller层:

    接口:

    public interface IAccountController {public void save(Account account);public void update(Account account);public Account selectByName(String name);public List<Account> selectAll();
    }
    

    实现类:

    @Controller
    public class AccountConreollerImpl implements IAccountController {@Autowiredprivate IAccountService service;@Overridepublic void save(Account account) {service.save(account);}@Overridepublic void update(Account account) {service.update(account);}@Overridepublic Account selectByName(String name) {Account account = service.selectByName(name);return account;}@Overridepublic List<Account> selectAll() {List<Account> accounts = service.selectAll();return accounts;}
    }
    
  6. 配置文件

    Mybatis的配置文件:Mybatis.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration><!--1.数据源==交给spring容器2.事务管理==交给spring容器3.mapper映射器注册--></configuration>
    

    Spring的配置文件:applicationContext.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"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 https://www.springframework.org/schema/context/spring-context.xsd"><!--注入数据源--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT"></property><property name="username" value="root"></property><property name="password" value="123456"></property></bean><!--配置sqlSessionFactory--><bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"></property><property name="configLocation" value="classpath:mybatisConfig.xml"></property></bean><!--配置mapper映射器,生成代理对象注入容器--><bean id="configurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.dong.dao"></property></bean><!--扫包--><context:component-scan base-package="com.dong"></context:component-scan>
    </beans>
    

    注入mapper映射器后,dao层就不需要再注入容器了

以上步骤完成,Spring就成功整合了Mybatis

Spring任务调度(spring task)

任务调度

​ 可以用于程序运行时设置在某些固定的时刻执行特定的操作,比如设置调度任务在凌晨的时候自动同步数据等等

实现技术:spring task

实现spring task任务调布只需要导入spring context坐标即可,不需要额外导入其他坐标,只需要在spring主配置文件中配置

配置文件实现任务调度

  1. 假如被调度的任务输出信息

    public class MyTask {public void task1(){System.out.println("task1:"+new Date());}public void task2(){System.out.println("task2:"+new Date());}
    }
    
  2. 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:task="http://www.springframework.org/schema/task"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"><!--注入任务--><bean id="task" class="com.dong.MyTask"></bean><!--创建调度线程池--><task:scheduler id="poolTaskScheduler" pool-size="10"></task:scheduler><!--配置任务调度--><task:scheduled-tasks scheduler="poolTaskScheduler"><!--任务方法   cron表达式:实现触发任务的策略--><task:scheduled ref="task" method="task1" cron="0-5 * * * * *"/><task:scheduled ref="task" method="task2" cron="3/3 * * * * *"/></task:scheduled-tasks>
    </beans>
    
    • 需要把被调度的任务对象注入容器
    • 调度线程池配不配置都可以
    • <task:scheduled-tasks></task:scheduled-tasks>标签中调度线程池若没配置就不需要写属性,想要调度的方法就可以在配置标签之间写,ref写注入的任务,method写调度任务的方法,cron表达式配置触发任务的策略
  3. 测试(让程序程序运行)

    public class Test01 {public static void main(String[] args) {ApplicationContext app = new ClassPathXmlApplicationContext("applicationcontext.xml");}
    }
    
  4. 控制台输出效果

    在这里插入图片描述

    上述配置的task1任务的cron表达式:每分钟前五秒调一次

    ​ task2任务的cron表达式:第3秒开始,每隔三秒钟调一次

cron表单式

在这里插入图片描述

cron表达式从左到右共有七位,分别代表:

秒、分、小时、日期、月份、星期、年份

cron表达式在线工具生成器网址

注解实现任务调度

  1. 导入坐标spring context

  2. 任务调度对象

    需要写@Component注解,注入容器

    ​ @Scheduled注解,表示调度任务,括号中写cron表达式

    @Component
    public class MyJob {@Scheduled(cron = "* * * * * *")public void myjob1(){System.out.println("Hacker Dong");}
    }
    
  3. 配置文件

    <?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:task="http://www.springframework.org/schema/task"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"><!--扫包--><context:component-scan base-package="com.dong"></context:component-scan><!--配置任务调度开启--><task:annotation-driven></task:annotation-driven>
    </beans>
    
    • 配置扫包
    • 配置开启任务调度
  4. 测试

    public class Test01 {public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");}
    }
    

Spring注解实现事务管理

spring实现事务管理就不再需要自己写事务管理工具类,只需要在业务上添加注解@Transactional

  1. 导入坐标

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.dong</groupId><artifactId>SpringAOP_transaction</artifactId><version>1.0-SNAPSHOT</version><dependencies><!--spring容器--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.29</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.5</version></dependency><!--springJDBC--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.9.RELEASE</version></dependency><!--数据源--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.21</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.29</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.0.3.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.7</version></dependency></dependencies></project>
    
  2. Bean实体类、Dao、Service、Controller三层

    Bean:

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @Component
    public class Account implements Serializable {private int aid;private String aname;private int amoney;
    }
    

    dao:

    public interface IAccountDao {@Insert("insert into acount(aname,amoney) values(#{aname},#{amoney})")public void save(Account account);@Update("update acount set aname=#{aname} , amoney=#{amoney} where aid = #{aid}")public void update(Account account);@Select("select * from acount where aname=#{v}")public Account selectByName(String name);@Select("select * from acount")public List<Account> selectAll();
    }
    

    Service层:

    IAccountSerivce:

    public interface IAccountSerivce {public void save(Account account);public void update(Account account);public Account selectByName(String name);public List<Account> selectAll();public void zhuanzhang(String user1,String user2,int money);
    }
    

    AccountServiceImp:

    @Service
    @Transactional         // spring事务管理
    public class AccountServiceImp implements IAccountSerivce {@Autowiredprivate IAccountDao dao;@Overridepublic void save(Account account) {dao.save(account);}@Overridepublic void update(Account account) {dao.update(account);}@Overridepublic Account selectByName(String name) {Account account = dao.selectByName(name);return account;}@Overridepublic List<Account> selectAll() {List<Account> accounts = dao.selectAll();return accounts;}@Overridepublic void zhuanzhang(String user1, String user2, int money) {Account account1 = dao.selectByName(user1);Account account2 = dao.selectByName(user2);account1.setAmoney(account1.getAmoney()-money);account2.setAmoney(account2.getAmoney()+money);dao.update(account1);dao.update(account2);}
    }
    

    Controller层:

    IAccountController:

    public interface IAccountController {public void save(Account account);public void update(Account account);public Account selectByName(String name);public List<Account> selectAll();public void zhuanzhang(String user1,String user2,int money);
    }
    

    AccountControllerImp:

    @Controller
    public class AccountControllerImp implements IAccountController{@Autowiredprivate IAccountSerivce service;@Overridepublic void save(Account account) {service.save(account);}@Overridepublic void update(Account account) {service.update(account);}@Overridepublic Account selectByName(String name) {Account account = service.selectByName(name);return account;}@Overridepublic List<Account> selectAll() {List<Account> accounts = service.selectAll();return accounts;}@Overridepublic void zhuanzhang(String user1, String user2, int money) {service.zhuanzhang(user1,user2,money);}
    }
    
  3. 配置文件:

    mybatis的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration><!--1.数据源==交给spring容器2.事务管理==交给spring容器3.mapper映射器注册--></configuration>
    

    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: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 https://www.springframework.org/schema/context/spring-context.xsd http://www.alibaba.com/schema/stat http://www.alibaba.com/schema/stat.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"><!--注入数据源--><bean id="data" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/spring?serverTimezone=GMT"></property><property name="username" value="root"></property><property name="password" value="123456"></property></bean><!--配置sqlSessionFactory--><bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="data"></property><property name="configLocation" value="classpath:mybatisConfig.xml"></property></bean><!--配置mapper映射器,生成代理对象注入容器--><bean id="configurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.dong.dao"></property></bean><!--扫包--><context:component-scan base-package="com.dong"></context:component-scan><!--配置事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--注入DataSource--><property name="dataSource" ref="data"></property></bean><!--开启spring对注解事务管理的支持--><tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven></beans>
    

    注意事项:

    • 数据源,SqlSessionFactoryBean需要配置
    • 配置mapper映射器,dao层不需要再注入容器
    • 扫包
    • 配置事务管理器
      • 需要注入数据源
      • 需要开启spring对注解事务管理的支持

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

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

相关文章

物联网二维码核销盒对接文档

核销盒是干嘛的&#xff1f; 1.在某些场景下快速核销订单或打卡签到等&#xff0c;通过核销盒能快速将订单信息发送到后端进行处理。 一&#xff0c;首先你需要有一台核销设备&#xff0c;也就是核销盒。 二&#xff0c;通过接口激活或更新核销盒 ​​​​​​​ 简要描述 激…

2.Vue — 模板语法、数据绑定、el与data的写法、数据代理

文章目录 一、模板语法1.1 插值语法1.2指令语法 二、数据绑定语法2.1 单向数据绑定2.2 双向数据绑定 三、el与data的两种写法3.1 el3.2 data 四、数据代理4.1 Object.defineProperty4.2 Vue数据代理4.2.1 展示数据代理4.2.2 Vue数据代理 一、模板语法 root容器里面的代码被称为…

边缘计算技术的崭新篇章:赋能未来智能系统

边缘计算是近年来云计算和物联网技术发展的重要趋势。通过将数据处理和分析从云端迁移到设备边缘&#xff0c;边缘计算能够实现更低的延迟和更高的数据安全。本文将探索边缘计算技术的最新进展及其在不同行业中的应用场景。 1. 实时数据处理与决策 在需要快速响应的场景中&…

Seata入门系列【15】@GlobalLock注解使用场景及源码分析

1 前言 在Seata 中提供了一个全局锁注解GlobalLock&#xff0c;字面意思是全局锁&#xff0c;搜索相关文档&#xff0c;发现资料很少&#xff0c;所以分析下它的应用场景和基本原理&#xff0c;首先看下源码中对该注解的说明&#xff1a; // 声明事务仅在单个本地RM中执行 //…

从0到1之微信小程序快速入门(03)

目录 什么是生命周期函数 WXS脚本 ​编辑 与 JavaScript 不同 纯数据字段 组件生命周期 定义生命周期方法 代码示例 组件所在页面的生命周期 代码示例 插槽 什么是插槽 启用多插槽 ​编辑 定义多插槽 组件通信 组件间通信 监听事件 触发事件 获取组件实例 自…

推荐免费的文本转语音工具TTS-Vue【且开源】

标签&#xff1a; 文本转语音&#xff1b; 免费文本转语音软件&#xff1b; 网上有很多文本转语音的工具&#xff0c;但收费具多。 这里推荐一个免费的文本转语音工具。 不需要注册&#xff0c;下载安装就可以使用。且代码开源。 TTS-Vue 软件主页&#xff1a;https://loker…

第06章_多表查询

第06章_多表查询 讲师&#xff1a;尚硅谷-宋红康&#xff08;江湖人称&#xff1a;康师傅&#xff09; 官网&#xff1a;http://www.atguigu.com 多表查询&#xff0c;也称为关联查询&#xff0c;指两个或更多个表一起完成查询操作。 前提条件&#xff1a;这些一起查询的表之…

<蓝桥杯软件赛>零基础备赛20周--第3周--填空题

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周&#xff08;读者可以按…

MySQL(1):开始

概述 DB&#xff1a;数据库&#xff08;Database&#xff09; 即存储数据的“仓库”&#xff0c;其本质是一个文件系统。它保存了一系列有组织的数据。 DBMS&#xff1a;数据库管理系统&#xff08;Database Management System&#xff09; 是一种操纵和管理数据库的大型软件…

mac 安装homebrew ,golang

mac 安装homebrew ,golang 安装homebrew安装golang选择 apple arm 版本安装配置环境变量 安装homebrew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"回车执行指令后&#xff0c;根据提示操作。具体包括以下提示操作&am…

腾讯云轻量服务器“镜像类型”以及“镜像”选择方法

腾讯云轻量应用服务器镜像类型分为应用镜像、系统镜像、Docker基础镜像、自定义镜像和共享镜像&#xff0c;腾讯云百科txybk.com来详细说下不同镜像类型说明和详细介绍&#xff1a; 轻量应用服务器镜像类型说明 腾讯云轻量应用服务器 应用镜像&#xff1a;独有的应用镜像除了包…

API安全之《大话:API的前世今生》

写在前面&#xff1a;本文结合API使用的业界现状&#xff0c;系统性地阐述API的基本概念、发展历史、表现形式等基础内容&#xff0c;主要包含以下内容&#xff1a; 1.什么是API 2.API的发展历史 3.现代API常用消息格式 4.top N 互联网企业API 使用现状 当前的世界是一个信…

九州未来入选“2023边缘计算产业图谱”三大细分领域

10月26日&#xff0c;边缘计算社区正式发布《2023边缘计算产业图谱》&#xff0c;九州未来凭借深厚的技术积累、优秀的产品服务、完善的产品解决方案体系以及开源贡献&#xff0c;实力入选图谱——边缘计算平台、边缘计算开源、边缘云服务提供商三大细分领域&#xff0c;充分彰…

uniapp保存网络图片

先执行下载uni.downloadFile接口&#xff0c;再执行保存图片uni.saveImageToPhotosAlbum接口。 // 保存二维码 saveQrcode() {var _this this;uni.downloadFile({url: _this.qrcodeUrl, //二维码网络图片的地址success(res) {console.log(res);uni.saveImageToPhotosAlbum({fi…

聊聊统一认证中的四种安全认证协议(干货分享)

大家好&#xff0c;我是陈哈哈。单点登录SSO的出现是为了解决众多企业面临的痛点&#xff0c;场景即用户需要登录N个程序或系统&#xff0c;每个程序与系统都有不同的用户名和密码。在企业发展初期&#xff0c;可能仅仅有几个程序时&#xff0c;管理账户和密码不是一件难事。但…

一文彻底理解python浅拷贝和深拷贝

目录 一、必备知识二、基本概念三、列表&#xff0c;元组&#xff0c;集合&#xff0c;字符串&#xff0c;字典浅拷贝3.1 列表3.2 元组3.3 集合3.4 字符串3.5 字典3.6 特别注意可视化展示浅拷贝总结 四、列表&#xff0c;元组&#xff0c;集合&#xff0c;字符串&#xff0c;字…

Python爬虫实战(六)——使用代理IP批量下载高清小姐姐图片(附上完整源码)

文章目录 一、爬取目标二、实现效果三、准备工作四、代理IP4.1 代理IP是什么&#xff1f;4.2 代理IP的好处&#xff1f;4.3 获取代理IP4.4 Python获取代理IP 五、代理实战5.1 导入模块5.2 设置翻页5.3 获取图片链接5.4 下载图片5.5 调用主函数5.6 完整源码5.7 免费代理不够用怎…

【UE 模型描边】UE5中给模型描边 数字孪生 智慧城市领域 提供资源下载

目录 0 引言1 Soft Outlines1.1 虚幻商城1.2 使用步骤 2 Auto Mesh Outlines2.1 虚幻商城2.2 使用步骤 3 Survivor Vision3.1 虚幻商城3.2 使用步骤 结尾 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;UE虚幻引擎专栏&#x1f4a5; 标题&#xf…

C++ 指针

*放在哪里&#xff1f; 如果声明一个变量&#xff1a;int* b; 如果声明多个变量&#xff1a;int a,*b,*c; nullptr c11中NULL的变形&#xff0c;是一个特殊值&#xff0c;可以赋给任意类型的指针&#xff0c;代表该指针指向为空。 this指针 this指针不是一个const Test*(…

关于ABB 机器人多任务的建立

关于ABB 机器人多任务的建立.需要实时监控某一区域&#xff0c;或者某一信号&#xff0c;或者计件到达某一数量机器人自动停止报警&#xff0c;显示到示教器上&#xff0c;多任务可以实现&#xff0c;类似发那科机器人后台逻辑指令 当软件选项漏选或者少选可以选择修改选项&…