黑马程序员---代理

2024-09-07 16:18
文章标签 程序员 黑马 代理

本文主要是介绍黑马程序员---代理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

分析代理类的作用与原理及AOP的概念

代理的概念与作用 

1.已经写好一个类,现在要为这个类增加一些功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
现在我们写一个代理类:

保持了原来那个类的功能,又增加了你现在需要的功能。

主函数调用的时候,直接调用代理类就行了。

这就是代理类的功能。

 

2.编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。

代理架构图:

 
3. 如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。

 AOP

1.系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:

                              安全       事务         日志

StudentService  ------|----------|------------|-------------

CourseService   ------|----------|------------|-------------

MiscService       ------|----------|------------|-------------

安全,事务,日志等功能要贯穿到好多个模块中,所以,它们就是交叉业务。

2.用具体的程序代码描述交叉业务:

method1         method2          method3

{                      {                       {

------------------------------------------------------切面

....            ....              ......

------------------------------------------------------切面

}                       }                       }

安全,事务,日志等这些功能,就是一个个的方面,不是一个点,而是一个面。是一个问题域。

3. 交叉业务的编程问题即为 面向方面的编程( Aspect oriented program ,简称AOP ), AOP 的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:

------------------------------------------------------切面

func1         func2            func3

{             {                {

....            ....              ......

}             }                }

------------------------------------------------------切面

4. 使用代理技术正好可以解决这种问题,代理是实现 AOP 功能的核心和关键技术。

    重要原则:不要把供货商暴露给你的客户。

 

动态代理技术

1.要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
 
2.JVM 可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
 
3.JVM 生成的动态类必须实现一个或多个接口,所以, JVM 生成的动态类只能用作具有相同接口的目标类的代理。
 
4.CGLIB 库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用 CGLIB 库。
 
5.代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
Ø 1. 在调用目标方法之前
Ø 2. 在调用目标方法之后
Ø 3. 在调用目标方法前后
Ø 4. 在处理目标方法异常的 catch 块中

需要写一个示意代码进行辅助说明,例如:

Class proxy{
void sayHello(){
……….
try{
target.sayHello();
}catch(Exception e){
………..
}
………….
}
}

 分析JVM动态生成的类

1.创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。
java.lang.reflect. Proxy 类:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
static Class<?>getProxyClass(ClassLoader loader,Class<?>... interfaces)
          返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
package cn.itcast.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) {
//java.lang.reflect.Proxy类中的方法:
//static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
//返回代理类的java.lang.Class对象,并向其提供类加载器和接口数组。
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());
//既然是个类,那么肯定有构造方法或者方法
//首先看看这个类有什么构造方法:
System.out.println("----------------begin constructors list--------------------");
Constructor[] constructors = clazzProxy1.getConstructors();
for(Constructor constructor : constructors)
{
StringBuilder sb = new StringBuilder();
sb.append(constructor.getName());
sb.append('(');
Class[] clazzParams = constructor.getParameterTypes();
if(clazzParams.length!=0 || clazzParams!=null)
{
for(Class clazzParam : clazzParams)
{
sb.append(clazzParam.getName()+',');
}
}
if(sb.charAt(sb.length()-1) != '(')
{
sb.deleteCharAt(sb.length()-1);	
}
sb.append(')');	
System.out.println(sb.toString());
}		
System.out.println("----------------begin methods list--------------------");
Method[] methods = clazzProxy1.getMethods();
for(Method method : methods)
{
StringBuilder sb = new StringBuilder();
sb.append(method.getName());
sb.append('(');
Class[] clazzParams = method.getParameterTypes();
if(clazzParams.length!=0 || clazzParams!=null)
{
for(Class clazzParam : clazzParams)
{
sb.append(clazzParam.getName()+',');
}
}
if(sb.charAt(sb.length()-1) != '(')
{
sb.deleteCharAt(sb.length()-1);	
}
sb.append(')');	
System.out.println(sb.toString());
}		
}
}
 
运行结果:
com.sun.proxy.$Proxy0
----------------begin constructors list--------------------
com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
----------------begin methods list--------------------
add(java.lang.Object)
remove(java.lang.Object)
equals(java.lang.Object)
toString()
hashCode()
clear()
contains(java.lang.Object)
isEmpty()
size()
toArray()
toArray([Ljava.lang.Object;)
addAll(java.util.Collection)
iterator()
containsAll(java.util.Collection)
removeAll(java.util.Collection)
retainAll(java.util.Collection)
isProxyClass(java.lang.Class)
getInvocationHandler(java.lang.Object)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
wait(long,int)
wait(long)
wait()
getClass()
notify()
notifyAll()
2. 编码列出动态类中的所有构造方法和参数签名
 同上
3. 编码列出动态类中的所有方法和参数签名
 同上
4.创建动态类的实例对象
Ø 用反射获得构造方法
得到的唯一构造方法是:com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
newInstance的时候需要用InvocationHandler的实例作为参数传进去。
java.lang.reflect.InvocationHandler是个接口,需要我们自己写一个类去继承它。
Ø 编写一个最简单的InvocationHandler类
Ø 调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去
Ø 打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。
package cn.itcast.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception{
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//既然是个类,那么肯定有构造方法或者方法
//首先看看这个类有什么构造方法:
System.out.println("----------------begin constructors list--------------------");
Constructor[] constructors = clazzProxy1.getConstructors();
for(Constructor constructor : constructors)
{
StringBuilder sb = new StringBuilder();
sb.append(constructor.getName());
sb.append('(');
Class[] clazzParams = constructor.getParameterTypes();
if(clazzParams.length!=0 || clazzParams!=null)
{
for(Class clazzParam : clazzParams)
{
sb.append(clazzParam.getName()+',');
}
}
if(sb.charAt(sb.length()-1) != '(')
{
sb.deleteCharAt(sb.length()-1);	
}
sb.append(')');	
System.out.println(sb.toString());
}		
System.out.println("----------------begin create instance object--------------------");
//Object obj = clazzProxy1.newInstance();//调用空参构造方法,可是我们从上面看到此类没有空参构造,所以报异常。
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
class MyInvocationHandler1 implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());
System.out.println(proxy1.toString());
//proxy1.size();//有返回值的方法不能调用,会报异常,这是为什么呢?
proxy1.clear();		
}
}

Ø 将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。
package cn.itcast.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception{
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println("----------------begin create instance object--------------------");
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);	
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
System.out.println(proxy2.toString());		
}
}

5.总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息?
Ø 三个方面:
生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;
产生的类字节码必须有个一个关联的类加载器对象;
生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。
 
6.用Proxy.newInstance方法直接一步就创建出代理对象。
java.lang.reflect.Proxy类
创建某一接口 Foo 的代理:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);
也就是说,创建某一接口的代理:首先调用Proxy的静态方法 newProxyInstance(这个接口的类加载器,这些接口的类字节码对象组成的数组,InvocationHandler对象)
注意
static ObjectnewProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)这里的参数不能用可变参数!
          返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
因为可变参数必须是最后一个参数,所以这里不能用可变参数。
package cn.itcast.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception{
//java.lang.reflect.Proxy类中的方法:
//static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
//返回代理类的java.lang.Class对象,并向其提供类加载器和接口数组。
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());
//既然是个类,那么肯定有构造方法或者方法
//首先看看这个类有什么构造方法:
System.out.println("----------------begin constructors list--------------------");
Constructor[] constructors = clazzProxy1.getConstructors();
for(Constructor constructor : constructors)
{
StringBuilder sb = new StringBuilder();
sb.append(constructor.getName());
sb.append('(');
Class[] clazzParams = constructor.getParameterTypes();
if(clazzParams.length!=0 || clazzParams!=null)
{
for(Class clazzParam : clazzParams)
{
sb.append(clazzParam.getName()+',');
}
}
if(sb.charAt(sb.length()-1) != '(')
{
sb.deleteCharAt(sb.length()-1);	
}
sb.append(')');	
System.out.println(sb.toString());
}		
System.out.println("----------------begin methods list--------------------");
Method[] methods = clazzProxy1.getMethods();
for(Method method : methods)
{
StringBuilder sb = new StringBuilder();
sb.append(method.getName());
sb.append('(');
Class[] clazzParams = method.getParameterTypes();
if(clazzParams.length!=0 || clazzParams!=null)
{
for(Class clazzParam : clazzParams)
{
sb.append(clazzParam.getName()+',');
}
}
if(sb.charAt(sb.length()-1) != '(')
{
sb.deleteCharAt(sb.length()-1);	
}
sb.append(')');	
System.out.println(sb.toString());
}
System.out.println("----------------begin create instance object--------------------");
//Object obj = clazzProxy1.newInstance();//调用空参构造方法,可是我们从上面看到此类没有空参构造,所以报异常。
Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
class MyInvocationHandler1 implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
}
Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());
System.out.println(proxy1.toString());
//proxy1.size();//有返回值的方法不能调用,会报异常,这是为什么呢?
proxy1.clear();	
Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return null;
}
});
System.out.println(proxy2.toString());
Collection proxy3 = (Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(), 
new Class[] {Collection.class}, 
new InvocationHandler() {
ArrayList target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long beginTime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+" running time of "+(endTime-beginTime));
return retVal;
}			
});
proxy3.add("zxx");
proxy3.add("lxx");
proxy3.add("yxx");
System.out.println(proxy3.size());
}
}

运行结果:

add running time of 0
add running time of 0
add running time of 0
size running time of 1
3

 

猜想分析动态生成的类的内部代码

1.动态生成的类实现了 Collection 接口(可以实现若干接口),生成的类有 Collection 接口中的所有方法和一个如下接受 InvocationHandler 参数的构造方法。
2.构造方法接受一个 InvocationHandler 对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?
$Proxy0 implements Collection
{
InvocationHandler handler;
public $Proxy0(InvocationHandler handler)
{
this.handler = handler;
}
}
3.实现 Collection 接口的动态类中的各个方法的代码又是怎样的呢?
$Proxy0 implements Collection
{
InvocationHandler handler;
public $Proxy0(InvocationHandler handler)
{
this.handler = handler;
}
//生成的Collection接口中的方法的运行原理
int size()
{
return handler.invoke(this,this.getClass().getMethod("size"),null);
}
void clear(){
return handler.invoke(this,this.getClass().getMethod("clear"),null);
}
boolean add(Object obj){
return handler.invoke(this,this.getClass().getMethod("add"),obj);
}
}
InvocationHandler 接口中定义的 invoke 方法接受的三个参数又是什么意思? 图解说明如下:
Client 程序调用 objProxy.add(“abc”) 方法时,涉及三要素: objProxy 对象、 add 方法、“ abc” 参数
Class Proxy$ {
add(Object object) {
return handler.invoke(Object proxy, Method method, Object[] args);
}
}

4.分析先前打印动态类的实例对象时,结果为什么会是null呢?调用有基本类型返回值的方法时为什么会出现NullPointerException异常?

5.分析为什么动态类的实例对象的 getClass() 方法返回了正确结果呢?
Ø 调用调用代理对象的从 Object 类继承的 hashCode, equals, toString 这几个方法时,代理对象将调用请求转发给 InvocationHandler 对象,对于其他方法,则不转发调用请求。

让动态生成的类成为目标类的代理

1.分析动态代理的工作原理图:

 

2. 怎样将目标类传进去?
Ø 直接在 InvocationHandler 实现类中创建目标类的实例对象,可以看运行效果和加入日志代码,但没有实际意义。
Ø InvocationHandler 实现类注入目标类的实例对象,不能采用匿名内部类的形式了。
Ø 让匿名的 InvocationHandler 实现类访问外面方法中的目标类实例对象的 final 类型的引用变量。
 
3.将创建代理的过程改为一种更优雅的方式, eclipse 重构出一个 getProxy 方法绑定接收目标同时返回代理对象,让调用者更懒惰,更方便,调用者甚至不用接触任何代理的 API
 
4.将系统功能代码模块化,即将切面代码也改为通过参数形式提供,怎样把要执行的系统功能代码以参数形式提供?
Ø 把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接收者只要调用这个对象的方法,即等于执行了外界提供的代码!
Ø bind 方法增加一个 Advice 参数。

接口Advice.java:

package cn.itcast.day3;
import java.lang.reflect.Method;
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}

MyAdvice.java:

package cn.itcast.day3;
import java.lang.reflect.Method;
public class MyAdvice implements Advice {
long beginTime = 0;
@Override
public void beforeMethod(Method method) {
System.out.println("来传智播客学习了");
beginTime = System.currentTimeMillis();
}
@Override
public void afterMethod(Method method) {
System.out.println("从传智播客毕业上班啦");
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+" running time of "+(endTime-beginTime));
}
}

ProxyTest.java:

package cn.itcast.day3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest {
public static void main(String[] args) throws Exception{
final ArrayList target = new ArrayList();//内部类里想访问局部变量,加final修饰符。
Collection proxy3 = (Collection)getProxy(target,new MyAdvice());
proxy3.add("zxx");
proxy3.add("lxx");
proxy3.add("yxx");
System.out.println(proxy3.size());
}
private static Object getProxy(final Object target, final Advice advice) {
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),  
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
});
return proxy3;
}
}

实现类似spring的可配置的AOP框架 

实现AOP功能的封装与配置

1.工厂类 BeanFactory 负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其 getBean 方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是 ProxyFactoryBean ,则直接返回该类的实例对象,否则,返回该类实例对象的 getProxy 方法返回的对象。
 
2.BeanFactory 的构造方法接收代表配置文件的输入流对象,配置文件格式如下:

  #xxx=java.util.ArrayList

  xxx=cn.itcast.ProxyFactoryBean

  xxx.target=java.util.ArrayList

  xxx.advice=cn.itcast.MyAdvice

 
3.ProxyFacotryBean 充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
Ø 目标
Ø 通知
 
4.编写客户端应用:
Ø 编写实现 Advice 接口的类和在配置文件中进行配置
Ø 调用 BeanFactory 获取对象

 

Advice.java:

package cn.itcast.day3;
import java.lang.reflect.Method;
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}

MyAdvice.java:

package cn.itcast.day3;
import java.lang.reflect.Method;
public class MyAdvice implements Advice {
long beginTime = 0;
@Override
public void beforeMethod(Method method) {
System.out.println("来传智播客学习了");
beginTime = System.currentTimeMillis();
}
@Override
public void afterMethod(Method method) {
System.out.println("从传智播客毕业上班啦");
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+" running time of "+(endTime-beginTime));
}
}

BeanFactory.java:

package cn.itcast.day3.aopframework;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import cn.itcast.day3.Advice;
public class BeanFactory {
Properties props = new Properties();
public BeanFactory(InputStream ips) {
try {
props.load(ips);
} catch (IOException e) {
e.printStackTrace();
}
}
public Object getBean(String name) {
String className = props.getProperty(name);
Object bean = null;
try {
Class clazz = Class.forName(className);
bean = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
} 
if(bean instanceof ProxyFactoryBean)
{
ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
Object target = null;
Advice advice = null;;
try {
target = Class.forName(props.getProperty(name+".target")).newInstance();
advice = (Advice)Class.forName(props.getProperty(name+".advice")).newInstance();
} catch (Exception e) {
e.printStackTrace();
} 
proxyFactoryBean.setTarget(target);
proxyFactoryBean.setAdvice(advice);
Object proxy = ((ProxyFactoryBean)bean).getProxy();
return proxy;
}
return bean;
}
}

ProxyFactoryBean.java:

package cn.itcast.day3.aopframework;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import cn.itcast.day3.Advice;
public class ProxyFactoryBean {	
private Object target;
private Advice advice;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}	
public Object getProxy() {
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),  
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
});
return proxy3;
}
}

AopFrameworkTest.java:

package cn.itcast.day3.aopframework;
import java.io.InputStream;
public class AopFrameworkTest {
public static void main(String[] args) {
InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");
Object bean = new BeanFactory(ips).getBean("xxx");
System.out.println(bean.getClass().getName());
}
}


 

这篇关于黑马程序员---代理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145539

相关文章

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

proxy代理解决vue中跨域问题

vue.config.js module.exports = {...// webpack-dev-server 相关配置devServer: {host: '0.0.0.0',port: port,open: true,proxy: {'/api': {target: `https://vfadmin.insistence.tech/prod-api`,changeOrigin: true,p

LabVIEW程序员是怎样成长为大佬

成为一名LabVIEW编程领域的“大佬”需要时间、实践、学习和解决复杂问题的经验。尽管LabVIEW作为一种图形化编程语言在初期可能相对容易上手,但要真正成为精通者,需要在多个层面上深入理解。以下是LabVIEW程序员如何逐步成长为“大佬”的路径: 1. 打好基础 LabVIEW的大佬们通常在初期会打下非常坚实的基础,理解LabVIEW编程的核心概念,包括: 数据流编程模型:Lab

程序员必备心理学——心流

心理学之心流 前言一、“心流”是什么?二、心流的好处二、如何进入心流心流状态的四个阶段第一个阶段:挣扎第二个阶段:放松第三个阶段:心流第四个阶段:巩固 进入心流的技巧 总结题外话 前言 你是否常常感觉自己明明学习了一整天,但是就是感觉没有太多的收获。这个时候除了你的学习方向等问题之外,也可能是你的学习方法太低效了。作者本人就经常有这种情况,好在偶然间在b站刷到一个大佬的这个心

Linux如何做ssh反向代理

SSH反向代理是一种通过SSH协议实现的安全远程访问方式,它允许客户端通过SSH连接到一台具有公网IP的代理服务器,然后这台代理服务器再将请求转发给内部网络中的目标主机。以下是实现SSH反向代理的步骤: 一、准备工作 确保服务器配置: 内网服务器(目标主机)和外网服务器(代理服务器)都安装了SSH服务,并且能够通过SSH进行互相访问。内网服务器上的服务(如Web服务、数据库服务等)需要在本地

将你的github仓库设置为web代理

将你的github仓库设置为web代理 废话不多说,直接上步骤 废话不多说,直接上步骤 创建一个仓库,上传静态web。 2. 设置仓库的 page 1)点击 “Settings” 如图设置

Nginx反向代理功能及动静分离实现

一:Nginx支持正向代理和反向代理 1.正向代理 正向代理,指的是通过代理服务器 代理浏览器/客户端去重定向请求访问到目标服务器 的一种代理服务。 正向代理服务的特点是代理服务器 代理的对象是浏览器/客户端,也就是对于目标服务器 来说浏览器/客户端是隐藏的。 正向代理是客户端指定让代理去访问哪个服务,代表客户端的利益。 2.反向代理 反向代理,指的是浏览器/客户端并不知道自己要

程序员都在使用的画图工具

大家好,我是袁庭新。 程序员都在使用的画图工具,你一定没用过这款画图工具吧!我教程中的架构图都是用它来画的。 比如我编写的RDB工作原理图就是用draw.io绘制的,如下图所示: 再例如Redis集群故障恢复原理图我也是通过draw.io工具绘制的,如下图所示: 是不是觉得draw.io绘制的图形特别简洁、美观。它的官网是: https://www.drawio.com dra

GitHub:代码是程序员沟通最直接的手段

如果不是 Andreessen horowitz 的投资,估计 GitHub 很难被福布斯、CNN、纽约时报等传统媒体注意到。普通大众之前不了解这个工具,是因为它距离记者的世界太远了——GitHub 是一个程序员所使用的托管项目的服务。 但在一些程序员眼里,它不仅是托管项目的地方,还是“开源”项目的大本营,而且是提高程序员“技术水平”和“技术品味”的地方,更是一个程序员社交的地方。