SSH三大框架分别是Struts2 Spring Hibernate这三个框架组成,这篇文章主要说一下Spring(小白刚学,大神绕道。。)
Spring的核心是个轻量级容器,实现了IoC(控制翻转)模式的容器,基于此核心容器
所建立的应用程序,可以达到程序组件的松散耦合。这些特性都使得整个应用程序维护
简化。 Spring框架核心由下图所示的七个模块组成。
这是Spring框架最基础的部分,它提供了依赖注入(Dependency Injection)特征来
实现容器对Bean的管理。这里最基本的概念是BeanFactory,它是任何Spring应用的核心。
BeanFactory是工厂模式的一个实现,它使用IoC将应用配置和依赖说明从实际的应用代码
中分离出来。
AOP即面向切面编程技术,Spring在它的AOP模块中提供了对面向切面编程的丰富支持。
AOP允许通过分离应用的业务逻辑与系统级服务(例如安全和事务管理)进行内聚性的开
发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责其它的
系统级关注点,例如日志或事务支持。
Hibernate是成熟的ORM产品,Spring并没有自己实现ORM框架而是集成了几个流行
的ORM产品如Hibernate、JDO和iBATIS等。可以利用Spring对这些模块提供事务支
持等。
Spring虽然集成了几个ORM产品,但也可以不选择这几款产品,因为Spring提供了JDBC
和DAO模块。该模块对现有的JDBC技术进行了优化。你可以保持你的数据库访问代码干净
简洁,并且可以防止因关闭数据库资源失败而引起的问题。
Web上下文模块建立于应用上下文模块之上,提供了一个适合于Web应用的上下文。另外
,这个模块还提供了一些面向服务支持。例如:实现文件上传的multipart请求,它也提供了
Spring和其它Web框架的集成,比如Struts、WebWork。
核心模块的BeanFactory使Spring成为一个容器,而上下文模块使它成为一个框架。Web
上下文模块建立于应用上下文模块之上,提供了一个适合于Web应用的上下文。该模块还提
供了一些面向服务支持这个模块扩展了BeanFactory的概念,增加了对国际化(I18N)消息、
事件传播以及验证的支持。
另外,这个模块还提供了许多企业服务,例如电子邮件、JNDI访问、EJB集成、远程以
及时序调度scheduling)服务。也包括对模版框架例如Velocity和FreeMarker集成的支持。
另外,这个模块还提供了许多企业服务,例如电子邮件、JNDI访问、EJB集成、远程以
及时序调度scheduling)服务。也包括对模版框架例如Velocity和FreeMarker集成的支持。
Spring为构建Web应用提供了一个功能全面的MVC框架。虽然Spring可以很容易地与其
它MVC框架集成,例如Struts2,但Spring的MVC框架使用IoC对控制逻辑和业务对象提供了
完全的分离。
在Spring中,两个核心模块就是 IOC (控制反转) 和 AOP(Aspect Oriented Programming)面向切面编程
这张图片转自点击打开链接
在Spring中,我们同样和之前一样,导入lib包,拷贝配置文件。
首先我们建立java Project,手动的调用配置文件
在spring包中,以2.5为分界线,2.5以后的版本能够明显的感觉大小比之前的小,这是因为公司觉得每次讲所有的lib包全部导入项目又没有使用,占空间也没什么效率,所以直接将包给去掉了。所以才导致后面的版本比前面的小(主要是lib包里面去掉了,实际上是增加的)
<span style="font-size:18px;">public void test(){ BeanFactory factory =new ClassPathXmlApplicationContext("applicationContext.xml");</span>
<span style="font-size:18px;">/*applicationContext.xml为spring的配置文件,读取Spring配置文件,创建一个Bean工厂*/ Person p =(Person) factory.getBean("p"); /*读取Spring容器一个称为hello的bean,Spring容器自动创建对象实例*/ System.out.println(p); }</span>在 配置文件中,我们只需要配置这样的xml就可以实现了
<?xml version="1.0" encoding="UTF-8"?> <!-- - Middle tier application context definition for the image database. --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <bean id="p" class="cn.hncu.Person" scope="prototype"> </bean> </beans></span>我们每次再运行时候,只需要配置这个
< !--在Spring中配置bean的id以及所对应的类-->
<bean id="p" class="cn.hncu.Person"></bean>
Spring的作用域:默认的是单例 scope="singleton"
scope="prototype" 多例(可以自己配置)
request request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
session session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效.
在这个配置中存在一个Person的javaBean对象,(自己写的),这样就能运行了,从开始到结束,我们没有new person这个对象,spring帮我们new 的对象,
在3.1中的测试类
@Test
public void test2(){ ApplicationContext context =new ClassPathXmlApplicationContext("applicationContext.xml"); Person p=context.getBean(Person.class); System.out.println(p); Person p1=(Person) context.getBean("p"); System.out.println(p1);}其他的文件配置文件 和2.5一样,也能达到可以用spring new 对象
IoC(Inversion of Control)中文译为控制反转也可以叫做DI(Dependency Injection,依赖注入)。
控制反转模式的基本概念是:不直接创建对象,但是描述创建它们的方式。在工程中使用
该Bean时由Spring容器创建Bean的实例。在代码中不直接与对象和服务连接,但在配置
文件中描述哪一个组件需要哪一项服务。
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如
何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,
从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制
权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方
便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”
的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,
应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— “别找我们,我们找你”;
即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
Spring注入(又称依赖注入DI):其目的是为其中bean的属性赋值。
其实就是在配置文件中配置,spring就能够通过这个配置文件去造相应的对象。
一个javaBean中可能会出现其他的,如list,set,array等,配置就有点技巧
<bean id="person" class="cn.hncu.demo3.Person"></span>
<!-- 正常的配置 --> <property name="name" value="Jack"> </property> <property name="age" value="25"></property> <!-- 属性中包含集合 --> <property name="names"> <list> <value>1111</value> <value>abc</value> <value>汉字</value> </list> </property>
<!-- 属性中包含map --></span>
<p><span style="font-size: 18px;"><property name="map"> </span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span><map></span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span> <entry key="name" value="益阳"/> </span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span><entry key="age" value="30"/> </span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span><entry key="sex" value="男"/></span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span> </map> </span></p><p><span style="font-size: 18px;"></property></span></p>
<!-- 属性中包含set --></span>
<p><span style="font-size: 18px;"><property name="set"> </span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span><set> </span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span><value>aaa</value> </span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span><value>中国中国</value> </span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span><value>ssss</value></span></p><p><span style="font-size: 18px;"><span style="white-space: pre;"></span> </set></span></p><p><span style="font-size: 18px;"></property></span></p>
<!-- 属性中包含数组 --></span>
<span style="font-size: 18px;"><property name="obj"></span><br style="font-size: 18px;" /><span style="font-size: 18px; white-space: pre;"></span><span style="font-size: 18px;"><array></span><br style="font-size: 18px;" /><span style="font-size: 18px; white-space: pre;"></span><span style="font-size: 18px;"><value>对象数组</value></span><br style="font-size: 18px;" /><span style="font-size: 18px; white-space: pre;"></span><span style="font-size: 18px;"><ref bean="tom"/><!-- 引用其他的id为tom的对象 --></span><br style="font-size: 18px;" /><span style="font-size: 18px; white-space: pre;"></span><span style="font-size: 18px;"></array></span><br style="font-size: 18px;" /><span style="font-size: 18px; white-space: pre;"></span><span style="font-size: 18px;"></property></span>
<!-- 可以从其他的位置导入,不用写在同一个文件里面,但是在后面基本都会在web.xml中统一配置--></span>
面向切面编程:Aspect Oriented Programming,可以通过预编译方式和运行期动态代理实现
在不修改源代码的情况下给程序动态统一添加功能的一种技术。
织入(Weaving):把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个
被通知(advised)的对象。 这些可以在编译时,类加载时和运行时完成。Spring和其它纯
Java AOP框架一样,在运行时完成织入。
通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作。通知有各种
类型,其中包括“around”、“before”和“after”等通知。 通知的类型将在后面部分进行讨论。
许多AOP框架,包括Spring,都是以拦截器做通知模型,并维护一个以连接点为中心的拦截
器链。
通知的类型:前置通知(Before advice):在某连接点(join point)之前执行的通知,但这个通知不能
阻止连接点前的执行(除非它抛出一个异常)。
返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知:
例如,一个方法没有抛出任何异常,正常返回。
抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。后置通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常
返回还是异常退出)。
环绕通知(Around Advice):包围一个连接点(join point)的通知,如方法调用。这是
最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否
继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
我们首先采用java代码来实现切面
切面= 切点 + 通知
通知 类型很多种,前面已经介绍了:
实现的接口不一样,环绕是 MethodInterceptor();(以后会用的很多)
<span style="font-size:18px;">//这是前后都拦截 @Test public void test(){ //1,声明被切面对象,也就是被代理对象-----被代理对象 Person p =new Person(); //2,声明一个代理对象的类-----代理对象 ProxyFactoryBean factory =new ProxyFactoryBean(); factory.setTarget(p);//3,设置切面对象 //4,切点 JdkRegexpMethodPointcut cut =new JdkRegexpMethodPointcut(); //5,设置切点的位置 cut.setPattern("cn.aop.Person.say");//这是切所有的函数 //6,通知 ,时间 //拦截方法,这里可以我们写一个类去实现这个接口,通知修改也是这里 Advice advice = new MethodInterceptor() { @Override public Object invoke(MethodInvocation method) throws Throwable { System.out.println("前面拦截。。"); Object objreturn=method.proceed(); System.out.println("后拦截"); return objreturn; } }; //7,切面 = 切点 + 通知 Advisor advisor= new DefaultPointcutAdvisor(cut, advice); //8 给代理设置一个切面 factory.addAdvisor(advisor); //9,从代理中获取被代理后的对象 // Person p1 = (Person) factory.getProxy(); Person p1 = (Person)factory.getObject(); //10,让代理后的对象去调内部函数 p1.run();//这是person中,我们写的方法 p1.say(); }</span>
(只是在拦截方法代理的时候实现的接口不一样 之前是MethodBeforeAdvice() )
修改的只有一个地方
<span style="font-size:18px;">@Test public void test2(){ //1,声明被切面对象,也就是被代理对象-----被代理对象 Person p =new Person(); //2,声明一个代理对象的类-----代理对象 ProxyFactory factory =new ProxyFactory(); factory.setTarget(p);//3,设置切面对象 //4,切点 JdkRegexpMethodPointcut cut =new JdkRegexpMethodPointcut(); //5,设置切点的位置 cut.setPattern("cn.aop.Person.*");//这是切所有的函数 //6,通知 ,时间 //拦截方法 Advice advice =new MethodBeforeAdvice() { @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("执行这个之前的了。"); } }; //7,切面 = 切点 + 通知 Advisor advisor= new DefaultPointcutAdvisor(cut, advice); //8 给代理设置一个切面 factory.addAdvisor(advisor); //9,从代理中获取被代理后的对象 Person p1 = (Person) factory.getProxy(); //10,让代理后的对象去调内部函数 p1.run(); p1.say(); }</span>
只是用的接口是AfterReturningAdvice()
//这是之后拦截 @Test public void test3(){ //1,声明被切面对象,也就是被代理对象-----被代理对象 Person p =new Person(); //2,声明一个代理对象的类-----代理对象 ProxyFactory factory =new ProxyFactory(); factory.setTarget(p);//3,设置切面对象 //4,切点 JdkRegexpMethodPointcut cut =new JdkRegexpMethodPointcut(); //5,设置切点的位置 cut.setPattern("cn.aop.Person.*");//这是切所有的函数 //6,通知 ,时间 //拦截方法 Advice advice =new AfterReturningAdvice() { @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println("这是执行之后的"); } }; //7,切面 = 切点 + 通知 Advisor advisor= new DefaultPointcutAdvisor(cut, advice); //8 给代理设置一个切面 factory.addAdvisor(advisor); //9,从代理中获取被代理后的对象 Person p1 = (Person) factory.getProxy(); //10,让代理后的对象去调内部函数 p1.run(); p1.say(); }</span>上面 的都是 java代码自己造对象,不符合spring的原理。下面采用spring的技术》》》
配置文件如下:根据上面的java代码修改,我们都知道spring能帮我们new 对象,所以全部new 对象的用配置文件实现。
<span style="font-size:18px;"> <!--注意: 要根据bean之间的依赖关系,安排好先后顺序 --> <bean id="person" class="cn.aop.Person"></bean> <!--切点 --> <bean id="cut" class="org.springframework.aop.support.JdkRegexpMethodPointcut"> <property name="pattern" value="cn.aop.Person.*n"></property> </bean> <!--通知 平台中没有的类,要自己新建 --> <bean id="advice" class="cn.aop.demo1.Around"> </bean> <!-- 切面 = 切点+通知 --> <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <!-- 属性名要通过 平台自己的构造方法中的参数名来推测 --> <property name="pointcut" ref="cut"></property> <property name="advice" ref="advice"></property> </bean> <!--代理类对象 --> <bean id="factory" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="person" /> <!-- 可以通过查API或源代码找到属性名称和类型,然后就可以配 --> <property name="interceptorNames"> <list> <value>advisor</value> </list> </property> </bean></span>
上面的这个可以简化。。(不多说了)
这样我们的切点就写的太死了,不够灵活,切点可以采用正则表达式来进行匹配需要拦截(切面)那个?
切面重新使用一个lib包中的类就可以
切面里面配置文件修改
<span style="font-size:18px;"> <!--注意: 要根据bean之间的依赖关系,安排好先后顺序 --> <bean id="person" class="cn.aop.Person"></bean> <!-- 切点和通知的整合体--> <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 属性名要通过 平台自己的构造方法中的参数名来推测 --> <property name="patterns" > <list> <value>.*run.*</value> </list> </property> <property name="advice" > <!--通知 平台中没有的类,要自己新建 --> <bean id="advice" class="cn.aop.demo1.Around"> </bean> </property> </bean> <!--代理类对象 --> <bean id="factory" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="person" /> <!-- 可以通过查API或源代码找到属性名称和类型,然后就可以配 --> <property name="interceptorNames"> <list> <value>advisor</value> </list> </property> </bean></span>其实修改的地方是
<!-- 切点和通知的整合体-->
<bean id="advisor" class="
org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 属性名要通过 平台自己的构造方法中的参数名来推测 ,
但是还只是针对单个的。。有缺陷,不能拦截返回值,参数类型和是否带参数等-->
<property name="patterns" > <list> <value>.*run.*</value> </list> </property>
<property name="advice" > <!--通知 平台中没有的类,要自己新建 --> <bean id="advice" class="cn.aop.demo1.Around"> </bean> </property> </bean>
自动代理就是加一行代码,
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean><!--注意: 要根据bean之间的依赖关系,安排好先后顺序 -->
<span style="font-size:18px;"> <bean id="person" class="cn.aop.Person"></bean> <bean id="stud" class="cn.aop.Student"></bean> <!-- 自动 代理对象 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean> <!--自己写的自动代理 <bean class="cn.aop.auto.MyAuto"></bean> <span style="white-space:pre"> </span>--> <!-- 切点和通知的整合体--> <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 属性名要通过 平台自己的构造方法中的参数名来推测 --> <property name="patterns" > <list> <value>.*run.*</value> <value>.*exe.*</value><!-- 只拦截方法中包含run和exe的方法,后面还有aspecj技术可以拦截整个栏目 --> </list> </property> <property name="advice" > <!--通知 平台中没有的类,要自己新建 --> <bean id="advice" class="cn.aop.demo1.Around"> </bean> </property> </bean></span></span>有 了自动代理,我们就只需要切面,切点和通知, 代理类,就能实现代理。
我们可以自己写一个自动代理类
自动代理需要实现BeanPostProcessor接口,我们还需要拿到配置文件里面的切面,需要实现ApplicationContextAware,一共实现两个接口就能做自动代理。
代码:
<span style="font-size:18px;">package cn.aop.auto; import org.springframework.aop.Advisor; import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import cn.aop.Person; public class MyAuto implements BeanPostProcessor,ApplicationContextAware{ private ApplicationContext ctx; //初始化之后 @Override public Object postProcessAfterInitialization(Object bean, String arg1) throws BeansException { if(bean instanceof Person){ // ProxyFactoryBean factory =new ProxyFactoryBean(); ProxyFactory factory =new ProxyFactory(); factory.setTarget(bean);//设置要进行代理的类 //自动代理的 new出来不行的,能够自动实行自动代理,肯定已经有容器了 //之前我们ApplicationContext context =new ClassPathXmlApplicationContext("cn/ioc/1.xml"); //因此我们需要实现接口ApplicationContextAware 获得容器ctx //通过 配置文件里面的id,我们可以获得切面(切点加通知)都配置完成了 Advisor advisor = (Advisor) ctx.getBean("advisor"); factory.addAdvisor(advisor);//添加进行代理 return factory.getProxy(); //可以一下得到很多advisor //String[] names=ctx.getBeanDefinitionNames(); //我们可以通过这个 来进行判断是否是我们需要的。 } return bean; } //初始化之前 @Override public Object postProcessBeforeInitialization(Object bean, String arg1) throws BeansException { return bean; } @Override public void setApplicationContext(ApplicationContext ctx) throws BeansException { this.ctx=ctx;//将值传过来 } }</span>
接口不能new 对象,采用另写一个类
<span style="font-size:18px;">package cn.aop.demo1; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class Around implements MethodInterceptor { @Override public Object invoke(MethodInvocation method) throws Throwable { System.out.println("开始拦截了"); Object obj= method.proceed(); System.out.println("拦截返回啦"); return obj; } }</span>
<<span style="font-size:24px;">span style="font-size:18px;">//可以拦截多个,进行优化 @Test public void Test7(){ ApplicationContext context = new ClassPathXmlApplicationContext("cn/aop/demo3.xml"); Person p=context.getBean("factory", Person.class); p.run(); }</span></span>
上面的这些运行出来,就能通过配置文件,在工程中使用该Bean时由Spring容器创建Bean的实例。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。
但是 在这些上面介绍中,切面只能拦截指定类的,不能拦截整个项目的。下次就用到aspectj,可以拦截整个项目。
个人理解,在Spring中,IOC(控制反转)和AOP(面向切面编程),这是两个核心,IOC技术是一种思想,在应用时,就是配置xml文件,通过Spring容器根据你的配置来造相应的对象,程序有原先的主动变成了被动,当然,没有存在太多的依赖关系。AOP(面向切面编程)也就是切面技术(切点+通知),切点可以通过配置文件中正则表达式,通知则是自己写一个类(实现这个MethodInterceptor接口),里面的内容就是我们需要切面做的事情,比如数据库中的事物处理,修改某个函数的返回值等。。切面的方法,有很多种,如采用注解,便签等也能实现切面。总之,切面技术,非常强大。。。