zoukankan      html  css  js  c++  java
  • Spring 使用AOP——xml配置

    目录


    AOP介绍

    Spring进行2种实现AOP的方式

    导入jar包

    基于schema-based方式实现AOP

    创建前置通知

    创建后置通知

    修改Spring配置文件

    基于schema-based方式实现环绕通知

    环绕通知介绍

    创建环绕通知类

    修改Spring配置文件

    基于schema-based方式实现异常通知

    异常通知介绍

    创建异常通知类

    修改Spring配置文件

    基于AspectJ方式实现AOP

    AspectJ方式的介绍

    创建很基础的通知类

    修改Spring配置类

    AspectJ方式设置通知绑定参数

    创建方法类,有接收参数的切点方法

    创建通知类

    修改配置

     


    AOP介绍

      AOP(Aspect Oriented Programming),面向切面编程。

      我们在平时编写的代码在执行的时候,都是从上往下开始执行的,比如下面的图示:

      

      

      AOP这个切面,就是指在某一点的前、后,分别进行一些操作,几个简单的例子就是下面这样:

      

       根据AOP的相关内容,介绍上面的有关细节:

      1、actionTwo称为切点(PointCut);

      2、actionBefore称为前置通知(beforeAdvice);

      3、actionAfter称为后置通知(afterAdvice);

      4、由actionBefore、actionTwo、actionAfter共同组成切面(Aspect)

    Spring进行2种实现AOP的方式

      在Spring中,有两种方式可以实现AOP,分别是schema-based、aspectJ,这两种方式的区别在于:

      1、schema-based方式:每个通知(advicor)都需要我们自己创建接口或者类;并且在配置的时候,如果使用xml配置,schema-based方式是在<aop:config>标签中配置的。

      2、aspectJ方式:不需要我们为每个通知创建接口或者类;在配置的时候,如果使用xml配置,aspectJ方式是在<aop:aspect>标签中配置的。

    导入jar包

      Spring对于每一个部分的功能,都单独有一个jar包,但是多数jar包之间都是相互依赖的,所以,为了解决不必要的麻烦,初期阶段可以导入所有的Spring jar包。

      另外,Spring框架还需要commons-logging、log4J的功能支持,所以还需要导入commons-logging、log4J的jar包。

      如果涉及到数据库操作,还需要额外导入jdbc-mysql驱动包

      如果需要集成mybatis,则还需要导入mybatis-spring的jar包。

      对于AOP来说,除了spring框架下关于aop的jar包外,还需要导入下面两个jar包:aspectjweaver.jar包以及其依赖的aopalliance.jar包。

    基于schema-based方式实现AOP

      创建前置通知类

    package cn.ganlixin.advisor;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    /**
     * 创建自定义前置通知类
     */
    public class MyBeforeAdvisor implements MethodBeforeAdvice {
    	/**
    	 * 重写MethodBeforeAdvice中before,就是前置通知执行的代码
    	 * @param arg0 	代表被调用的切点方法签名
    	 * @param arg1 	代表传入切点方法的参数列表
    	 * @param arg2	代表调用的是哪个对象大的切点方法
    	 * @throws Throwable
    	 */
    	@Override
    	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
    		System.out.println("arg0" + arg0);
    		System.out.println("arg1" + Arrays.toString(arg1));
    		System.out.println("arg3" + arg2);
    	}
    }
    

      

      创建后置通知类

    package cn.ganlixin.advisor;
    
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    import org.springframework.aop.AfterReturningAdvice;
    
    /**
     * 自定义的后置通知类
     */
    public class MyAfterAdvisor implements AfterReturningAdvice {
    
    	/**
    	 * 后置通知被调用的时候执行的方法
    	 * @param arg0	切点方法的返回值
    	 * @param arg1	切点方法的签名
    	 * @param arg2	传递给切点方法的参数
    	 * @param arg3	切点方法所在类的对象
    	 * @throws Throwable
    	 */
    	@Override
    	public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
    		System.out.println("arg0 " + arg0);
    		System.out.println("arg1 " + arg1);
    		System.out.println("arg2 " + Arrays.toString(arg2));
    		System.out.println("arg3 " + arg3);
    	}
    	
    }
    

       

      修改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: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/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    	<bean id="demo" class="cn.ganlixin.test.Demo"></bean>
    	
    	<!-- 创建前置通知类bean -->
    	<bean id="myBeforeAdvisor" class="cn.ganlixin.advisor.MyBeforeAdvisor"></bean>
    	
    	<!-- 创建 后置通知类bean -->
    	<bean id="myAfterAdvisor" class="cn.ganlixin.advisor.MyAfterAdvisor"></bean>
    	
    	<aop:config>
    		<!-- 使用<aop:pointcut>来设置切点:
    			满足条件:访问权限,返回类型任意,包名+类名+方法名+参数类型    (访问权限可省,默认为public)-->
    		
    		<!-- 将cn.ganlixin.test包下的Demo类中,接收两个int类型的参数的demo1、任意返回值的方法 设置为切点 -->
    		<aop:pointcut expression="execution(public * cn.ganlixin.test.Demo.demo1(int, int))" id="point_cut1"/>
    		
    		<!-- 将cn.ganlixin.test包下的Demo类中,接收任意参数的、返回值类型任意的demo1方法设置切点 -->
    		<aop:pointcut expression="execution(* cn.ganlixin.test.Demo.demo1(..))" id="point_cut2"/>
    		
    		<!-- 将cn.ganlixin.test包下的Demo类中,只要返回值为String,参数列表任意,全部设置为切点 -->
    		<aop:pointcut expression="execution(String cn.ganlixin.test.Demo.*(..))" id="point_cut3"/>
    		
    		<!-- 将cn.ganlixin.test包下的所有类中的所有方法都设置为切点 -->
    		<aop:pointcut expression="execution(* cn.ganlixin.test.*.*(..))" id="point_cut4"/>
    		
    		<!-- 将满足包名为cn.ganlixin.*.test的包下的所有类中的所有方法都设置为切点 -->
    		<aop:pointcut expression="execution(* cn.ganlixin.*.test.*.*(..))" id="point_cut5"/>
    		
    		<!-- 为指定的切点 设置通知, 直接引用自己创建的通知bean, 运行时会自动判断是前置通知和后置通知 -->
    		<aop:advisor advice-ref="myBeforeAdvisor" pointcut-ref="point_cut3"/>
    		<aop:advisor advice-ref="myAfterAdvisor" pointcut-ref="point_cut3"/>
    		
    	</aop:config>
    
    </beans>
    

      

      测试

    package cn.ganlixin.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
    	
    	public static void main(String[] args) {
    		ApplicationContext ac = new ClassPathXmlApplicationContext("config/applicationContext.xml");
    
    		Demo d = ac.getBean("demo", Demo.class);
    		
    		d.demo2(5, 6);
    	}
    }
    

      

      测试输出结果

    // 自定义前置通知打印的内容
    arg0public java.lang.String cn.ganlixin.test.Demo.demo2(int,int)
    arg1[5, 6]
    arg3cn.ganlixin.test.Demo@dd8ba08
    
    # 切点方法的输出内容
    调用public String demo2(int 5, int 6)
    
    # 自定义后置通知打印的内容
    arg0 hello world
    arg1 public java.lang.String cn.ganlixin.test.Demo.demo2(int,int)
    arg2 [5, 6]
    arg3 cn.ganlixin.test.Demo@dd8ba08
    

      

    基于schema-based方式实现环绕通知

      环绕通知介绍

      servlet中有过滤器filter,可以在接收到请求之后,先一步处理请求,然后交给servlet处理,之后在返回响应之前,再对响应进一步操作之后再返回给客户端。   

      而环绕通知有点类似于filter,但是环绕通知其实是一个拦截器,在处理流程上和filter相似。

      其实就是将前置通知和后置通知结合结合在一起,不用单独创建一个前置通知类和后置通知类,使用schema-based方式就只需要建立一个环绕通知类即可。

      创建环绕通知类

    package cn.ganlixin.advisor;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    /**
     * 自定义环绕通知类
     * @author 13550
     *
     */
    public class MyAroundAdvisor implements MethodInterceptor {
    
    	/**
    	 * 环绕通知执行的代码
    	 * @param arg0	切点方法
    	 * @return
    	 * @throws Throwable
    	 */
    	@Override
    	public Object invoke(MethodInvocation arg0) throws Throwable {
    		
    		System.out.println("环绕通知--前");
    		
    		// 执行切点方法,获得切点方法执行后的返回值
    		Object result = arg0.proceed();
    		
    		System.out.println("环绕通知--后");
    		
    		return result;
    	}
    }
    

      

      修改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: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/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    	<bean id="demo" class="cn.ganlixin.test.Demo"></bean>
    	
    	<!-- 创建环绕通知类 -->
    	<bean id="myAroundAdvisor" class="cn.ganlixin.advisor.MyAroundAdvisor"></bean>
    	
    	<aop:config>
    		<!-- 设置cn.ganlixin.test包下的Demo类中的所有方法都为切点方法 -->
    		<aop:pointcut expression="execution(* cn.ganlixin.test.Demo.*(..))" id="point_cut"/>
    		 <!-- 只需要配置一个advisor, 该advisor中包含了前置通知和后置通知 -->
    		<aop:advisor advice-ref="myAroundAdvisor" pointcut-ref="point_cut"/>
    	</aop:config>
    
    </beans>
    

      

       测试:

    package cn.ganlixin.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
    	
    	public static void main(String[] args) {
    		ApplicationContext ac = new ClassPathXmlApplicationContext("config/applicationContext.xml");
    
    		Demo d = ac.getBean("demo", Demo.class);
    		
    		d.demo2(5, 6);
    	}
    }
    
    
    输出:
    环绕通知--前
    调用public String demo2(int 5, int 6)
    环绕通知--后
    

      

    基于schema-based方式实现异常通知

      异常通知介绍

      我们平时编写function的时候,如果function中可能出现异常,我们一般有两种方式处理异常:

      1、在本function内部自己通过try{  } catch(ex) {   } 来捕获并处理异常。

      2、定义function的时候,通过throws ex来抛出异常,如果出现异常,就将异常交给调用者处理。

      上面两种方式,我们都需要自己在每一个可能出现异常的地方编写处理异常的操作,这样有点麻烦,因为有时候,某个类下的某些function出现异常之后,处理异常的操作都相同,如果每次都单独编写异常处理,就有点冗余,也有点麻烦。

      此时,我们可以使用AOP方式的异常通知:设置切点后,为切点绑定异常通知,不需要在切点方法或者切点方法的调用方处理异常,而是在出现异常的时候,统一调用我们编写的异常通知。

      异常通知的目的不仅限于上面这个原因。

      需要注意的是:如果使用异常通知,那么在切点方法中就不要进行try { } catch,否则不会触发异常通知,可以在定义切点方法的时候使用throws Exception。  

      创建异常通知类

       我们自定义的异常通知类需要继承Spring-aop中ThrowsAdvice接口,这个ThrowsAdvice接口中并没有定义任何抽象方法,但是我们却需要在子类(也就是我们的自定义异常通知类)中定义一个afterThrowing方法,可以参考Spring-aop下ThrowsAdvice接口的说明:

    package org.springframework.aop;
    
    /**
     * Tag interface for throws advice.
     *
     * <p>There are not any methods on this interface, as methods are invoked by
     * reflection. Implementing classes must implement methods of the form:
     *
     * <pre class="code">void afterThrowing([Method, args, target], ThrowableSubclass);</pre>
     *
     * <p>Some examples of valid methods would be:
     *
     * <pre class="code">public void afterThrowing(Exception ex)</pre>
     * <pre class="code">public void afterThrowing(RemoteException)</pre>
     * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, Exception ex)</pre>
     * <pre class="code">public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)</pre>
     */
    public interface ThrowsAdvice extends AfterAdvice {
    
    }
    

      

      上面的介绍中,重点就是,必须在实现类中定义一个名称为afterThrowing的方法,并且方法签名有四种格式:

    public void afterThrowing(Exception ex)
    public void afterThrowing(RemoteException)
    public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
    public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)
    

      上面这四种签名中,第一种和第二种参数是一个Exeception,注意,这里的Exception种类最好指定确切的Exception,如果此处定义Exception和运行时抛出的XxxxException不匹配的话,就不会触发我们自定义的异常通知。

      下面我们创建自定义的异常通知类:

    package cn.ganlixin.advisor;
    
    import org.springframework.aop.ThrowsAdvice;
    
    public class MyExceptionAdvisor implements ThrowsAdvice {
    	/**
    	 * 创建一个异常通知处理方法,只处理ArithmeticException这一种异常
    	 * @param ex
    	 */
    	public void afterThrowing(ArithmeticException e) {
    		System.out.println("触发ArithmeticException异常通知");
    		System.out.println(e.getMessage());
    	}
    	
    	/**
    	 * 重载一个异常通知方法,只处理ArrayIndexOutOfBoundsException这一种异常
    	 * @param e
    	 */
    	public void afterThrowing(ArrayIndexOutOfBoundsException e) {
    		System.out.println("触发ArrayIndexOutOfBoundsException异常通知");
    		System.out.println(e.getMessage());
    	}
    }
    

      

      修改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: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/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    	<bean id="demo" class="cn.ganlixin.test.Demo"></bean>
    	
    	<!-- 创建异常通知类bean -->
    	<bean id="myExceptionAdvisor" class="cn.ganlixin.advisor.MyExceptionAdvisor"></bean>
    	
    	<aop:config>
    		<!-- 设置cn.ganlixin.test包下的Demo类中的所有方法都为切点方法 -->
    		<aop:pointcut expression="execution(* cn.ganlixin.test.Demo.*(..))" id="point_cut"/>
    		
    		<!-- 引用自定义的通知异常bean -->
    		<aop:advisor advice-ref="myExceptionAdvisor" pointcut-ref="point_cut"/>
    	</aop:config>
    
    </beans>
    

      

      修改Demo类,定义两个function,分别触发不同种类的异常:

    package cn.ganlixin.test;
    
    public class Demo {
    	public void demo1() {
    		int[] a = new int[2];
    		a[3] = 9;
    	}
    	
    	public void demo2() {
    		int a = 5/0;
    	}
    }
    

      

      测试:

    package cn.ganlixin.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
    	
    	public static void main(String[] args) {
    		ApplicationContext ac = new ClassPathXmlApplicationContext("config/applicationContext.xml");
    
    		Demo d = ac.getBean("demo", Demo.class);
    		
    		try {
    			d.demo1();
    		} catch (Exception e) {
    			
    		}
    
    		try {
    			d.demo2();
    		} catch (Exception e) {
    			
    		}
    	}
    }
    

      输出:

    触发ArrayIndexOutOfBoundsException异常通知
    3
    触发ArithmeticException异常通知
    / by zero
    

      

    基于AspectJ方式实现AOP

       AspectJ方式的介绍

      前面实现AOP的方式是基于schema-based方式的,这种方式需要我们为每一种通知都单独创建一个通知类,并且实现某个特定方法,然后在配置文件中,每一个通知类都要写一个<aop:advisor>。

      而基于AspectJ的方式,不需要为每一种通知都单独创建一个通知类,而是只创建一个通知类,该类不需要实现某个接口,并且需要在该类中编写多个方法(方法名随意,没有明确要求)。这多个方法的每一个方法都可以对应一种通知,在配置文件中可以绑定方法和指定的通知种类。

      创建很基础的通知类

    package cn.ganlixin.advisor;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    /**
     * 自定义通知类,有多个方法,每个方法可以绑定到不同的通知种类上
     */
    public class MyAdvisor {
    	
    	public void mybefore() {
    		System.out.println("执行前置通知方法");
    	}
    	
    	public void myafter() {
    		System.out.println("执行后置通知方法");
    	}
    	
    	public void myafterReturnging() {
    		System.out.println("执行myafterReturning");
    	}
    	
    	public Object myaround(ProceedingJoinPoint p) throws Throwable {
    		System.out.println("执行环绕通知-前置");
    		// 执行切点方法
    		Object result = p.proceed();
    		System.out.println("执行环绕通知-后置");
    		return result;
    	}
    	
    	public void mythrow() {
    		System.out.println("执行异常通知方法");
    	}
    	
    }
    

      

      修改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: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/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    	<bean id="demo" class="cn.ganlixin.test.Demo"></bean>
    	
    	<!-- 创建通用通知类 -->
    	<bean id="myadvisor" class="cn.ganlixin.advisor.MyAdvisor"></bean>
    	
    	<aop:config>
    		<!-- 如果使用aspectJ方式,就要使用<aop:aspect>标签 -->
    		<aop:aspect ref="myadvisor">
    			<!-- 设置cn.ganlixin.test包下的Demo类中的所有方法都为切点方法 -->
    			<aop:pointcut id="point_cut" expression="execution(* cn.ganlixin.test.Demo.*(..))" />
    			
    			<aop:before method="mybefore" pointcut-ref="point_cut"/>
    			<aop:after method="myafter" pointcut-ref="point_cut"/>
    			<aop:after-returning method="myafterReturning" pointcut-ref="point_cut"/>
    			<aop:after-throwing method="mythrow" pointcut-ref="point_cut"/>
    			
    			<!-- 设置前置和后置之后,不要再设置around
    				<aop:around method="myaround" pointcut-ref="point_cut"/>
    			-->
    		</aop:aspect>
    	</aop:config>
    
    </beans>
    

      介绍一下上面的几个新标签:

      1、<aop:aspect>:使用aspectJ方式实现AOP需要的标签。

      2、<aop:before /> :为指定的切点绑定前置通知,该通知会先于切点方法执行。

      3、<aop:after />:为指定的切点绑定后置通知,会后于切点方法执行。

      4、<aop:after-returning />:为指定的切点绑定后置通知,会后于切点方法执行。

      5、<aop:throwing /> :为指定的切点方法绑定异常通知,当切点方法执行时出现异常,就会执行该通知。

      6、<aop:around />:为指定的切点方法绑定环绕通知。

      其中,需要注意下面几点:

      1、<aop:after-returning /> 标签绑定的通知,只有当切点方法没有出现异常时,该通知才会执行。

      2、<aop:after />标签绑定的通知,不论切点方法是否出现异常,该通知都会执行。

      3、如果同时有<aop:after>和<aop:after-returning />标签,他们的顺序是有意义的,如果切点方法没有出现异常,那么就会按照<aop:after>和<aop:after-returning>出现的顺序执行。

      4、一个<aop:config>标签中可以有多个<aop:aspect>标签,同时,一个<aop:aspect>标签中可以有多个<aop:point-cut>,可以为不同的point-cut绑定通知。

     AspectJ方式设置通知绑定参数

      前面使用aspectJ方式完成AOP的功能,其实是比较简单的,因为我们的通知方法没有接收任何参数(除了环绕通知方法有一个ProceedingJoinPoint类型的参数)。

      Schema-based方式中创建的每个通知类都实现了某个接口,并且重写了某个方法,方法接收多个参数,通过这些参数可以获取切点方法的相关信息。但是上面AspectJ方式,我们的通知方法中却没有接收参数,这是因为如果要接收参数的话,就稍微复杂一点点。

      

      创建切点方法类,有接受参数的切点方法

    package cn.ganlixin.test;
    
    public class Demo {
    	public void demo1(int id, String name) {
    		System.out.println(id + "   " + name);
    	}
    }
    

     

      创建通知类

    package cn.ganlixin.advisor;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class MyAdvisor {
    	
    	/**
    	 * 注意,mybefore方法接收两个参数,并且参数名和切点方法的参数相同(个数、名称)
    	 * @param id
    	 * @param name
    	 */
    	public void mybefore(int id, String name) {
    		System.out.println("前置 " + id + " " + name);
    	}
    }
    

      

      配置文件修改

    <?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"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    	<!-- 创建切点方法的类 -->
    	<bean id="demo" class="cn.ganlixin.test.Demo"></bean>
    	
    	<!-- 创建通用通知类 -->
    	<bean id="myadvisor" class="cn.ganlixin.advisor.MyAdvisor"></bean>
    	
    	<aop:config>
    		<aop:aspect ref="myadvisor">
    			<!-- 设置一个确定的方法为切点方法 -->
    			<aop:pointcut id="point_cut" 
    				expression="execution(* cn.ganlixin.test.Demo.demo1(int, String)) and args(id, name)" />
    		</aop:aspect>
    	</aop:config>
    
    </beans>
    

      

      测试

    package cn.ganlixin.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
    	
    	public static void main(String[] args) {
    		ApplicationContext ac = new ClassPathXmlApplicationContext("config/applicationContext.xml");
    
    		Demo d = ac.getBean("demo", Demo.class);
    		d.demo1(99, "hello");
    	}
    }
    

      

  • 相关阅读:
    创建基于MailKit和MimeKit的.NET基础邮件服务
    MailKit---获取邮件
    C# 与JAVA 的RSA 加密解密交互,互通,C#使用BouncyCastle来实现私钥加密,公钥解密的方法
    .net 开源 FTP 组件 edtFTPnet
    Consul1-window安装consul
    通信传输利器Netty(Net is DotNetty)介绍
    工作中,如何衡量一个人的 JavaScript 编码水平?
    10个有趣又能编译为JavaScript的语言,你用过哪些?
    一定要你明白Java中的volatile
    面试总被问到HTTP缓存机制及原理?看完你就彻底明白了
  • 原文地址:https://www.cnblogs.com/-beyond/p/10482209.html
Copyright © 2011-2022 走看看