标题 & 代码高亮

This commit is contained in:
gdut-yy
2019-12-28 22:27:33 +08:00
parent 67f3f67fac
commit 893fb65d8c
17 changed files with 684 additions and 682 deletions

View File

@@ -1,4 +1,4 @@
Systems
# 第 11 章 Systems
by Dr. Kevin Dean Wampler
Image
@@ -22,13 +22,13 @@ Software systems should separate the startup process, when the application objec
The startup process is a concern that any application must address. It is the first concern that we will examine in this chapter. The separation of concerns is one of the oldest and most important design techniques in our craft.
Unfortunately, most applications dont separate this concern. The code for the startup process is ad hoc and it is mixed in with the runtime logic. Here is a typical example:
```java
public Service getService() {
if (service == null)
service = new MyServiceImpl(); // Good enough default for most cases?
return service;
}
```
This is the LAZY INITIALIZATION/EVALUATION idiom, and it has several merits. We dont incur the overhead of construction unless we actually use the object, and our startup times can be faster as a result. We also ensure that null is never returned.
However, we now have a hard-coded dependency on MyServiceImpl and everything its constructor requires (which I have elided). We cant compile without resolving these dependencies, even if we never actually use an object of this type at runtime!
@@ -73,9 +73,9 @@ A powerful mechanism for separating construction from use is Dependency Injectio
3. See, for example, [Fowler].
JNDI lookups are a “partial” implementation of DI, where an object asks a directory server to provide a “service” matching a particular name.
```java
MyService myService = (MyService)(jndiContext.lookup(NameOfMyService));
```
The invoking object doesnt control what kind of object is actually returned (as long it implements the appropriate interface, of course), but the invoking object still actively resolves the dependency.
True Dependency Injection goes one step further. The class takes no direct steps to resolve its dependencies; it is completely passive. Instead, it provides setter methods or constructor arguments (or both) that are used to inject the dependencies. During the construction process, the DI container instantiates the required objects (usually on demand) and uses the constructor arguments or setter methods provided to wire together the dependencies. Which dependent objects are actually used is specified through a configuration file or programmatically in a special-purpose construction module.
@@ -111,7 +111,7 @@ First, you had to define a local (in process) or remote (separate JVM) interface
Listing 11-1 An EJB2 local interface for a Bank EJB
```java
package com.example.banking;
import java.util.Collections;
import javax.ejb.*;
@@ -131,12 +131,12 @@ Listing 11-1 An EJB2 local interface for a Bank EJB
void setAccounts(Collection accounts) throws EJBException;
void addAccount(AccountDTO accountDTO) throws EJBException;
}
```
I have shown several attributes for the Banks address and a collection of accounts that the bank owns, each of which would have its data handled by a separate Account EJB. Listing 11-2 shows the corresponding implementation class for the Bank bean.
Listing 11-2 The corresponding EJB2 Entity Bean Implementation
```java
package com.example.banking;
import java.util.Collections;
import javax.ejb.*;
@@ -176,7 +176,7 @@ Listing 11-2 The corresponding EJB2 Entity Bean Implementation
public void ejbStore() {}
public void ejbRemove() {}
}
```
I havent shown the corresponding LocalHome interface, essentially a factory used to create objects, nor any of the possible Bank finder (query) methods you might add.
Finally, you had to write one or more XML deployment descriptors that specify the object-relational mapping details to a persistence store, the desired transactional behavior, security constraints, and so on.
@@ -215,7 +215,7 @@ Listing 11-3 shows the skeleton for a JDK proxy to provide persistence support f
Listing 11-3 JDK Proxy Example
```java
// Bank.java (suppressing package names…)
import java.utils.*;
@@ -277,7 +277,7 @@ Listing 11-3 JDK Proxy Example
Bank.class.getClassLoader(),
new Class[] { Bank.class },
new BankProxyHandler(new BankImpl()));
```
We defined an interface Bank, which will be wrapped by the proxy, and a Plain-Old Java Object (POJO), BankImpl, that implements the business logic. (We will revisit POJOs shortly.)
The Proxy API requires an InvocationHandler object that it calls to implement any Bank method calls made to the proxy. Our BankProxyHandler uses the Java reflection API to map the generic method invocations to the corresponding methods in BankImpl, and so on.
@@ -303,7 +303,7 @@ Listing 11-4 shows a typical fragment of a Spring V2.5 configuration file, app.x
Listing 11-4 Spring 2.X configuration file
```xml
<beans>
<bean id=”appDataSource”
@@ -322,7 +322,7 @@ Listing 11-4 Spring 2.X configuration file
p:dataAccessObject-ref=”bankDataAccessObject”/>
</beans>
```
Each “bean” is like one part of a nested “Russian doll,” with a domain object for a Bank proxied (wrapped) by a data accessor object (DAO), which is itself proxied by a JDBC driver data source. (See Figure 11-3.)
@@ -335,11 +335,11 @@ The client believes it is invoking getAccounts() on a Bank object, but it is act
14. [GOF].
In the application, a few lines are needed to ask the DI container for the top-level objects in the system, as specified in the XML file.
```java
XmlBeanFactory bf =
new XmlBeanFactory(new ClassPathResource(app.xml, getClass()));
Bank bank = (Bank) bf.getBean(bank);
```
Because so few lines of Spring-specific Java code are required, the application is almost completely decoupled from Spring, eliminating all the tight-coupling problems of systems like EJB2.
Although XML can be verbose and hard to read,15 the “policy” specified in these configuration files is simpler than the complicated proxy and aspect logic that is hidden from view and created automatically. This type of architecture is so compelling that frameworks like Spring led to a complete overhaul of the EJB standard for version 3. EJB3
@@ -355,7 +355,7 @@ Listing 11-5 shows our Bank object rewritten in EJB316.
Listing 11-5 An EBJ3 Bank EJB
```java
package com.example.banking.model;
import javax.persistence.*;
import java.util.ArrayList;
@@ -401,7 +401,7 @@ Listing 11-5 An EBJ3 Bank EJB
this.accounts = accounts;
}
}
```
This code is much cleaner than the original EJB2 code. Some of the entity details are still here, contained in the annotations. However, because none of that information is outside of the annotations, the code is clean, clear, and hence easy to test drive, maintain, and so on.
Some or all of the persistence information in the annotations can be moved to XML deployment descriptors, if desired, leaving a truly pure POJO. If the persistence mapping details wont change frequently, many teams may choose to keep the annotations, but with far fewer harmful drawbacks compared to the EJB2 invasiveness.