Nice book written by Rob Harrop and Jan Machacek.But i suggest to know about spring and then read this book as it tries to give more examples and less theory.
I wanted to share few important points i found from this book.
1) Spring is described as a lightweight framework for building Java applications, but that statement brings up two interesting points. First, you can use Spring to build any application in Java and, unlike many other frameworks such as Apache Struts, it is not just limited to web applications. Second, the lightweight part of the description doesn't really refer to the number of classes or the size of the distribution, but rather, it defines the principle of the Spring philosophy as a whole—that is, minimal impact. Spring is lightweight in the sense that you have to make few, if any, changes to your application code to gain the benefits of the Spring core, and should you choose to discontinue using Spring at any point, you will find doing so quite simple.
2) The core of the Spring framework is based on the principle of Inversion of Control (IoC). IoC is a technique that externalizes the creation and management of component dependencies. Consider an example where class Foo depends on an instance of class Bar to perform some kind of processing. Traditionally, Foo creates an instance of Bar using the new operator or obtains one from some kind of factory class. Using the IoC approach, an instance of Bar (or a subclass) is provided to Foo at runtime by some external process. This behavior, the injection of dependencies at runtime, leads to IoC being renamed the much more descriptive Dependency Injection (DI).
3) I was trying to understand imageDB sample application that gets bundled up with Spring.
I wanted to share how spring helps us to implement quartz so easily.
Below are the steps required.
a) Add a entry in web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml /WEB-INF/schedulingContext-quartz.xml
</param-value>
b) an example implementation for both SimpleTrigger and CronTrigger using spring is as below which should be declared in schedulingContext-quartz.xml
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref local="listImagesTrigger"/>
<ref local="checkImagesTrigger"/>
</list>
</property>
</bean>
<bean id="checkImagesJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="imageDatabase"/>
<property name="targetMethod" value="checkImages"/>
</bean>
<!-- Trigger for the job defined above -->
<!-- Registered by the 'scheduler' bean -->
<bean id="checkImagesTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="checkImagesJobDetail"/>
<property name="cronExpression" value="0/5 * * * * ?"/>
</bean>
Please remember that imageDatabase should be again another bean which should be declared and ideally which refers to a class and having a method called checkImages.
Below example is for SimpleTrigger
<bean id="listImagesTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="listImagesJobDetail"/>
<property name="startDelay" value="10000"/>
<property name="repeatInterval" value="10000"/>
</bean>
<bean id="listImagesJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="ListImagesQuartzJob"/>
<property name="jobDataAsMap">
<map>
<entry key="mailTo" value="dummy"/>
</map>
</property>
</bean>
Required Source Code For ListImagesQuartzJob.java
public class ListImagesQuartzJob extends QuartzJobBean {
private String mailTo;
protected void executeInternal(JobExecutionContext context) {
logger.info("Listing images in image database, scheduled by Quartz" + mailTo);
.................
}
}
4) Below example describes how to manage beans in .txt files instead of .xml files.
HelloWorldSpring.java
public class HelloWorldSpring {
public static void main(String[] args) throws Exception {
// get the bean factory
BeanFactory factory = getBeanFactory();
MessageRenderer mr = (MessageRenderer) factory.getBean("renderer");
MessageProvider mp = (MessageProvider) factory.getBean("provider");
mr.setMessageProvider(mp);
mr.render();
}
private static BeanFactory getBeanFactory() throws Exception {
// get the bean factory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// create a definition reader
PropertiesBeanDefinitionReader rdr = new PropertiesBeanDefinitionReader(
factory);
// load the configuration options
Properties props = new Properties();
props.load(new FileInputStream("c:/workspace/pro_spring/src/beans.properties"));
rdr.registerBeanDefinitions(props);
return factory;
}
}
# The MessageRenderer
renderer.class=StandardOutMessageRenderer
renderer.messageProvider(ref)=provider
# The MessageProvider
provider.class=HelloWorldMessageProvider
Finally provide some implementation for the two classes and run HelloWorldSpring.java
5) At its core, IoC, and therefore DI also, aims to offer a simpler mechanism for provisioning component dependencies (often referred to as an object's collaborators) and managing these dependencies throughout their lifecycles. A component that requires certain dependencies is often referred to as the dependent object or, in the case of IoC, the target. This is a rather grand way of saying that IoC provides services through which a component can access its dependencies and services for interacting with the dependencies throughout their life. In general, IoC can be decomposed into two subtypes: Dependency Injection and Dependency Lookup. These subtypes are further decomposed into concrete implementations of the IoC services. From this definition, you can clearly see that when we are talking about DI we are always talking about IoC, but when we are talking about IoC we are not always talking about DI.
6) Types of Inversion of Control
Dependency Lookup is a much more traditional approach and at first glance, it seems more familiar to Java programmers. Dependency Injection is a newer, less well-established approach that, although it appears counterintuitive at first, is actually much more flexible and usable than Dependency Lookup.
With Dependency Lookup–style IoC, a component must acquire a reference to a dependency, whereas with Dependency Injection, the dependencies are literally injected into the component by the IoC container. Dependency Lookup comes in two types: Dependency Pull and Contextualized Dependency Lookup (CDL). Dependency Injection also has two common flavors: Constructor Dependency Injection and Setter Dependency Injection.
Setter injection allows you to swap dependencies for a different implementation on the fly without creating a new instance of the parent component. Currently Spring doesn't support this feature, but as soon as Spring is JMX aware, this feature will present itself. Perhaps the biggest benefit of setter injection is that it is the least intrusive of the Injection mechanisms.
7) The core of Spring's Dependency Injection container is the BeanFactory. A BeanFactory is responsible for managing components and their dependencies. In Spring, the term bean is used to refer to any component managed by the container. Typically your beans adhere, at some level, to the JavaBeans specification, but this is not required, especially if you plan to use Constructor Injection to wire your beans together.
8) Choosing an Instantiation Mode
In most scenarios, it is quite easy to see which instantiation mode is suitable. Typically we find that singleton is the default mode for our beans. In general, singletons should be used in the following scenarios:
Shared objects with no state: When you have an object that maintains no state and has many dependent objects. Because you do not need synchronization if there is no state, you do not really need to create a new instance of the bean each time a dependent object needs to use it for some processing.
Shared object with read-only state: This is similar to the previous point, but you have some read-only state. In this case, you still do not need synchronization, so creating an instance to satisfy each request for the bean is just adding additional overhead.
Shared object with shared state: If you have a bean that has state that must be shared, then singleton is the ideal choice. In this case, ensure that your synchronization for state writes is as granular as possible.
High throughput objects with writable state: If you have a bean that is used a great deal in your application, then you may find that keeping a singleton and synchronizing all write access to the bean state allows for better performance than constantly creating hundreds of instances of the bean. When using this approach, try to keep the synchronization as granular as possible without sacrificing consistency. You will find that this approach is particularly useful when your application creates a large number of instances over a long period of time, when your shared object has only a small amount of writable state, or when the instantiation of a new instance is expensive.
9) Bean Lifecycle Management
An important part of any IoC container, Spring included, is that beans can be constructed in such a way that they receive notifications at certain points in their lifecycle. This enables your beans to perform relevant processing at certain points throughout their life. The number of possible notifications is huge, but in general, two lifecycle events are particularly relevant to a bean: post-initialization and pre-destruction.
In the context of Spring, the post-initialization event is raised as soon as Spring finishes setting all the property values on the bean and finishes any dependency checks that you configured it to perform. The pre-destruction event is fired just before Spring destroys the bean instance. Both of these lifecycle events are only fired for beans that are singletons. Spring doesn't manage the lifecycle of beans that are configured as non-singletons.
Using the BeanFactoryAware Interface
Using the BeanFactoryAware method, it is possible for your beans to get a reference to the BeanFactory that configured them. The main reason this interface was created was to allow a bean to access other beans programmatically, using getBean(). You should, however, avoid this practice and use Dependency Injection to provide your beans with their collaborators. If you use the lookup-based getBean() approach to obtain dependencies when you can use Dependency Injection, you are adding unnecessary complexity to your beans and coupling them to the Spring framework without good reason.
ApplicationContext is an extension of BeanFactory, providing all the same functionality, but it also reduces the amount of code you need to interact with it and adds new features into the pot for good measure. When using an ApplicationContext, you can control bean instantiation declaratively on a bean-by-bean basis, and any BeanFactoryPostProcessors registered in the ApplicationContext are executed for you automatically.
In addition to providing a model that is focused more on declarative configuration, the ApplicationContext supports the following features not present in a BeanFactory:
Internationalization
Event publication
Resource management and access
Additional lifecycle interfaces
Improved automatic configuration of infrastructure components
10) AOP Concepts
As with most technologies, AOP comes with its own specific set of concepts and terms. It is important that you understand what these terms mean before we explain how to use AOP in an application. The following list explains the core concepts of AOP:
Joinpoints: A joinpoint is a well-defined point during the execution of your application. Typical examples of joinpoints include a call to a method, the method invocation itself, class initialization, and object instantiation. Joinpoints are a core concept of AOP and define the points in your application at which you can insert additional logic using AOP.
Advice: The code that is executed at a particular joinpoint is called the advice. There are many different types of advice, including before, which executes before the joinpoint, and after, which executes after it.
Pointcuts: A pointcut is a collection of joinpoints that you use to define when advice should be executed. By creating pointcuts, you gain fine-grained control over how you apply advice to the components in your application. As mentioned previously, a typical joinpoint is a method invocation. A typical pointcut is the collection of all method invocations in a particular class. Often you can compose pointcuts in complex relationships to further constrain when advice is executed. We discuss pointcut composition in more detail in the next chapter.
Aspects: An aspect is the combination of advice and pointcuts. This combination results in a definition of the logic that should be included in the application and where it should execute.
Weaving: This is the process of actually inserting aspects into the application code at the appropriate point. For compile-time AOP solutions, this is, unsurprisingly, done at compile time, usually as an extra step in the build process. Likewise, for runtime AOP solutions, the weaving process is executed dynamically at runtime.
Target: An object whose execution flow is modified by some AOP process is referred to as the target object. Often you see the target object referred to as the advised object.
Introduction: This is the process by which you can modify the structure of an object by introducing additional methods or fields to it. You can use introduction to make any object implement a specific interface without needing the object's class to implement that interface explicitly.
11) The MethodInterceptor interface is the AOP Alliance standard interface for implementing around advice for method invocation joinpoints.
12) Joinpoints in Spring
One of the more noticeable simplifications in Spring AOP is that it only supports one joinpoint type: method invocation. At first glance, this might seem like a severe limitation if you are familiar with other AOP implementations like AspectJ, which supports many more joinpoints, but in fact this actually makes Spring more accessible.
13) Creating Before Advice
Before advice is one of the most useful advice types available in Spring. A before advice can modify the arguments passed to a method and can prevent the method from executing by raising an exception.
Example
public class SimpleBeforeAdvice implements MethodBeforeAdvice {
public static void main(String[] args) {
MessageWriter target = new MessageWriter();
// create the proxy
ProxyFactory pf = new ProxyFactory();
pf.addAdvice(new SimpleBeforeAdvice());
pf.setTarget(target);
MessageWriter proxy = (MessageWriter) pf.getProxy();
// write the messages
proxy.writeMessage();
}
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("Before method: " + method.getName());
}
}
public class MessageWriter implements IMessageWriter{
public void writeMessage() {
System.out.print("World");
}
}
public interface IMessageWriter {
public void writeMessage();
}
14) A good use of after returning advice is to perform some additional error checking when it is possible for a method to return an invalid value. In the scenario we described earlier, it is possible for a cryptographic key generator to generate a key that is considered weak for a particular algorithm. Ideally, the key generator would check for these weak keys, but since the chance of these keys arising is often very small, many generators do not check. By using an after returning advice, we can advise the method that generates the key and performs this additional check.
KeyGenerator.java
public class KeyGenerator {
public static final long WEAK_KEY = 0xFFFFFFF0000000L;
public static final long STRONG_KEY = 0xACDF03F590AE56L;
private Random rand = new Random();
public long getKey() {
int x = rand.nextInt(3);
if(x == 1) {
return WEAK_KEY;
} else {
return STRONG_KEY;
}
}
}
WeakKeyCheckAdvice.java
public class WeakKeyCheckAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
if ((target instanceof KeyGenerator)
&& ("getKey".equals(method.getName()))) {
long key = ((Long) returnValue).longValue();
if (key == KeyGenerator.WEAK_KEY) {
throw new SecurityException(
"Key Generator generated a weak key. Try again");
}
}
}
}
AfterAdviceExample.java
public class AfterAdviceExample {
public static void main(String[] args) {
KeyGenerator keyGen = getKeyGenerator();
for(int x = 0; x < 10; x++) {
try {
long key = keyGen.getKey();
System.out.println("Key: " + key);
} catch(SecurityException ex) {
System.out.println("Weak Key Generated!");
}
}
}
private static KeyGenerator getKeyGenerator() {
KeyGenerator target = new KeyGenerator();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvice(new WeakKeyCheckAdvice());
return (KeyGenerator)factory.getProxy();
}
}
After creating an advised proxy of a KeyGenerator target, the AfterAdviceExample class attempts to generate ten keys. If a SecurityException is thrown during a single generation, then a message is written to stdout informing the user that a weak key was generated, otherwise the generated key is displayed.
As you can see, the KeyGenerator class sometimes generates weak keys, as expected, and the WeakKeyCheckAdvice ensures that a SecurityException is raised whenever a weak key is encountered.
15) Creating Around Advice
Around advice functions like a combination of before and after advice, with one big difference— you can modify the return value. Not only that, you can also prevent the method from actually executing. This means that using around advice, you can essentially replace the entire implementation of a method with new code. Around advice in Spring is modeled as an interceptor using the MethodInterceptor interface. There are many uses for around advice, and you will find that many features of Spring are created using method interceptors, such as the remote proxy support and the transaction management features. Method interception is also a good mechanism for profiling the execution of your application.
public class BeanOne {
public void foo() {
System.out.println("foo");
}
public void bar() {
System.out.println("bar");
}
}
public class BeanTwo {
public void foo() {
System.out.println("foo");
}
public void bar() {
System.out.println("bar");
}
}
public class SimpleAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println(">> Invoking " + invocation.getMethod().getName());
Object retVal = invocation.proceed();
System.out.println(">> Done");
return retVal;
}
}
public class SimpleStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches(Method method, Class cls) {
return ("foo".equals(method.getName()));
}
public ClassFilter getClassFilter() {
return new ClassFilter() {
public boolean matches(Class cls) {
return (cls == BeanOne.class);
}
};
}
}
public class StaticPointcutExample {
public static void main(String[] args) {
BeanOne one = new BeanOne();
BeanTwo two = new BeanTwo();
BeanOne proxyOne;
BeanTwo proxyTwo;
// create pointcut, advice and advisor
Pointcut pc = new SimpleStaticPointcut();
Advice advice = new SimpleAdvice();
Advisor advisor = new DefaultPointcutAdvisor(pc, advice);
// create BeanOne proxy
ProxyFactory pf = new ProxyFactory();
pf.addAdvisor(advisor);
pf.setTarget(one);
proxyOne = (BeanOne)pf.getProxy();
// create BeanTwo proxy
pf = new ProxyFactory();
pf.addAdvisor(advisor);
pf.setTarget(two);
proxyTwo = (BeanTwo)pf.getProxy();
proxyOne.foo();
proxyTwo.foo();
proxyOne.bar();
proxyTwo.bar();
}
}
Notice that the DefaultPointcutAdvisor instance is then used to create two proxies: one for an instance of BeanOne and one for an instance of BeanTwo. Finally, both the foo() and bar() methods are invoked on the two proxies.
Running this example results in the following output:
>> Invoking foo
foo
>> Done
foo
bar
As you can see, the only method for which the SimpleAdvice was actually invoked was the foo() method for the BeanOne class, exactly as expected
16) What if you don't know all of the methods' names in advance, and instead you know the pattern that the names follow? For instance, what if you want to match all methods whose names starts with get? In this case, you can use one of the regular expression pointcuts, either JdkRegexpMethodPointcut or Perl5RegexpMethodPointcut, to match a method name based on a regular expression.
public class RegexpBean {
public void foo1() {
System.out.println("foo1");
}
public void foo2() {
System.out.println("foo2");
}
public void bar() {
System.out.println("bar");
}
}
public class RegexpPointcutExample {
public static void main(String[] args) {
RegexpBean target = new RegexpBean();
// create the advisor
JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut();
pc.setPattern(".*foo.*");
Advisor advisor = new DefaultPointcutAdvisor(pc, new SimpleAdvice());
// create the proxy
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
RegexpBean proxy = (RegexpBean)pf.getProxy();
proxy.foo1();
proxy.foo2();
proxy.bar();
}
}
Notice we do not need to create a class for the pointcut; instead, we just create an instance of JdkRegexpMethodPointcut (which could just as easily be Perl5RegexpMethodPointcut), specify the pattern to match, and we are done. The interesting thing to note is the pattern. When matching method names, Spring matches the fully qualified name of the method, so for foo1(), Spring is matching against com.apress.prospring.ch6.regexppc.RegexpBean.foo1, hence the leading .* in the pattern. This is a powerful concept because it allows you to match all methods within a given package, without needing to know exactly which classes are in that package and what the names of the methods are. Running this example yields the following output:
>> Invoking foo1
foo1
>> Done
>> Invoking foo2
foo2
>> Done
bar
As you would expect, only the foo1() and foo2() methods have been advised, because the bar() method does not match the regular expression pattern.
17) Using JDK Dynamic Proxies
JDK proxies are the most basic type of proxy available in Spring. Unlike the CGLIB proxy, the JDK proxy can only generate proxies of interfaces, not classes. In this way, any object you wish to proxy must implement at least one interface. In general, it is good design to use interfaces for your classes, but it is not always possible, especially when you are working with third-party or legacy code. In this case, you must use the CGLIB proxy.
18) Using ComposablePointcut
In some cases, you may need to compose two or more point- cuts together to achieve the desired goal. Consider the situation where you want to pointcut all getter and setter methods on a bean. You have a pointcut for getters and a pointcut for setters, but you don't have one for both. Of course, you could just create another pointcut with the new logic, but a better approach is to combine the two pointcuts into a single pointcut using ComposablePointcut.
The ComposablePointcut supports two methods: union() and intersection(). By default, ComposablePointcut is created with a ClassFilter that matches all classes and a MethodMatcher that matches all methods, although you can supply your own initial ClassFilter and MethodMatcher during construction. The union() and intersection() methods are both overloaded to accept ClassFilter and MethodMatcher arguments.
Invoking the union() method for a MethodMatcher replaces the MethodMatcher of the ComposablePointcut with an instance of UnionMethodMatcher using the current MethodMatcher of the ComposablePointcut and the MethodMatcher passed to the union() method as arguments. The UnionMethodMatcher then returns true for a match if either of its wrapped MethodMatchers return true. You can invoke the union() method as many times as you want, with each call creating a new UnionMethodMatcher that wraps the current MethodMatcher of the ComposablePointcut with the MethodMatcher passed to union(). A similar structure is followed when you are using ClassFilter with the union() method.
Internally, the intersection() method works in a similar way to the union(). However, the IntersectionMethodMatcher class only returns true for a match if both of the embedded MethodMatchers return true for a match. Essentially, you can think of the union() method as an any match, in that it returns true if any of the matchers it is wrapping return true; and you can think of the intersection() method as an all match, in that it only returns true if all its wrapped matchers return true.
public class SampleBean {
public String getName() {
return "Rob Harrop";
}
public void setName(String name) {
}
public int getAge() {
return 100;
}
}
public class ComposablePointcutExample {
public static void main(String[] args) {
// create target
SampleBean target = new SampleBean();
ComposablePointcut pc = new ComposablePointcut(ClassFilter.TRUE,
new GetterMethodMatcher());
System.out.println("Test 1");
SampleBean proxy = getProxy(pc, target);
testInvoke(proxy);
System.out.println("Test 2");
pc.union(new SetterMethodMatcher());
proxy = getProxy(pc, target);
testInvoke(proxy);
System.out.println("Test 3");
pc.intersection(new GetAgeMethodMatcher());
proxy = getProxy(pc, target);
testInvoke(proxy);
}
private static SampleBean getProxy(ComposablePointcut pc, SampleBean target) {
// create the advisor
Advisor advisor = new DefaultPointcutAdvisor(pc,
new SimpleBeforeAdvice());
// create the proxy
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
return (SampleBean) pf.getProxy();
}
private static void testInvoke(SampleBean proxy) {
proxy.getAge();
proxy.getName();
proxy.setName("Rob Harrop");
}
private static class GetterMethodMatcher extends StaticMethodMatcher {
public boolean matches(Method method, Class cls) {
return (method.getName().startsWith("get"));
}
}
private static class GetAgeMethodMatcher extends StaticMethodMatcher {
public boolean matches(Method method, Class cls) {
return "getAge".equals(method.getName());
}
}
private static class SetterMethodMatcher extends StaticMethodMatcher {
public boolean matches(Method method, Class cls) {
return (method.getName().startsWith("set"));
}
}
}
19) Spring treats introductions as a special type of advice, more specifically, as a special type of around advice. Because introductions apply solely at the class level, you cannot use pointcuts with introductions; semantically, the two don't match. An introduction adds new interface implementations to a class and a pointcut defines which methods an advice applies. You create an introduction by implementing the IntroductionInterceptor class, which extends the MethodInterceptor interface.
Just as you need to use a PointcutAdvisor when you are working with pointcut advice, you need to use an IntroductionAdvisor to add introductions to a proxy. The default implementation of IntroductionAdvisor is DefaultIntroductionAdvisor, which should suffice for most, if not all, of your introduction needs. You should be aware that adding an introduction using ProxyFactory.addAdvice() is not permitted and results in an AopConfigException being thrown.
20) The JdbcTemplate Class
This class represents the core of Spring's JDBC support. It can execute all types of SQL statements. In the most simplistic view, you can classify the data definition and data manipulation statements. Data definition statements cover creating various database objects (tables, views, stored procedures, etc). Data manipulation statements manipulate the data and can be classified as select and update statements. A select statement generally returns a set of rows; each row has the same set of columns. An update statement modifies the data in the database but does not return any results.
21) HQL(Hibernate Query Language) is case-sensitive. Unlike in SQL, the column names (bean properties) as well as table names (object names) are case sensitive. This is because HQL uses object names rather than table names to select the data. When you are writing an HQL statement, you do not actually have to worry about which tables are going to be used to build the result—it is up to Hibernate to find which tables map to which classes and issue the select statement to the database.
22) There are many reasons you should design and code your applications to interfaces rather than to a concrete class hierarchy, but perhaps the biggest reason is to reduce coupling.
23) Local transactions are specific to a single transactional resource (a JDBC connection, for example), whereas global transactions are managed by the container and can span multiple transactional resources.
24) Spring's implementation of the MVC architecture for web applications is based around DispatcherServlet. This servlet processes the requests and invokes appropriate controllers to handle the request.
The DispatcherServlet intercepts the incoming requests and determines which Controller will handle the request. The Spring controllers return a ModelAndView class from their handling methods. The ModelAndView instance holds a reference to a view and a model. The model is a simple Map instance that holds Java beans that the view is going to render. The View is an interface that, when implemented, defines the render method. It makes sense that the View implementation can be virtually anything the client can interpret.
25) Interceptors are closely related to mappings because you can specify a list of interceptors that are called for each mapping. HandlerInterceptor implementations can process each request before or after the appropriate controller has processed it. You can choose to implement the HandlerInterceptor interface or extend HandlerInterceptorAdapter, which provides a default implementation for all HandlerInterceptor methods. As an example, we are going to implement a BigBrotherHandlerInterceptor that is going process each request.
public class BigBrotherHandlerInterceptor extends HandlerInterceptorAdapter {
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// process the request
}
}
<beans>
<bean id="bigBrotherHandlerInterceptor"
class="BigBrotherHandlerInterceptor"/>
<bean id="publicUrlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref local="bigBrotherHandlerInterceptor"/>
</list>
</property>
<property name="mappings">
<props>
<prop key="/index.html">indexController</prop>
<prop key="/product/index.html">productController</prop>
<prop key="/product/view.html">productController</prop>
<prop key="/product/edit.html">productFormController</prop>
</props>
</property>
</bean>
</beans>
26) Views, Locales, and Themes
We already touched on the View interface, but we simply stated its uses. It is now time to examine it in more detail. Let's start with a custom implementation of the View interface. This demonstrates how simple it is to create a custom view and what Spring does to look up (and instantiate) an appropriate instance of a view when we refer to the view by its name.
Using Views Programmatically
we manually implement a View and return this implementation in the ModelAndView class, which is a result of the AbstractController.handleRequestInternal() method.
Our view must implement only a single method from the View interface: render(Map, HttpServletRequest, HttpServletResponse).
Example:
public class PlainTextView implements View {
public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType("text/plain");
response.addHeader("Content-disposition", "attachment; filename=output.txt");
PrintWriter writer = response.getWriter();
for (Iterator k = model.keySet().iterator(); k.hasNext();) {
Object key = k.next();
writer.print(key);
writer.println(" contains:");
writer.println(model.get(key));
}
}
}
public class IndexController extends AbstractController {
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
setCacheSeconds(10);
Map<String, Object> model = new HashMap<String, Object>();
model.put("Greeting", "Hello World");
model.put("Server time", new Date());
View view = (View)getApplicationContext().getBean("plainTextView");
return new ModelAndView(view, model);
}
}
<bean id="plainTextView" class="com.apress.prospring.ch17.web.views.PlainTextView"/>
About the Authors
Rob Harrop is a software consultant specializing in delivering high-performance, highly-scalable enterprise applications. He is an experienced architect with a particular flair for understanding and solving complex design issues. With a thorough knowledge of both Java and .NET, Harrop has successfully deployed projects across both platforms. He also has extensive experience across a variety of sectors, retail and government in particular.Harrop is the author of five books, including Pro Spring, a widely-acclaimed, comprehensive resource on the Spring Framework.Harrop has been a core developer of the Spring Framework since June 2004 and currently leads the JMX and AOP efforts. He co-founded UK-based software company, Cake Solutions, in May 2001, having spent the previous two years working as Lead Developer for a successful dotcom start-up. Rob is a member of the JCP and is involved in the JSR-255 Expert Group for JMX 2.0.
Jan Machacek is a chief software architect at Cake Solutions Limited (www.cakesolutions.net), a UK-based software company. He has been an early adopter of Spring at Cake Solutions and has seen the dramatic change the Spring framework has brought to the Java world. As part of his job, Jan designs and oversees the development of majority of Cake's projects. Where appropriate, Jan also applies his interest in declarative programming and artificial intelligence. Throughout his programming career, Jan has designed and implemented large J2EE and .NET systems for the UK government and large private sector bodies. When not programming, Jan enjoys foreign languages; he also enters races and time trials as a member of the Manchester Wheelers' cycling club.
Hope you enjoy reading this book.
No comments:
Post a Comment