• spring----AOP 和 AspectJ


    一、AOP介绍

    什么是AOP

      AOP采用了横向抽取机制取代了传统的纵向继承体系重复性代码

      经典应用:事务管理,缓存,性能监控,安全检查

    AOP实现原理

      实现原理:底层采用代理机制来实现的。

      接口+实现类:spring采用jdk的动态代理Proxy

        只有实现类,spring采用cglib字节码增强

    AOP术语

      target:目标对象,需要被代理的对象,例如UserService

      Joinpoint:连接点,指可能被拦截到的方法,例如UserService中的所有的方法

      PointCut:切入点,已经被增强的连接点,例如UserService中的save() 方法 

      Aspect:切面,切入点和通知的结合,交叉在各个业务逻辑中的系统服务,类似于安全验证,事务处理,日志记录都可以理解为切面 

      advice:通知/增强,切面的实现,切面织入的目标对象,时间点(方法执行前,后,前后,异常)以及内容

      Weaving:织入,将切面代码插入到目标对象某一个方法的过程,相当于我们在jdk动态代理里面的invocationHandler接口方法中的内容

      proxy:代理,一个类被AOP织入增强后,就产生了一个结果代理类

    代理类

      所有的调用的方法,有代理类来完成。代理类会拦截所有的方法,通过代理类来调用相应的方法,并在调用方法前后,执行其他的操作,在使用上还是调用接口中方法。这个可以参考python中的装饰器,大同小异。

      

    二、手动方式编写代理(代理不依赖xml)

    JDK动态代理

    jdk动态代理是对"装饰者"设计模式的简化,使用前提,必须有接口

    service

    public interface UserService {
        void save();
        void delete();
    }
    

    serviceImpl

    public class UserServiceImpl implements UserService {
        @Override
        public void save() {
            System.out.println("保存数据完毕");
        }
    
        @Override
        public void delete() {
            System.out.println("删除数据完毕");
        }
    }
    

    MyAspect

    public class MyAspect {
        public void before() {
            System.out.println("开启事务");
        }
    
        public void after() {
            System.out.println("结束事务");
        }
    }

    MybeanFactory(生成代理对象)

    public class MyBeanFactory {
        public static UserService createUserService(){
         //目标类
            UserService userService = new UserServiceImpl();
            //切面类
         MyAspect myAspect = new MyAspect();
            /**
             * 参数1:loader,类加载器,动态代理类运行时创建,任何类都需要类加载器将类加载到内存中
             *       一般写法, 当前类.class.getClassLoader()
             * 参数2:interfaces,代理类需要实现的所有的接口
             *       写法1,目标类实例对象.getClass().getInterfaces(),只能获得自己类的所有的接口,不能获得父类的接口
             *       写法2,new Class[]{目标类接口.class}
             * 参数3:invocationHandle,处理类,接口,一般采用匿名内部类实例化接口
             *       提供了一个invoke方法,代理类的每一个方法执行的时候,都会调用invoke方法;
             */
            UserService proxy = (UserService) Proxy.newProxyInstance(MyBeanFactory.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() {
                /**
                 *
                 * @param proxy:代理对象
                 * @param method:代理对象当前执行方法的描述对象(反射)
                 *               method.getName():方法名
                 *               method.invoke(目标类对象,args)
                 * @param args:方法传递的参数
                 * @return
                 * @throws Throwable
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    myAspect.before();
                    Object obj = method.invoke(userService,args);
                    myAspect.after();
                    return obj;
                }
            });
            return proxy;
        }
    }

    测试

    public class Test{
        public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            UserService userService = MyBeanFactory.createUserService();
            userService.save();
            userService.delete();
        }
    }
    

      

    cglib字节码增强

    没有接口,只有实现类

    需要导入核心的两个jar包(sam,cglib),但是spring-core已经包含了这两个jar包 

    UserServiceImpl

    public class UserServiceImpl{
    
        public void save() {
            System.out.println("保存数据完毕");
        }
    
        public void delete() {
            System.out.println("删除数据完毕");
        }
    }

     MyAspect

    public class MyAspect {
        public void before() {
            System.out.println("开启事务");
        }
    
        public void after() {
            System.out.println("结束事务");
        }
    }
    

    MyBeanFactory

    public class MyBeanFactory {
        public static UserServiceImpl createUserService(){
            UserServiceImpl userServiceImpl = new UserServiceImpl();
            MyAspect myAspect = new MyAspect();
    
            /**
             * 代理类,采用cglib,底层创建目标类的子类
             */
            //核心类
            Enhancer enhancer = new Enhancer();
            //确定父类
            enhancer.setSuperclass(userServiceImpl.getClass());
            //设置回调
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    myAspect.before();
                    //执行目标类的方法
                    Object obj = method.invoke(userServiceImpl,args);
                    //methodProxy.invokeSuper(proxy,args); 等同上面的代码(执行代理类父类的方法,即执行目标类)
                    myAspect.after();
                    return obj;
                }
            });
            //创建代理
            UserServiceImpl userServiceProxy = (UserServiceImpl)enhancer.create();
            return userServiceProxy;
        }
    }
    

    测试

    public class Test{
        public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            //手动创建代理
         UserServiceImpl userServiceimpl = MyBeanFactory.createUserService();
         userServiceimpl.save();
         userServiceimpl.delete();
        }
    }
    

      

    三、编写代理(半自动)

    UsersService

    public interface UserService {
        void save();
        void delete();
    }
    

    UserServiceImpl

    public class UserServiceImpl implements UserService{
    
        public void save() {
            System.out.println("保存数据完毕");
        }
    
        public void delete() {
            System.out.println("删除数据完毕");
        }
    }
    

    MyAspect (切面类,可以理解拦截器)

    AOP联盟通知类型

    前置通知   MethodBeforeAdvice:在目标方法执行前实施增强

    后置通知   AfterReturningAdvice:在目标方法执行后实施后增强

    环绕通知   MethodInterceptor :在目标方法执行前后实施增强

    异常抛出通知  ThrowsAdvice:在方法抛出异常后实施增强

    引介通知   IntroductionInterceptor:在目标类中添加一些新的方法和属性

    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class MyAspect implements MethodInterceptor {
        public static void before() {
            System.out.println("开启事务");
        }
    
        public static void after() {
            System.out.println("结束事务");
        }
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            MyAspect.before();
            Object obj = methodInvocation.proceed();
            MyAspect.after();
            return obj;
        }
    }

    xml配置可以简单理解,配置了接口和实现类,表示调用的目标对象,配置了切面类(拦截器),拦截方法的调用(拦截后,可以选择处理方法,也可以忽略方法)

    xml配置取代了手动编写代理(jdk动态代理)中的MyBeanFactory,由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"
           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">
    
    <!--    目标类-->
        <bean id="userServiceImpl" class="com.test.UserServiceImpl"></bean>
    <!--    切面类-->
        <bean id="myAspect" class="com.test.MyAspect"></bean>
    <!--
        创建代理类
        使用工厂 factory bean,底层调用getObject(),返回特殊的Bean
        ProxyFactoryBean 用于创建工厂bean,生成特殊的代理对象
            interfaces:确定接口
                如果只有一个接口 可以简写<property name="interfaces" value="">
                如果有多个接口,就使用<array>
    
        底层:
            如果有接口,默认采用jdk动态代理
            如果没有接口,应该采用cglib(但是测试该是使用的是jdk动态代理)
            如果加上 <property name="optimize" value="true"></property>,表示一定是采用了cglib字节码增强
    -->
    
        <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="interfaces">
                <array>
                    <value>com.test.UserService</value>
                </array>
            </property>
            <property name="target" ref="userServiceImpl"></property>
            <property name="interceptorNames" value="myAspect"></property>
    <!--        <property name="optimize" value="true"></property>-->
        </bean>
    </beans>

    测试

    public class Test{
        public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
            //有spring创建代理
            UserService userServiceproxy = (UserService) classPathXmlApplicationContext.getBean("proxy");
            userServiceproxy.save();
            userServiceproxy.delete();
        }
    }
    

      

    四、编写代理(全自动,重点)

    从spring容器中获得对象,如果配置了aop,将自动生成代理

    导包,aspectj

    <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.4</version>
    </dependency>

    这个包也可以com.springsource.org.aspectj.weaver-1.7.2.RELEASE.jar,但是maven下载不下来。可以手动添加

    UserService

    public interface UserService {
        void save();
        void delete();
    }
    

    UserServiceImpl

    public class UserServiceImpl implements UserService{
        public void save() {
            System.out.println("保存数据完毕");
        }
        public void delete() {
            System.out.println("删除数据完毕");
        }
    }
    

    MyAspect

    public class MyAspect implements MethodInterceptor {
        public static void before() {
            System.out.println("开启事务");
        }
    
        public static void after() {
            System.out.println("结束事务");
        }
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            MyAspect.before();
            Object obj = methodInvocation.proceed();
            MyAspect.after();
            return obj;
        }
    }
    

    xml

    将<aop: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:aop="http://www.springframework.org/schema/aop"
           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
           http://www.springframework.org/schema/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!--    目标类-->
        <bean id="userServiceImpl" class="com.test.UserServiceImpl"></bean>
    <!--    切面类-->
        <bean id="myAspect" class="com.test.MyAspect"></bean>
    
    <!--
        aop编程
        导入命名空间(xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd")
    
        使用<aop:config>进行配置
                默认使用jdk动态代理
                如果添加上 proxy-target-class="true",使用cglib代理
            <aop:pointcut> 切入点:从目标对象获取具体方法
                expression:里面写切入点表达式
                    格式:execution(* com.test.UserServiceImpl.*(..))
                        * 表示所有的方法返回值任意
                        * 表示类中的所有的方法
                        (..) 表示方法中的传递参数任意
                        修改:execution(* com.test.*.*(..))
                        将类名变成 * :表示所有的类
            <aop:advisor>  特殊的切面:只有一个通知和一个切入点
                advice-ref:配置通知
                pointcut-ref:配置切入点
    -->
        <aop:config proxy-target-class="true">
            <aop:pointcut expression="execution(* com.test.UserServiceImpl.*(..))" id="myPointCut"></aop:pointcut>
            <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor>
        </aop:config>
    </beans>
    

    测试

    public class Test{
        public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
            UserService userService = (UserService) classPathXmlApplicationContext.getBean("userServiceImpl");
            userService.save();
            userService.delete();
        }
    }
    

      

    五、Aspectj

    aspectj是一个基于java语言的aop框架

    5.1切入点表达式

    1、execution() :用于描述方法

    语法:execution(修饰符 返回值 包.类.方法(参数) throws 异常)
      修饰符;一般省略
        public:公共方法
        *        :任意
      返回值:
        Void      :没有返回值
        String   :返回字符串
        *           :返回任意
      包,[可以省略]
        com.itheima.crm 固定包
        com.itheima.crm.*.service crm包下面子包任意 (例如:com.itheima.crm.staff.service)
        com.itheima.crm.. crm包下面的所有子包(含自己)
        com.itheima.crm.*.service.. crm包下面任意子包,固定目录service,service目录任意包
      类,[可以省略]
        UserServiceImpl 指定类
        *Impl 以Impl结尾
        User* 以User开头
        * 任意
      方法名,不能省略
        addUser 固定方法
        add* 以add开头
        *Do 以Do结尾
        * 任意
      (参数)
        () 无参
        (int) 一个整型
        (int ,int) 两个
        (..) 参数任意
      throws ,可省略,一般不写。

    综合1

      execution(* com.itheima.crm.*.service..*.*(..))

    综合2

      <aop:pointcut expression="execution(* com.itheima.*WithCommit.*(..)) || execution(* com.itheima.*Service.*(..))" id="myPointCut"/>

    2、within:匹配包或子包中的方法(了解)

      within(com.itheima.aop..*)

    3、this:匹配实现接口的代理对象中的方法(了解)

      this(com.itheima.aop.user.UserDAO)

    4、target:匹配实现接口的目标对象中的方法(了解)

      target(com.itheima.aop.user.UserDAO)

    5、args:匹配参数格式符合标准的方法(了解)

      args(int,int)

    6、bean(id) 对指定的bean所有的方法(了解)

      bean('userServiceId')

    5.2 AspectJ 通知类型

      aop联盟定义通知类型,具有特性接口,必须实现,从而确定方法名称。

      aspectj 通知类型,只定义类型名称。以及方法格式。

      个数:6种,知道5种,掌握1中。

      before:前置通知(应用:各种校验)
        在方法执行前执行,如果通知抛出异常,阻止方法运行
      afterReturning:后置通知(应用:常规数据处理)
        方法正常返回后执行,如果方法中抛出异常,通知无法执行
        必须在方法执行后才执行,所以可以获得方法的返回值。
      around:环绕通知(应用:十分强大,可以做任何事情)
        方法执行前后分别执行,可以阻止方法的执行
        必须手动执行目标方法
      afterThrowing:抛出异常通知(应用:包装异常信息)
        方法抛出异常后执行,如果方法没有抛出异常,无法执行
      after:最终通知(应用:清理现场)
        方法执行完毕后执行,无论方法中是否出现异常

    5.3导入jar包

    aop联盟规范、spring aop 实现、aspect 规范、spring aspect 实现 spring-aspects.jar

    在maven中,只需要依赖sprint-context和aspectjweaver这连个jar包即可测试

    5.4 基于xml

     UserService

    public interface UserService {
        void save();
        void delete();
    }
    

    UserviceImpl

    public class UserServiceImpl implements UserService{
        public void save() {
            //int a = 1/0; //测试抛出异常
            System.out.println("保存数据完毕");
        }
        public void delete() {
            System.out.println("删除数据完毕");
        }
    }
    

    MyAspect

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    
    
    public class MyAspect{
        /**
         *
         * @param joinPoint:通知方法可以加上参数,用于描述连接点(目标方法)
         */
        public static void before(JoinPoint joinPoint) {
            System.out.println("前置通知--method="+joinPoint.getSignature().getName());
    
        }
    
        /**
         *
         * @param ret 方法的返回值,参数名字(ret)必须和xml配置中的一样[<aop:after-returning method="after" pointcut="execution(* com.test.UserServiceImpl.save(..))" returning="ret"></aop:after-returning>]
         */
        public static void afterReturning(JoinPoint joinPoint,Object ret) {
            System.out.println("后置通知--method="+joinPoint.getSignature().getName()+"--返回值="+ret);
        }
    
    
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            //需要手动执行目标方法;会抛出异常
            Object object = proceedingJoinPoint.proceed();
            System.out.println("环绕通知--method="+proceedingJoinPoint.getSignature().getName());
            return object;
        }
    
        /**
         *
         * @param e:异常对象,参数名(e)由xml配置[<aop:after-throwing method="throwing" pointcut-ref="myPointCut" throwing="e" />]
         */
        public void throwing(Throwable e){
            System.out.println("异常通知="+e.getMessage());
        }
        
        public void after(JoinPoint joinPoint){
            System.out.println("最终通知,method方法:"+joinPoint.getSignature().getName()+"结束");
        }
    }

    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:aop="http://www.springframework.org/schema/aop"
           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
           http://www.springframework.org/schema/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!--    目标类-->
        <bean id="userServiceImpl" class="com.test.UserServiceImpl"></bean>
    <!--    切面类-->
        <bean id="myAspect" class="com.test.MyAspect"></bean>
        <!--
        aop编程
        <aop:aspect></aop:aspect> :将切面类声明"切面",从而获得通知方法;
            ref:切面类引用
        <aop:pointcut>声明一个切入点,所有的通知都可以使用
            id:用于其他通知引用
            execution():切入点表达式
        -->
        <aop:config>
            <aop:aspect ref="myAspect">
    <!--
                <aop:pointcut> 切入点,可以给所有通知使用;
    -->
                <aop:pointcut expression="execution(* com.test.UserServiceImpl.*(..))" id="myPointCut"></aop:pointcut>
    <!--
                method:通知方法
                pointcut:切入点表达式,此表达式只能当前通知使用
                pointcut-ref:切入点引用,可以与其他通知共享切入点;
    -->
                <aop:before method="before" pointcut-ref="myPointCut"></aop:before>
    <!--
                后置方法可以获得方法的返回值
    -->
                <aop:after-returning method="afterReturning" pointcut="execution(* com.test.UserServiceImpl.save(..))" returning="ret"></aop:after-returning>
    <!--
                环绕通知
    -->
                <aop:around method="around" pointcut="execution(* com.test.UserServiceImpl.delete(..))"></aop:around>
    <!--
                异常通知
    -->
                <aop:after-throwing method="throwing" pointcut-ref="myPointCut" throwing="e"></aop:after-throwing>
    <!--
                最终通知
    -->
                <aop:after method="after" pointcut-ref="myPointCut"></aop:after>
            </aop:aspect>
        </aop:config>
    </beans>
    

    测试

    public class Test{
        public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
            UserService userService = (UserService) classPathXmlApplicationContext.getBean("userServiceImpl");
            userService.save();
            userService.delete();
        }
    }

     

    5.5 基于注解

     UserService

    public interface UserService {
        void save();
        void delete();
    }
    

    UserServiceImpl

    @Service("userServiceImpl")
    public class UserServiceImpl implements UserService{
    
        public void save() {
            //int a = 1/0; //测试抛出异常
            System.out.println("保存数据完毕");
        }
    
        public void delete() {
            System.out.println("删除数据完毕");
        }
    }
    

    MyAspect(切面类)

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    //@Aspect 声明切面类
    @Component
    @Aspect
    public class MyAspect{
        //配置公共的切入点;
        @Pointcut("execution(* com.test.UserServiceImpl.*(..)))")
        private void myPointCut(){
    
        }
        /**
         *
         * @param joinPoint:通知方法可以加上参数,用于描述连接点(目标方法)
         */
        @Before("execution(* com.test.UserServiceImpl.*(..))")
        public static void before(JoinPoint joinPoint) {
            System.out.println("前置通知--method="+joinPoint.getSignature().getName());
    
        }
    
        /**
         *
         * @param ret 方法的返回值,参数名字(ret)必须和xml配置中的一样[<aop:after-returning method="after" pointcut="execution(* com.test.UserServiceImpl.save(..))" returning="ret"></aop:after-returning>]
         */
        @AfterReturning(value = "execution(* com.test.UserServiceImpl.save(..))",returning = "ret")
        public static void afterReturning(JoinPoint joinPoint,Object ret) {
            System.out.println("后置通知--method="+joinPoint.getSignature().getName()+"--返回值="+ret);
        }
    
        @Around("execution(* com.test.UserServiceImpl.delete(..))")
        public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            //需要手动执行目标方法;会抛出异常
            Object object = proceedingJoinPoint.proceed();
            System.out.println("环绕通知--method="+proceedingJoinPoint.getSignature().getName());
            return object;
        }
    
        /**
         *
         * @param e:异常对象,参数名(e)由xml配置[<aop:after-throwing method="throwing" pointcut-ref="myPointCut" throwing="e" />]
         */
        @AfterThrowing(value = "execution(* com.test.UserServiceImpl.save(..))",throwing = "e")
        public void throwing(Throwable e){
            System.out.println("异常通知="+e.getMessage());
        }
        @After(value = "myPointCut()")
        public void after(JoinPoint joinPoint){
            System.out.println("最终通知,method方法:"+joinPoint.getSignature().getName()+"结束");
        }
    }
    

    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:aop="http://www.springframework.org/schema/aop"
           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
           http://www.springframework.org/schema/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd">
    
            <context:component-scan base-package="com.test"></context:component-scan>
    <!--
            确定aop注解生效(Aspect)
    -->
            <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    

    测试

    public class Test{
        public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    
            ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
            UserService userService = (UserService) classPathXmlApplicationContext.getBean("userServiceImpl");
            userService.save();
            userService.delete();
        }
    } 

    spring Boot 整合AspectJ

    使用 @EnableAspectJAutoProxy 代替了配置文件

    @SpringBootApplication
    @EnableAspectJAutoProxy
    public class SpringbootProjectApplication {
        public static void main(String[] args) {
            SpringApplication.run(SpringbootProjectApplication.class, args);
        }
    }
    

      

  • 相关阅读:
    ECDSA—模乘模块
    ECDSA—模加减模块
    复微杯参赛感悟与总结
    利用system generator 生成vivado ip—以低通滤波器举例
    科普—为什么要用ECDSA加签及其数学上的验签证明
    查看CentOS版本号
    查看MySQL的版本号
    MySQL修改端口号
    CentOS7上安装MySQL
    CentOS7中装MySQL & yum install mysql-community-server问题
  • 原文地址:https://www.cnblogs.com/yanxiaoge/p/11232823.html
Copyright © 2020-2023  润新知