SSH是Spring + Struts + Hibernate,Struts是对Servlet的一个封装,通过配置文件来达到对Web请求做处理;SpringMVC是对系统的设计,使用轻量级、最小侵入的原则,通过配置文件使得程序员可以更专注逻辑/业务处理;Hibernate是对jdbc的优化处理,可以使用Hibernate直接操作bean,使用hibernate中的Session来操作数据库,支持事务处理。
Struts
一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。
主要配置struts.xml
<package name="default" namespace="" extends="struts-default"> <action name="action" class="com.test.demo.Action" method="sayHello"> <result name="success" value="">some.jsp</result> </action> </package>
注解方式:
@ParentPackage("struts-default")// 与package中的extends功能一样 @Namespace("/user")// package中的namespace一样,指定上级请求 public class UserAction extends ActionSupport implements ServletRequestAware, ServletResponseAware { private static final long serialVersionUID = 1L; private HttpServletRequest req; private HttpServletResponse resp; @Action("/login") public void login() throws Exception { // do login } }
上面的示例代码中有实现 ServletRequestAware 和 ServletResponseAware 接口,这两个接口可以让我们获取到对应的 HttpServletRequest、HttpServletResponse 。
获取其参数也可以通过Context:
ServletActionContext.getRequest();
ServletActionContext.getServletContext();
ServletActionContext.getContext().getParameters();
需要注意的是获取post中的JSON数据时需要通过流的形式读取,我这里使用的是 BufferedReader 读取其字符,并相应解码:
private String parseReq() throws IOException { BufferedReader br = this.request.getReader(); String line = null; StringBuilder sb = new StringBuilder(); while((line = br.readLine()) != null) { sb.append(line); } JSONObject json = JSON.parseObject(sb.toString()); return json.toJSONString(); }
Struts也可以实现输入验证及国际化,具体的请百度。可能listener这个配置我们开发中会用到,可以用来实现在线人数的统计,通过实现 HttpSessionListener 来监听
// 会话被创建 default public void sessionCreated(HttpSessionEvent se) {} // 会话被销毁 default public void sessionDestroyed(HttpSessionEvent se) {}
如果需要实现其他的功能,可以设置其他的Listener,并获取相应的数据,这里就不说了。
Hibernate
1、配置文件:配置一些数据库需要的基本数据,数据库引擎、url、driver、用户名、密码等基本配置,还有就是hibernate的参数配置,能使性能更佳。
2、创建持久化对象
public class Bean {
private int userId; private String name; // ...... }
使用hibernate应该使用xml配置,获取对应文件的获取,文档上介绍的是xml获取效率更高
<hibernate-mapping> <class name="com.test.user.User" table="USER"> <id name="userId" type="int"> <column name="USERID" /> <generator class="increment" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" /> </property> </class> </hibernate-mapping>
3、获取Configuration new StandardServiceRegistryBuilder().configure()
4、获取SessionFactory
private static void getSessionFactory() { final StandardServiceRegistry sr = new StandardServiceRegistryBuilder().configure().build(); try { sf = new MetadataSources(sr).buildMetadata().buildSessionFactory(); } catch (Exception e) { e.printStackTrace(); StandardServiceRegistryBuilder.destroy(sr); } }
5、获取Session接口操作数据库
getSessionFactory(); s = sf.openSession(); session.set(s);
6、获取事务接口Transaction
session.beginTransaction(); // some operations ; transaction.commit()
7、通过 session.createQuery("HQL"); session.createSQLQuery("原生的sql语句"); 获取Query实例
关于HQL语句
与原生SQL类似, from、select、where以及其他的运算符 ,HQL可以直接使用对象进行操作。Hibernate中的映射关系需要注意,
有一对一双/单向、一对多单/双、多对多单/双向的配置问题,下面是一对一双向的配置示例:
<!-- Person.hbm.xml -->
<hibernate-mapping> <class name="Person" table="PERSON" lazy="true"> <id name="id" type="int"><column name="ID"/><generator class="native"/></id> <property name="name"/> <one-to-one name="idCard" class="IdCard" fetch="join" cascade="all"></one-to-one> </class> </hibernate-mapping>
<!-- IdCard.hbm.xml --> <hibernate-mapping> <calss name="IdCard" table="ID_CARD" lazy="true"> <id name="id" type="int"> <column name="ID"/> <!-- 引用person的主键作为IdCard的主键和外健 --> <generator class="foreign"> <param name="property">person</param> </generator> <id> <property name="cardNo"/> <!-- 表示IdCard引用了person的主键作为了外健 --> <one-to-one name="person" class="Person" constrained="true"></one-to-one> </class> </hibernate-mapping>
其相应的Java类
public class Person { private IdCard idCard; private String name; private int id; // get setter.... } public class IdCard { private Person person; private String cardNo; private int id; // get setter.... }
Spring
IoC/DI
控制反转/依赖注入,简单来说就是由一个类(也就是Context)去创建bean对象,然后通过注入的方式产生使用类对bean的引用,bean的生命周期由Context控制。
示例:
class ADemo { public void methodA() { //...... } } class BDemo { public void methodB() { //...... } }
如果在ADemo中需要使用到BDemo对象,那么我们可以使用Context创建,就跟main函数一样:
class ADemo { private BDemo b; public void setB(BDemo b) { this.b = b; } public void methodA() { b.methodB(); //...... } } class BDemo { public void methodB() { //...... } } class ExampleContext { public static void main(String[] args) { ADemo a = new ADemo(); BDemo b = new BDemo(); // 将b注入到使用者a中,这样a只对b产生了引用,并没有实际拥有 // 一般想到的方法是向a中声明一个属性,然后通过set方法注入 a.setB(b); } }
当然也是可以使用构造器注入,接口注入(其实相当于方法注入)等等,最主要的思想是将a对b的一个强引用变成弱引用,使得使用者无需考虑其声明周期及内存管理问题,同时还能将这个对象复用,减少多个重复对象的创建。
在Spring中通过 AnnotationConfigApplicationContext ClassPathXmlApplicationContext 读取相关的配置,利用Java的反射机制获取相关类并创建实例对象,在需要的时候注入到相关的类中。
AOP
AOP为Aspect Oriented Programming的缩写,面向切面编程,在不改动原来代码情况下插入需要执行的代码,可以很好的处理日志打印。简单的理解就是在指定的规则下筛选出符合规定的方法,aop有before、after、around,可以在方法之前、之后、环绕的时候织入代码。说到织入代码,分为两种:静态织入、动态织入,静态织入是需要借助 execution 表达式,表达式的格式为:
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?) // 比如: @Before("execution(* com.demo.aop.BDemo.methodB(..))") public void methodA(JoinPoint jp) { // do something }
动态织入我理解为做标记,由Spring容器发现需要织入的PointCut,首先需要定义一个注解,这个注解就是一个标记符,在告诉Spring 这是一个标记
// 只能方法使用 @Target(ElementType.METHOD) // 运行时 @Retention(RetentionPolicy.RUNTIME) public @interface ExampleAop { String name(); }
定义好了标记之后,就需要指定方法需要被关注,直接使用@ExampleAop("some info")来标记
@ExampleAop(name="注解式拦截的methodA") public void methodA() { // do something }
做完这些之后,我们需要对这个标记附上意义,就犹如你定义一个“œ”操作符,如果你没规定这个符号代表什么,也就无法使用它,就像我们使用“+”表示两个数的相加一样,那么我们将ExampleAop作为一个筛选的规则,所有打上这个标记的方法都需要执行某段代码。如:
// 注解式拦截 @Pointcut("@annotation(com.demo.aop.ExampleAop)") public void annotionPointcut() {} @Before("annotionPointcut()") public void methodBefore(JoinPoint jp) { System.out.println("上帝啊,竟然在一个切面里添加多个Before,使用不同的方式拦截"); }
所有由标记的方法都会在执行前加上一句打印。
在Spring MVC中,如果我们使这些打印实现的话,还需要配置文件,这里我贴上代码,就不详细介绍了
@Configuration @ComponentScan("com.demo.aop") @EnableAspectJAutoProxy// 开启对AspectJ代理的支持 public class ConfigAop { }
需要了解更多的可以阅读相关文档(Spring家族文档)