测试:
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = null;
// 表示找到配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从配置文件之中取得Bean的实例
Person per = (Person) ctx.getBean("per9");
System.out.println(per);
Iterator iter = per.getBooks().entrySet().iterator();
while (iter.hasNext()) {
Map.Entry me = (Map.Entry) iter.next();
Book b = (Book) me.getValue();
System.out.println("\t|- " + me.getKey() + " --> " + b.getTitle());
}
}
}
在以后的开发中Properties注入是最常见的一种,属性。
import java.util.Properties;
public class Person {
private Properties mypro ;
public Properties getMypro() {
return mypro;
}
public void setMypro(Properties mypro) {
this.mypro = mypro;
}
}
之后在配置文件中建立关系
<bean id="per" class="org.lxh.ioc.demo10.Person">
<property name="mypro">
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
<prop key="key3">value3</prop>
</props>
</property>
</bean>
准备好了三个所需要的属性值
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = null;
// 表示找到配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从配置文件之中取得Bean的实例
Person per = (Person) ctx.getBean("per");
System.out.println(per.getMypro());
}
}
3.3、AOP(了解)
AOP:面向方面的编程。
代理设计模式:
• 一个接口定义两个子类,其中一个子类包含另外一个子类。
• 代理完成比真实实现类更多的操作。
Subject.java:
package org.lxh.aop.demo01;
public interface Subject {
public void request() ;
}
真实主题实现类:RealSubject.java:
package org.lxh.aop.demo01;
public class RealSubject implements Subject {
public void request() {
System.out.println("真实的业务操作。。。");
}
}
代理主题操作类:ProxySubject.java:
package org.lxh.aop.demo01;
public class ProxySubject implements Subject {
private Subject sub = null ;
public ProxySubject(Subject sub){
this.sub = sub ;
}
public void before(){
System.out.println("操作开始的相关业务。");
}
public void request() {
this.before() ;
this.sub.request() ;
this.after() ;
}
public void after(){
System.out.println("将操作进行日志记录。");
}
}
测试类:
package org.lxh.aop.demo01;
public class Test {
public static void main(String[] args) {
Subject sub = new ProxySubject(new RealSubject()) ;
sub.request() ;
}
}
以上的程序是一个代理的基本操作。属于静态代理类的功能,但是如果要对多个接口进行代理,则无法做到,此时就要对程序进行扩充,使用动态代理操作。
动态代理操作中,一个类只需要实现InvocationHandler接口即可。
动态代理类Proxy.java:
package org.lxh.aop.demo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Proxy implements InvocationHandler {
private Object sub;
public Object bind(Object obj) { // 绑定一个真实的对象操作
this.sub = obj;
return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
.getClassLoader(), obj.getClass().getInterfaces(), this);
}
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
// 调用接口中的方法
this.before();
Object obj = arg1.invoke(this.sub, arg2);
this.after();
return obj;
}
public void before() {
System.out.println("操作开始的相关业务。");
}
public void after() {
System.out.println("将操作进行日志记录。");
}
}
此时,通过程序进行测试。
package org.lxh.aop.demo02;
public class Test {
public static void main(String[] args) {
Subject sub = (Subject)new Proxy().bind(new RealSubject()) ;
sub.request() ;
}
}
以后只要想修改所有代理的相关业务操作,只需要修改动态代理类即可。
此时,可以直接使用Spring中的AOP功能,在配置文件中对程序进行功能的增加,以后只要修改配置文件,就可以完成程序横切面上的操作。
如果要想实现这样的AOP功能,在Spring中提供了若干个Advices接口,直接使用此接口实现类,就可以完成程序的之前操作、之后操作的代码。
例如:将之前的代码
使用MethodBeforeAdvice接口定义一个子类,就表示之前的操作代码类。
Before.java:
package org.lxh.aop.demo03;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class Before implements MethodBeforeAdvice {
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
System.out.println("*** 代理之前的操作!") ;
}
}
也就是把之前在代理类之中编写的before()方法,单独提取到一个类之中了,以后直接配置此类的实例就可以完成代理操作之前的工作。
在AOP中就必须在applicationContext.xml文件之中进行若干个配置才可以完成这种AOP开发。
applicationContext.xml:
<bean id="before" 定义真实主题操作之前的功能
class="org.lxh.aop.demo03.Before"></bean>
<bean id="subject" 定义接口
class="org.lxh.aop.demo03.Subject"
abstract="true">
</bean>
<bean id="realsubject" 定义接口实现类
class="org.lxh.aop.demo03.RealSubject"
parent="subject">
</bean>
<bean id="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean"> spring中已经提供了一个代理的操作类,只需要设置好若干属性即可
<property name="proxyInterfaces"> 被代理的接口
<value>org.lxh.aop.demo03.Subject</value>
</property>
<property name="target"> 代理中所包含的真实实现类
<ref bean="realsubject" />
</property>
<property name="interceptorNames">
<list>
<value>before</value> 定义before,因为实现MethodBeforeAdvices接口,所以会自动设置成在主题调用之前操作此代码。
</list>
</property>
</bean>
测试:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = null ;
ctx = new ClassPathXmlApplicationContext("applicationContext.xml") ;
Subject sub = (Subject)ctx.getBean("proxy") ;
sub.request() ;
}
}
程序运行之后,代理之前的操作已经调用了。
同样,也可以在方法执行完毕之后进行相关的收尾工作:AfterReturningAdvice接口。
After.java:
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class After implements AfterReturningAdvice {
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
System.out.println("*** 收尾工作。。。") ;
}
}
此类就是取代了after()方法的操作。
直接在applicationContext.xml中配置收尾的工作。
applicationContext.xml:
<bean id="before" class="org.lxh.aop.demo03.Before"></bean>
<bean id="after" class="org.lxh.aop.demo03.After"></bean>
<bean id="subject" class="org.lxh.aop.demo03.Subject"
abstract="true">
</bean>
<bean id="realsubject" class="org.lxh.aop.demo03.RealSubject"
parent="subject">
</bean>
<bean id="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.lxh.aop.demo03.Subject</value>
</property>
<property name="target">
<ref bean="realsubject" />
</property>
<property name="interceptorNames">
<list>
<value>before</value>
<value>after</value>
</list>
</property>
</bean>
所谓的AOP就是可以对程序进行灵活的配置,把与具体业务无关的操作都统一形成一个个的操作类,在配置文件之中进行整合。
3.4、SSH整合(绝对重点)
SSH:Struts + Spring + Hibernate。使用此种方式,程序中不再需要工厂,不再需要手工管理数据库连接,所有的属性配置都在Spring中完成。但是因为程序发展的历史问题,SSH整合是最麻烦的。
在整个操作中如果那个部分出错了,则全部的配置作废。
三个框架每个框家都有自己的配置文件:
• Struts:struts-config.xml
• Hibernate:hiberante.cfg.xml、*.hbm.xml
• Spring:applicationContext.xml
整合之后,Hibernate的配置文件将不再需要,所有的配置信息直接写在applicationContext.xml中即可。
但是添加框架支持的顺序:Spring Hibernate Struts。
整个一个程序,以简单的登陆程序为准,
3.4.1、准备工作
新建立一个项目:SSHDemo。
在MyEclipse中配置数据库的连接。
建立如下的数据库创建脚本:
USE demo ;
DROP TABLE user ;
CREATE TABLE user(
id VARCHAR(50) PRIMARY KEY NOT NULL ,
name VARCHAR(50) NOT NULL ,
password VARCHAR(32)
) ;
INSERT INTO user(id,name,password) VALUES ('admin','管理员','admin') ;
3.4.2、Spring + Hibernate
一定要记住:先加入Spring,之后再加入Hibernate,而且本身在加入之前必须保证已经建立好了一个数据库连接。
添加包的时候,一定要选择以下几个:
• Spring核心包
• Hiberante 3的支持开发包
• AOP包
• WEB支持的包,如果不加此包,则以后无法在web上使用Spring。
配置文件还是applicationContext.xml。
在Spring中所有的容器的信息都是通过log4j完成,需要建立一个log4j.properties的文件,用于打印容器启动问题的各种信息。
log4j.properties:
log4j.rootLogger=INFO, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=d:/ssh.log
log4j.appender.logfile.MaxFileSize=512KB
# Keep three backup files.
log4j.appender.logfile.MaxBackupIndex=3
# Pattern to output: date priority [category] - message
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
添加完spring之后加上Hibernate支持。
添加的时候将所有的包全部选中,之后的把所有的包加入到WEB-INF/lib目录之中。
此项目已经存在了Spring,所以此时询问,是否把Hibernate直接加入spring进行管理,选择加入管理,这样就不会再建立hibernate.cfg.xml文件了。
此处,表示在applicationContext.xml中配置一个sessionFactory,sessionFactory在Hibernate中表示所有的数据库连接,可以从中取得一个Session。
Spring + Hibernate中肯定需要操作数据库,此处表示使用的数据库配置,beanid表示的是Spring配置文件中的一个bean节点配置。
因为Hibernate中添加的包,可能与Spring中的包重复,所以此处询问,是否需要把旧的包进行替换。
此时就已经自动在applicationContext.xml中加入了Hibernate的若干配置。
applicationContext.xml说明:
1、 数据库连接配置
<bean id="dataSource" 表示文件中的一个实例
class="org.apache.commons.dbcp.BasicDataSource"> 使用了dbcp的JDBC操作包,使用此类进行数据库连接的操作管理。此类本身由Spring提供完成。
<property name="driverClassName"
value="org.gjt.mm.mysql.Driver"> 数据库的驱动程序
</property>
<property name="url" 连接地址
value="jdbc:mysql://localhost:3306/demo"></property>
<property name="username" value="root"></property> 用户名
<property name="password" value="mysqladmin"></property> 密码
</bean>
以后如果要把JDBC连接变为JNDI连接直接更换此处即可,其他的所有代码都不用改变。
2、 配置Hiberante的连接
<bean id="sessionFactory" 配置的是一个连接工厂
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource"> 表示要使用的数据库连接
<ref bean="dataSource"></ref> 连接使用dataSource实例
</property>
<property name="hibernateProperties"> Hibernate中的若干属性
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect 数据库的使用方言
</prop>
</props>
</property>
</bean>
在整个Spring中提供了一个Hibernate的操作模板,通过此模板可以帮用户自动处理何时打开数据库、何时关闭数据库。
3、 配置HibernateTemplate
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref bean="sessionFactory"></ref>
</property>
</bean>
此处表示定义了一个操作的模板,所以在以后的程序代码中,如果要想使用此模板,必须让DAO的实现类继承了HibernateDaoSupport类。
使用之前的数据库创建脚本完成一个登陆功能。
通过表创建完POJO和hbm.xml文件之后,会在applicationContext.xml中增加以下的内容:
<property name="mappingResources">
<list>
<value>org/lxh/ssh/vo/User.hbm.xml</value>
</list>
</property>
表示的是所有的资源文件。
定义IUserDAO接口,同时声明一个登陆的操作:
package org.lxh.ssh.dao;
import org.lxh.ssh.vo.User;
public interface IUserDAO {
public boolean findLogin(User user) throws Exception ;
}
此类与之前一样,但是在建立实现类的时候就不一样,必须为实现类中增加Spring的Hibernate支持。
package org.lxh.ssh.dao.impl;
import java.sql.SQLException;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.lxh.ssh.dao.IUserDAO;
import org.lxh.ssh.vo.User;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class IUserDAOImpl extends HibernateDaoSupport implements IUserDAO {
public boolean findLogin(final User user) throws Exception {
boolean flag = false;
try {
flag = (Boolean) super.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException, SQLException {
boolean flag = false;
String hql = "FROM User u WHERE u.id=? AND u.password=?";
Query q = session.createQuery(hql);
q.setString(0, user.getId());
q.setString(1, user.getPassword());
User u = (User) q.uniqueResult();
if (u != null) {
flag = true;
user.setName(u.getName());
}
return flag;
}
});
} catch (Exception e) {
throw e;
}
return flag;
}
}
之后修改applicationContext.xml文件,加入接口和实现类。
applicationContext.xml:
<bean id="iuserdao" class="org.lxh.ssh.dao.IUserDAO"
abstract="true">
</bean>
<bean id="iuserdaoimpl" class="org.lxh.ssh.dao.impl.IUserDAOImpl"
parent="iuserdao">
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"></ref>
</property>
</bean>
此两种配置完成之后,实现类和接口就要统一的接受Spring的管理了,所有的数据库的打开与关闭操作都与程序本身无关,完全由Spring处理。
测试:
import org.lxh.ssh.dao.IUserDAO;
import org.lxh.ssh.vo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"applicationContext.xml");
IUserDAO dao = (IUserDAO) ctx.getBean("iuserdaoimpl");
User user = new User();
user.setId("admin");
user.setPassword("admin");
System.out.println(dao.findLogin(user));
System.out.println(user.getName());
}
}
但此时在运行时出现了以下的错误:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
Caused by: java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
以上的错误提示是缺少必要的包。
之前存在一个替换包的过程,所以有可能有些包就无法加进来,此时必须手工加入所要的包。
添加完成之后,如果此包还没有的话,则可以从Spring的开发包中拷贝出来,放在lib之中。
之前在使用Hibernate的时候,可以直接在hibernate.cfg.xml文件中进行显示SQL语句的配置,实际上现在就可以直接把此属性加在applicationContext.xml文件之中。
<prop key="hibernate.show_sql">true</prop>
以后再操作数据库的时候就会自动显示所执行的SQL语句了。
3.4.3、Struts + Spring + Hibernate
完成了后台代码之后,下面就需要完成前台的操作,因为Spring本身是一个容器,所以需要在web.xml中注册此容器。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext.xml
</param-value>
</context-param>
找到applicationContext.xml中的配置文件,此文件将在容器启动的时候加载,同时启动Spring容器。
如果要想正确的启动容器,还需要增加一个监听:
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
此两个配置为固定的,表示允许WEB中使用Spring。
为项目添加Struts支持。
因为Sturts出现较早,所以对于配置来说比较麻烦,跟验证框架一样,Spring在Struts中是以插件的形式出现的,需要在struts-config.xml中进行配置。
<plug-in
className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/classes/applicationContext.xml" />
</plug-in>
就表示Struts中集成了Spring管理了。
建立Struts的AcionForm、Action。
UserAction.java:
package og.lxh.ssh.struts.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import og.lxh.ssh.struts.form.UserForm;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;
import org.lxh.ssh.dao.IUserDAO;
import org.lxh.ssh.vo.User;
public class UserAction extends DispatchAction {
private IUserDAO iuserdao = null;
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
UserForm userForm = (UserForm) form;
User user = new User();
user.setId(userForm.getId());
user.setPassword(userForm.getPassword());
try {
if (this.iuserdao.findLogin(user)) {
request.setAttribute("flag", true);
} else {
request.setAttribute("flag", false);
}
} catch (Exception e) {
e.printStackTrace();
}
return mapping.findForward("logindo");
}
// 此时的代码,会通过spring进行注入
public void setIuserdao(IUserDAO iuserdao) {
this.iuserdao = iuserdao;
}
}
以上是Struts中的代码。与之前相比把DAO作为一个类的属性存在了,不再像之前那样通过工厂取得,所有的DAO实例现在都是通过Spring注入进来的。
此时,应该把所有的Struts的处理请求先统一提交给Spring,因为Spring中要把DAO实例注入进来,所以在操作的时候要在struts-config.xml文件中加入以下的配置:
<controller
processorClass="org.springframework.web.struts.DelegatingRequestProcessor">
</controller>
加入以上的配置就表示所有的请求先交给Spring进行处理。
此时,就可以把完整的struts-config.xml文件整理出来:
<struts-config>
<data-sources />
<form-beans>
<form-bean name="userForm"
type="og.lxh.ssh.struts.form.UserForm" />
</form-beans>
<global-exceptions />
<global-forwards />
<action-mappings>
<action attribute="userForm" input="/form/user.jsp"
name="userForm" parameter="status" path="/user" scope="request"
type="og.lxh.ssh.struts.action.UserAction">
<forward name="logindo" path="/login.jsp"></forward>
</action>
</action-mappings>
<controller
processorClass="org.springframework.web.struts.DelegatingRequestProcessor">
</controller>
<message-resources
parameter="og.lxh.ssh.struts.ApplicationResources" />
<plug-in
className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/classes/applicationContext.xml" />
</plug-in>
</struts-config>
之后要在Spring中声明,Spring管理的是那个Action。修改applicationContext.xml文件。
applicationContext.xml:
<bean name="/user" class="og.lxh.ssh.struts.action.UserAction">
<property name="iuserdao">
<ref bean="iuserdaoimpl"></ref>
</property>
</bean>
此时,是把iuserdaoimpl的实例注入到Struts之中了。
建立JSP页面完成用户登陆操作
login.jsp:
<%@ page language="java" pageEncoding="GBK"%>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles"%>
<html:html lang="true">
<head>
<title>login.jsp</title>
</head>
<body>
<logic:present name="flag" scope="request">
<logic:equal value="true" name="flag">
<h2>
登陆成功!
</h2>
</logic:equal>
<logic:equal value="false" name="flag">
<h2>
登陆失败!
</h2>
</logic:equal>
</logic:present>
<form action="user.do" method="post">
用户ID:
<input type="text" name="id">
<br>
密码:
<input type="password" name="password">
<br>
<input type="submit" value="登陆">
<input type="reset" value="重置">
</form>
</body>
</html:html>
将项目部署到tomcat服务器之中。
进行数据库连接池的配置:
<Context path="/ssh" docBase="E:\ld\SSHDemo\WebRoot"
debug="5" reloadable="true" crossContext="true">
<Resource name="jdbc/mysql"
auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="mysqladmin"
driverClassName="org.gjt.mm.mysql.Driver"
url="jdbc:mysql://localhost:3306/mldnshop"/>
</Context>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/mysql</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
运行之后有可能出现以下的错误:
java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V
此错误是包的冲突造成的。删除掉冲突的包。把asm-2.2.3.jar包删除掉。
此时代码配置完成,SSH已经整合了,但是之前已经配置好了数据库连接池了,如果现在要在项目中使用数据库连接池,则必须更换一个数据库的操作模板。修改applicationContext.xml:
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/mysql</value>
</property>
</bean>
此时,程序中就可以使用JNDI的方式连接数据库了。
4、总结
1、 掌握Spring IOC的基本原理
2、 SSH项目整合
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = null;
// 表示找到配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从配置文件之中取得Bean的实例
Person per = (Person) ctx.getBean("per9");
System.out.println(per);
Iterator iter = per.getBooks().entrySet().iterator();
while (iter.hasNext()) {
Map.Entry me = (Map.Entry) iter.next();
Book b = (Book) me.getValue();
System.out.println("\t|- " + me.getKey() + " --> " + b.getTitle());
}
}
}
在以后的开发中Properties注入是最常见的一种,属性。
import java.util.Properties;
public class Person {
private Properties mypro ;
public Properties getMypro() {
return mypro;
}
public void setMypro(Properties mypro) {
this.mypro = mypro;
}
}
之后在配置文件中建立关系
<bean id="per" class="org.lxh.ioc.demo10.Person">
<property name="mypro">
<props>
<prop key="key1">value1</prop>
<prop key="key2">value2</prop>
<prop key="key3">value3</prop>
</props>
</property>
</bean>
准备好了三个所需要的属性值
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = null;
// 表示找到配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从配置文件之中取得Bean的实例
Person per = (Person) ctx.getBean("per");
System.out.println(per.getMypro());
}
}
3.3、AOP(了解)
AOP:面向方面的编程。
代理设计模式:
• 一个接口定义两个子类,其中一个子类包含另外一个子类。
• 代理完成比真实实现类更多的操作。
Subject.java:
package org.lxh.aop.demo01;
public interface Subject {
public void request() ;
}
真实主题实现类:RealSubject.java:
package org.lxh.aop.demo01;
public class RealSubject implements Subject {
public void request() {
System.out.println("真实的业务操作。。。");
}
}
代理主题操作类:ProxySubject.java:
package org.lxh.aop.demo01;
public class ProxySubject implements Subject {
private Subject sub = null ;
public ProxySubject(Subject sub){
this.sub = sub ;
}
public void before(){
System.out.println("操作开始的相关业务。");
}
public void request() {
this.before() ;
this.sub.request() ;
this.after() ;
}
public void after(){
System.out.println("将操作进行日志记录。");
}
}
测试类:
package org.lxh.aop.demo01;
public class Test {
public static void main(String[] args) {
Subject sub = new ProxySubject(new RealSubject()) ;
sub.request() ;
}
}
以上的程序是一个代理的基本操作。属于静态代理类的功能,但是如果要对多个接口进行代理,则无法做到,此时就要对程序进行扩充,使用动态代理操作。
动态代理操作中,一个类只需要实现InvocationHandler接口即可。
动态代理类Proxy.java:
package org.lxh.aop.demo02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Proxy implements InvocationHandler {
private Object sub;
public Object bind(Object obj) { // 绑定一个真实的对象操作
this.sub = obj;
return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
.getClassLoader(), obj.getClass().getInterfaces(), this);
}
public Object invoke(Object arg0, Method arg1, Object[] arg2)
throws Throwable {
// 调用接口中的方法
this.before();
Object obj = arg1.invoke(this.sub, arg2);
this.after();
return obj;
}
public void before() {
System.out.println("操作开始的相关业务。");
}
public void after() {
System.out.println("将操作进行日志记录。");
}
}
此时,通过程序进行测试。
package org.lxh.aop.demo02;
public class Test {
public static void main(String[] args) {
Subject sub = (Subject)new Proxy().bind(new RealSubject()) ;
sub.request() ;
}
}
以后只要想修改所有代理的相关业务操作,只需要修改动态代理类即可。
此时,可以直接使用Spring中的AOP功能,在配置文件中对程序进行功能的增加,以后只要修改配置文件,就可以完成程序横切面上的操作。
如果要想实现这样的AOP功能,在Spring中提供了若干个Advices接口,直接使用此接口实现类,就可以完成程序的之前操作、之后操作的代码。
例如:将之前的代码
使用MethodBeforeAdvice接口定义一个子类,就表示之前的操作代码类。
Before.java:
package org.lxh.aop.demo03;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class Before implements MethodBeforeAdvice {
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
System.out.println("*** 代理之前的操作!") ;
}
}
也就是把之前在代理类之中编写的before()方法,单独提取到一个类之中了,以后直接配置此类的实例就可以完成代理操作之前的工作。
在AOP中就必须在applicationContext.xml文件之中进行若干个配置才可以完成这种AOP开发。
applicationContext.xml:
<bean id="before" 定义真实主题操作之前的功能
class="org.lxh.aop.demo03.Before"></bean>
<bean id="subject" 定义接口
class="org.lxh.aop.demo03.Subject"
abstract="true">
</bean>
<bean id="realsubject" 定义接口实现类
class="org.lxh.aop.demo03.RealSubject"
parent="subject">
</bean>
<bean id="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean"> spring中已经提供了一个代理的操作类,只需要设置好若干属性即可
<property name="proxyInterfaces"> 被代理的接口
<value>org.lxh.aop.demo03.Subject</value>
</property>
<property name="target"> 代理中所包含的真实实现类
<ref bean="realsubject" />
</property>
<property name="interceptorNames">
<list>
<value>before</value> 定义before,因为实现MethodBeforeAdvices接口,所以会自动设置成在主题调用之前操作此代码。
</list>
</property>
</bean>
测试:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ctx = null ;
ctx = new ClassPathXmlApplicationContext("applicationContext.xml") ;
Subject sub = (Subject)ctx.getBean("proxy") ;
sub.request() ;
}
}
程序运行之后,代理之前的操作已经调用了。
同样,也可以在方法执行完毕之后进行相关的收尾工作:AfterReturningAdvice接口。
After.java:
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class After implements AfterReturningAdvice {
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
System.out.println("*** 收尾工作。。。") ;
}
}
此类就是取代了after()方法的操作。
直接在applicationContext.xml中配置收尾的工作。
applicationContext.xml:
<bean id="before" class="org.lxh.aop.demo03.Before"></bean>
<bean id="after" class="org.lxh.aop.demo03.After"></bean>
<bean id="subject" class="org.lxh.aop.demo03.Subject"
abstract="true">
</bean>
<bean id="realsubject" class="org.lxh.aop.demo03.RealSubject"
parent="subject">
</bean>
<bean id="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.lxh.aop.demo03.Subject</value>
</property>
<property name="target">
<ref bean="realsubject" />
</property>
<property name="interceptorNames">
<list>
<value>before</value>
<value>after</value>
</list>
</property>
</bean>
所谓的AOP就是可以对程序进行灵活的配置,把与具体业务无关的操作都统一形成一个个的操作类,在配置文件之中进行整合。
3.4、SSH整合(绝对重点)
SSH:Struts + Spring + Hibernate。使用此种方式,程序中不再需要工厂,不再需要手工管理数据库连接,所有的属性配置都在Spring中完成。但是因为程序发展的历史问题,SSH整合是最麻烦的。
在整个操作中如果那个部分出错了,则全部的配置作废。
三个框架每个框家都有自己的配置文件:
• Struts:struts-config.xml
• Hibernate:hiberante.cfg.xml、*.hbm.xml
• Spring:applicationContext.xml
整合之后,Hibernate的配置文件将不再需要,所有的配置信息直接写在applicationContext.xml中即可。
但是添加框架支持的顺序:Spring Hibernate Struts。
整个一个程序,以简单的登陆程序为准,
3.4.1、准备工作
新建立一个项目:SSHDemo。
在MyEclipse中配置数据库的连接。
建立如下的数据库创建脚本:
USE demo ;
DROP TABLE user ;
CREATE TABLE user(
id VARCHAR(50) PRIMARY KEY NOT NULL ,
name VARCHAR(50) NOT NULL ,
password VARCHAR(32)
) ;
INSERT INTO user(id,name,password) VALUES ('admin','管理员','admin') ;
3.4.2、Spring + Hibernate
一定要记住:先加入Spring,之后再加入Hibernate,而且本身在加入之前必须保证已经建立好了一个数据库连接。
添加包的时候,一定要选择以下几个:
• Spring核心包
• Hiberante 3的支持开发包
• AOP包
• WEB支持的包,如果不加此包,则以后无法在web上使用Spring。
配置文件还是applicationContext.xml。
在Spring中所有的容器的信息都是通过log4j完成,需要建立一个log4j.properties的文件,用于打印容器启动问题的各种信息。
log4j.properties:
log4j.rootLogger=INFO, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
log4j.appender.logfile.File=d:/ssh.log
log4j.appender.logfile.MaxFileSize=512KB
# Keep three backup files.
log4j.appender.logfile.MaxBackupIndex=3
# Pattern to output: date priority [category] - message
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
添加完spring之后加上Hibernate支持。
添加的时候将所有的包全部选中,之后的把所有的包加入到WEB-INF/lib目录之中。
此项目已经存在了Spring,所以此时询问,是否把Hibernate直接加入spring进行管理,选择加入管理,这样就不会再建立hibernate.cfg.xml文件了。
此处,表示在applicationContext.xml中配置一个sessionFactory,sessionFactory在Hibernate中表示所有的数据库连接,可以从中取得一个Session。
Spring + Hibernate中肯定需要操作数据库,此处表示使用的数据库配置,beanid表示的是Spring配置文件中的一个bean节点配置。
因为Hibernate中添加的包,可能与Spring中的包重复,所以此处询问,是否需要把旧的包进行替换。
此时就已经自动在applicationContext.xml中加入了Hibernate的若干配置。
applicationContext.xml说明:
1、 数据库连接配置
<bean id="dataSource" 表示文件中的一个实例
class="org.apache.commons.dbcp.BasicDataSource"> 使用了dbcp的JDBC操作包,使用此类进行数据库连接的操作管理。此类本身由Spring提供完成。
<property name="driverClassName"
value="org.gjt.mm.mysql.Driver"> 数据库的驱动程序
</property>
<property name="url" 连接地址
value="jdbc:mysql://localhost:3306/demo"></property>
<property name="username" value="root"></property> 用户名
<property name="password" value="mysqladmin"></property> 密码
</bean>
以后如果要把JDBC连接变为JNDI连接直接更换此处即可,其他的所有代码都不用改变。
2、 配置Hiberante的连接
<bean id="sessionFactory" 配置的是一个连接工厂
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource"> 表示要使用的数据库连接
<ref bean="dataSource"></ref> 连接使用dataSource实例
</property>
<property name="hibernateProperties"> Hibernate中的若干属性
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect 数据库的使用方言
</prop>
</props>
</property>
</bean>
在整个Spring中提供了一个Hibernate的操作模板,通过此模板可以帮用户自动处理何时打开数据库、何时关闭数据库。
3、 配置HibernateTemplate
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref bean="sessionFactory"></ref>
</property>
</bean>
此处表示定义了一个操作的模板,所以在以后的程序代码中,如果要想使用此模板,必须让DAO的实现类继承了HibernateDaoSupport类。
使用之前的数据库创建脚本完成一个登陆功能。
通过表创建完POJO和hbm.xml文件之后,会在applicationContext.xml中增加以下的内容:
<property name="mappingResources">
<list>
<value>org/lxh/ssh/vo/User.hbm.xml</value>
</list>
</property>
表示的是所有的资源文件。
定义IUserDAO接口,同时声明一个登陆的操作:
package org.lxh.ssh.dao;
import org.lxh.ssh.vo.User;
public interface IUserDAO {
public boolean findLogin(User user) throws Exception ;
}
此类与之前一样,但是在建立实现类的时候就不一样,必须为实现类中增加Spring的Hibernate支持。
package org.lxh.ssh.dao.impl;
import java.sql.SQLException;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.lxh.ssh.dao.IUserDAO;
import org.lxh.ssh.vo.User;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class IUserDAOImpl extends HibernateDaoSupport implements IUserDAO {
public boolean findLogin(final User user) throws Exception {
boolean flag = false;
try {
flag = (Boolean) super.getHibernateTemplate().execute(
new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException, SQLException {
boolean flag = false;
String hql = "FROM User u WHERE u.id=? AND u.password=?";
Query q = session.createQuery(hql);
q.setString(0, user.getId());
q.setString(1, user.getPassword());
User u = (User) q.uniqueResult();
if (u != null) {
flag = true;
user.setName(u.getName());
}
return flag;
}
});
} catch (Exception e) {
throw e;
}
return flag;
}
}
之后修改applicationContext.xml文件,加入接口和实现类。
applicationContext.xml:
<bean id="iuserdao" class="org.lxh.ssh.dao.IUserDAO"
abstract="true">
</bean>
<bean id="iuserdaoimpl" class="org.lxh.ssh.dao.impl.IUserDAOImpl"
parent="iuserdao">
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"></ref>
</property>
</bean>
此两种配置完成之后,实现类和接口就要统一的接受Spring的管理了,所有的数据库的打开与关闭操作都与程序本身无关,完全由Spring处理。
测试:
import org.lxh.ssh.dao.IUserDAO;
import org.lxh.ssh.vo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"applicationContext.xml");
IUserDAO dao = (IUserDAO) ctx.getBean("iuserdaoimpl");
User user = new User();
user.setId("admin");
user.setPassword("admin");
System.out.println(dao.findLogin(user));
System.out.println(user.getName());
}
}
但此时在运行时出现了以下的错误:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
Caused by: java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
以上的错误提示是缺少必要的包。
之前存在一个替换包的过程,所以有可能有些包就无法加进来,此时必须手工加入所要的包。
添加完成之后,如果此包还没有的话,则可以从Spring的开发包中拷贝出来,放在lib之中。
之前在使用Hibernate的时候,可以直接在hibernate.cfg.xml文件中进行显示SQL语句的配置,实际上现在就可以直接把此属性加在applicationContext.xml文件之中。
<prop key="hibernate.show_sql">true</prop>
以后再操作数据库的时候就会自动显示所执行的SQL语句了。
3.4.3、Struts + Spring + Hibernate
完成了后台代码之后,下面就需要完成前台的操作,因为Spring本身是一个容器,所以需要在web.xml中注册此容器。
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/classes/applicationContext.xml
</param-value>
</context-param>
找到applicationContext.xml中的配置文件,此文件将在容器启动的时候加载,同时启动Spring容器。
如果要想正确的启动容器,还需要增加一个监听:
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
此两个配置为固定的,表示允许WEB中使用Spring。
为项目添加Struts支持。
因为Sturts出现较早,所以对于配置来说比较麻烦,跟验证框架一样,Spring在Struts中是以插件的形式出现的,需要在struts-config.xml中进行配置。
<plug-in
className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/classes/applicationContext.xml" />
</plug-in>
就表示Struts中集成了Spring管理了。
建立Struts的AcionForm、Action。
UserAction.java:
package og.lxh.ssh.struts.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import og.lxh.ssh.struts.form.UserForm;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.actions.DispatchAction;
import org.lxh.ssh.dao.IUserDAO;
import org.lxh.ssh.vo.User;
public class UserAction extends DispatchAction {
private IUserDAO iuserdao = null;
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
UserForm userForm = (UserForm) form;
User user = new User();
user.setId(userForm.getId());
user.setPassword(userForm.getPassword());
try {
if (this.iuserdao.findLogin(user)) {
request.setAttribute("flag", true);
} else {
request.setAttribute("flag", false);
}
} catch (Exception e) {
e.printStackTrace();
}
return mapping.findForward("logindo");
}
// 此时的代码,会通过spring进行注入
public void setIuserdao(IUserDAO iuserdao) {
this.iuserdao = iuserdao;
}
}
以上是Struts中的代码。与之前相比把DAO作为一个类的属性存在了,不再像之前那样通过工厂取得,所有的DAO实例现在都是通过Spring注入进来的。
此时,应该把所有的Struts的处理请求先统一提交给Spring,因为Spring中要把DAO实例注入进来,所以在操作的时候要在struts-config.xml文件中加入以下的配置:
<controller
processorClass="org.springframework.web.struts.DelegatingRequestProcessor">
</controller>
加入以上的配置就表示所有的请求先交给Spring进行处理。
此时,就可以把完整的struts-config.xml文件整理出来:
<struts-config>
<data-sources />
<form-beans>
<form-bean name="userForm"
type="og.lxh.ssh.struts.form.UserForm" />
</form-beans>
<global-exceptions />
<global-forwards />
<action-mappings>
<action attribute="userForm" input="/form/user.jsp"
name="userForm" parameter="status" path="/user" scope="request"
type="og.lxh.ssh.struts.action.UserAction">
<forward name="logindo" path="/login.jsp"></forward>
</action>
</action-mappings>
<controller
processorClass="org.springframework.web.struts.DelegatingRequestProcessor">
</controller>
<message-resources
parameter="og.lxh.ssh.struts.ApplicationResources" />
<plug-in
className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/classes/applicationContext.xml" />
</plug-in>
</struts-config>
之后要在Spring中声明,Spring管理的是那个Action。修改applicationContext.xml文件。
applicationContext.xml:
<bean name="/user" class="og.lxh.ssh.struts.action.UserAction">
<property name="iuserdao">
<ref bean="iuserdaoimpl"></ref>
</property>
</bean>
此时,是把iuserdaoimpl的实例注入到Struts之中了。
建立JSP页面完成用户登陆操作
login.jsp:
<%@ page language="java" pageEncoding="GBK"%>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles"%>
<html:html lang="true">
<head>
<title>login.jsp</title>
</head>
<body>
<logic:present name="flag" scope="request">
<logic:equal value="true" name="flag">
<h2>
登陆成功!
</h2>
</logic:equal>
<logic:equal value="false" name="flag">
<h2>
登陆失败!
</h2>
</logic:equal>
</logic:present>
<form action="user.do" method="post">
用户ID:
<input type="text" name="id">
<br>
密码:
<input type="password" name="password">
<br>
<input type="submit" value="登陆">
<input type="reset" value="重置">
</form>
</body>
</html:html>
将项目部署到tomcat服务器之中。
进行数据库连接池的配置:
<Context path="/ssh" docBase="E:\ld\SSHDemo\WebRoot"
debug="5" reloadable="true" crossContext="true">
<Resource name="jdbc/mysql"
auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="root" password="mysqladmin"
driverClassName="org.gjt.mm.mysql.Driver"
url="jdbc:mysql://localhost:3306/mldnshop"/>
</Context>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/mysql</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
运行之后有可能出现以下的错误:
java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V
此错误是包的冲突造成的。删除掉冲突的包。把asm-2.2.3.jar包删除掉。
此时代码配置完成,SSH已经整合了,但是之前已经配置好了数据库连接池了,如果现在要在项目中使用数据库连接池,则必须更换一个数据库的操作模板。修改applicationContext.xml:
<bean id="dataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/mysql</value>
</property>
</bean>
此时,程序中就可以使用JNDI的方式连接数据库了。
4、总结
1、 掌握Spring IOC的基本原理
2、 SSH项目整合