黑马程序员---代理

分析代理类的作用与原理及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());
	}
}


 

黑马程序员苍穹外卖项目中的Nginx配置文件可以根据具体需求进行配置。根据引用\[1\]中的描述,可以通过双击nginx.exe启动Nginx,并在http://localhost/访问前端页面。这意味着Nginx的配置文件应该包含有关前端页面的相关配置。另外,根据引用\[2\]中的描述,Nginx还可以用作反向代理和负载均衡,因此配置文件还应包含有关反向代理和负载均衡的相关配置。最后,根据引用\[3\]中的描述,苍穹外卖项目还需要与第三方配送公司进行对接和管理,因此配置文件还应包含有关与第三方配送公司对接的相关配置。综上所述,黑马程序员苍穹外卖项目的Nginx配置文件应包含前端页面的相关配置、反向代理和负载均衡的相关配置以及与第三方配送公司对接的相关配置。 #### 引用[.reference_title] - *1* [黑马程序员_Java项目实战《苍穹外卖》_Day01_开发环境搭建](https://blog.csdn.net/BallerWang9/article/details/131824385)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [最适合新手的SpringBoot+SSM项目《苍穹外卖》实战—(一)项目概述](https://blog.csdn.net/qq_20185737/article/details/131575898)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值