企业级引用系统开发技术(1)

stateful和stateless的讨论

想要区分什么是有状态和无状态,首先要明白什么是状态。

  1. 状态是什么?比如说一个人35岁,那么这个35岁就可以视为这个”人”的状态。在对象中像这类的“状态”就是用属性保存。比如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Person{
    private int age;

    public Person(){}

    public int getAge(){
    return this.age;
    }

    public void setAge(int newAge){
    this.age = newAge;
    }
    }

这就是一个有状态的类。

  1. 在java web中,singlton和prototype的使用

    • singlton(单例)默认是在全局中只有一个实例,所有调用都使用的是这一个实例。
    • prototype(原型)每次创建都会重新创建一个实例,将construct的过程走一遍。

      优缺点:
      由于singlton只有一个实例,而现实情况中经常会有多线程访问的情况,如果singlton是一个有状态的实例,那么就有可能出现竞争的情况,对资源进行竞争。
      prototype每次都会创建,我们知道每一次创建都需要消耗时间和资源,当有很多人访问的时候,代价又难以接受。
      所以对于web中应用,如果是没有状态的类,可以实现为单例,对于有状态的类,实现为原型。

  2. 更多的讨论

    在MVC架构中,struts由于与前段端交流,常会涉及到有状态的类,因此其为原型模式;而在spring中,默认为singlton(单例)模式。
    在MVC的Controller,Service,Dao层中,我们应该尽可能实现为无状态的类,只包含方法。

    servlet为无状态的类,session为有状态的类。

    更多的讨论

RMI(远程方法调用)

从三个方面理解RMI。

  1. 目的
    远程方法调用,就是为了让在某个java虚拟机上的对象像调用本地对象一样调用另一个java 虚拟机中的对象上的方法。是牵涉到多个JVM的过程。
  2. RMI的过程
    rmi
  • 为了在两个JVM中都通,有了RMI,使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信;
  • 为了屏蔽掉通信的具体实现,在客户端有stub,服务端有skeleton,两端通过这两个代理进行远端方法调用。(相当于加了一层)
  • 到目前为止,client端并不知道server端有那些可以调用的方法。通过设置一个中间rmiregister,server将提供的方法注册到该rmiregister中,client端可以到rmiregister中查找需要的远端调用。

    远端调用实现的过程:

    1. rmiregister运行
    2. server在rmiregister注册所提供的调用(将stub上传到rmiregister)
    3. client在rmiregister查找所需要的远端调用,并将其stub下载至本地至此,client端有stub,server端有skeleton,两端可以直接通信
    4. client远端调用
    5. client的stub发出远端调用请求
    6. server端的skeleton接收到stub发过来的远端调用,解析并调用远端调用的实现
    7. 第6步的返回值被skeleton发送值stub
    8. stub解析第6步的结果并返回给client。
  1. 代码实现
    在java1.8中,已经不需要使用rmic(rmi compile)手动编译stub和skeleton了所以过程会简单些。代码步骤(假设远端调用返回的对象是Warehouse):

    1. 定义Warehouse接口,并且继承自Remote
    2. 在服务器端定义WarehouseImpl,继承自UnicastRemoteObject
    3. 编写client和server端的代码
    4. 运行rmiregister
    5. 运行server端代码
    6. 运行client端代码

      最后client端代码:Warehouse.class,client.class
      服务器端代码:Warehouse.class WarehouseImpl.class,server.class

      注意:设置CLASSPATH,添加client和server的路径到CLASSPATH,否则java找不到其对应的class文件
      使用jndi,配置jndi.properties

      代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//Warehouse 两端都有
public interface Warehouse extends Remote
{
double getPrice(String description) throws RemoteException;
}
//client
public class WarehouseClient
{
public static void main(String[] args) throws NamingException, RemoteException
{
// System.setProperty("java.security.policy", "client.policy");
// System.setSecurityManager(new SecurityManager());
Context namingContext = new InitialContext();

System.out.print("RMI registry bindings: ");
NamingEnumeration<NameClassPair> e = namingContext.list("rmi://localhost/");
while (e.hasMore())
System.out.println(e.next().getName());

String url = "rmi://localhost:1099/central_warehouse";
Warehouse centralWarehouse = (Warehouse) namingContext.lookup(url);

Scanner in = new Scanner(System.in);
System.out.print("Enter keywords: ");
String name = in.nextLine().split("\\s+")[0];
Double price = centralWarehouse.getPrice(name);

System.out.println("Price" + ": " + price);
}
}
//WarehouseImpl
public class WarehouseImpl extends UnicastRemoteObject implements Warehouse
{
private Map<String, Double> products;

/**
* Constructs a warehouse implementation.
*/
public WarehouseImpl() throws RemoteException
{
products = new HashMap<>();
}

public void add(String keyword, Double product)
{
products.put(keyword, product);
}

public double getPrice(String description) throws RemoteException
{
return products.get(description);
}

}
//server
public class WarehouseServer
{
public static void main(String[] args) throws RemoteException, NamingException
{
// System.setProperty("java.security.policy", "server.policy");
// System.setSecurityManager(new SecurityManager());

System.out.println("Constructing server implementation...");
WarehouseImpl centralWarehouse = new WarehouseImpl();
centralWarehouse.add("java",new Double(23.5));


System.out.println("Binding server implementation to registry...");
Context namingContext = new InitialContext();
namingContext.bind("rmi:central_warehouse", centralWarehouse);
System.out.println("Waiting for invocations from clients...");
}
}

//jndi.properties

java.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory
java.naming.provider.url=rmi://localhost:1099

[TODO activation pdf2]

  1. rmi和spring结合
  1. Java程序执行过程
    宏观上来看Java程序运行的过程。
  • 编写好的.java程序被编译成为.class文件(可以加密在编译好字后尽心加密 ,然后用自定义的类加载器解密)
  • 启动Java虚拟机,类加载器加载需要的类到内存中(可以包括解密过程)
  • 执行引擎执行该类

    如下图:
    Class Loader

  1. 加载过程
    Java程序启动过程中,会按照顺序有以下几个类加载器:
    class Loader1
  • 启动类加载器(BootstrapClassLoader):使用C语言编写(如果用java编写,而这个加载器也需要使用类加载器加载,形成一个环,无法加载),主要加载jre/lib下的文件,比如rt.jar等,该类无法被开发者使用。
  • 扩展类加载器(Extension ClassLoader):该类加载器负责加载JDK安装目录下的\jre\lib\ext的类,开发者也可以直接使用扩展类加载器。
  • 应用程序类加载器(AppClassLoader):负责加载用户类路径(Classpath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有定义过自己的类加载器,该类加载器为默认的类加载器。
  • 用户自定义类加载器(User ClassLoader):JVM自带的类加载器是从本地文件系统加载标准的java class文件,而自定义的类加载器可以做到在执行非置信代码之前,自动验证数字签名,动态地创建符合用户特定需要的定制化构建类,从特定的场所(数据库、网络中)取得java class,用户使用的plugin classloader严格来说也是属于user
    classloader。

双亲委派模型

每个类加载器加载类的时候,首先调用它的父加载器,如果他的父加载器不能加载,再轮到它加载。
好处:首先从java的内库中加载相应的类,在加载器之间形成一种加载的优先级顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
//check the class has been loaded or not
Class c = findLoadedClass(name);
if(c == null) {
try{
if(parent != null) {//调用父加载器,递归上去,第一个使用的加载器就是启动类加载器
c = parent.loadClass(name, false);
} else{
c = findBootstrapClassOrNull(name);
}
} catch(ClassNotFoundException e) {
//if throws the exception , the father can not complete the load
}
if(c == null) {
c = findClass(name);
}
}
if(resolve) {
resolveClass(c);
}
return c;
}

双亲委派模型

如何使用自定义的类加载器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//Book类定义
public class Book
{
public String isbn;

public Book(String isbn)
{
this.isbn = isbn;
}

public Book() {
this.isbn = "123456789";
}

public String getIsbn()
{
return isbn;
}

public void setIsbn(String isbn){
this.isbn = isbn;
}

}

//User ClassLoader
import java.io.*;
public class myClassLoader extends ClassLoader {

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = this.loadByteClass(name);
return this.defineClass(name, bytes, 0, bytes.length);
}

private byte[] loadByteClass(String name){
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
is = new FileInputStream(new File("/home/tbxsx/文档/大三下/企业级应用系统开发技术/serverComponent1/WarehouseClient/src/" +name+".class" ));
int c = 0;
while(-1 != (c = is.read())){
baos.write(c);
}
data = baos.toByteArray();

} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if(is != null)
is.close();
if(baos != null)
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return data;
}
}

//main test

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
myClassLoader mc = new myClassLoader();
Class<?> c = mc.loadClass("Book");
Method[] ms = c.getMethods();
Book b = (Book) c.newInstance();
for(Method m:ms){
System.out.println(m.getName());
}
Field[] fs = c.getFields();
for(Field f:fs){
System.out.println(f.getName());
}
Method m = c.getMethod("getIsbn");
Method m1 = c.getMethod("setIsbn",String.class);

System.out.println(m.getName());
System.out.println(m1.getName());
System.out.println("m1");

System.out.println(m.invoke(b));
m1.invoke(b,"hhh");
System.out.println(m.invoke(b));

}
}

//输出

getIsbn
setIsbn
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
isbn
getIsbn
setIsbn
m1
123456789
hhh

注意:定义ClassLoader从根本上来说就是重载findClass函数,找到需要load的类的二进制文件,在loader中处理(解密验证等等处理)之后,通过defineClass函数之后返回

invoke函数(反射)
获得反射Class类的三种反射机制:
Person person = new Person();

  • 1、通过Object类的getClass()方法:(需要先实例化一个对象)
    Class clazz1 = person.getClass();
  • 2、通过对象实例方法获取对象:(需要先实例化一个对象)
    Class clazz2 = person.class;
  • 3、类的全路径:(不许呀实例对象)通过类的加载器实现
    Class clazz3 = Class.forName("com.cn.Person");
    invoke 参考
    运行时数据区:
  • 堆(类实例,java垃圾收集器)
  • 方法区(常量入”this is the string”,常量池)
  • 虚拟机栈(方法调用栈)
  • 本地方法栈
  • 程序计数器

前两个线程公用

JVM 参考